1.使用繪制方法,畫(huà)出一個(gè)view,然后在xml文件中使用即可
1.創(chuàng)建自定義的view
這里我只畫(huà)了一個(gè)空心圓,canvas是畫(huà)布,paint是畫(huà)筆,用畫(huà)筆可以畫(huà)出任何圖形設(shè)置顏色、空心實(shí)心、線(xiàn)條寬度,通過(guò)RectF設(shè)置圓的位置和大小
public class PaintView extends View {
public PaintView(Context context) {
super(context);
}
public PaintView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rectF = new RectF(10, 10, 100, 100);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
canvas.drawOval(rectF, paint);
}
}
2.將畫(huà)好的view引用到xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mazhan.view.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.mazhan.view.PaintView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
這樣我們繪制的view就可以被加載出來(lái)了

2.繪制線(xiàn)條時(shí)的注意點(diǎn)
1.需要設(shè)置stroke
2.二階的貝塞爾曲線(xiàn)可以設(shè)置跟隨手勢(shì)畫(huà)出任意的圖形,這個(gè)在線(xiàn)教育中使用的畫(huà)板就是使用的二階貝塞爾曲線(xiàn)
public class CanvasPathView extends View {
public CanvasPathView(Context context) {
super(context);
}
public CanvasPathView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
// 如果需要繪制path的話(huà),需要設(shè)置paint的style為stroke
paint.setStyle(Paint.Style.STROKE);
// 線(xiàn)條需要通過(guò)path來(lái)繪制
Path path = new Path();
// 設(shè)置起點(diǎn)
path.moveTo(50,50);
// 設(shè)置連接點(diǎn)
path.lineTo(100,100);
//二階貝塞爾曲線(xiàn)
path.quadTo(200,200,300,100);
canvas.drawPath(path, paint);
}
}

3.移動(dòng)坐標(biāo)系
默認(rèn)的坐標(biāo)系是(0,0),可以通過(guò)設(shè)置canvas的屬性來(lái)平移或者是旋轉(zhuǎn)坐標(biāo)系,然后將坐標(biāo)系的模式保存下來(lái),這樣可以便利的設(shè)置一些特殊的坐標(biāo)系
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
// 設(shè)置坐標(biāo)系起始點(diǎn)為100,100
canvas.translate(100,100);
// 保存設(shè)置好的坐標(biāo)系
canvas.save();
// 設(shè)置坐標(biāo)系旋轉(zhuǎn)
canvas.rotate(90);
// 銷(xiāo)毀坐標(biāo)系,還原上一次保存的坐標(biāo)系
canvas.restore();
canvas.drawLine(0,0,100,0,paint);
}
4.自定義控件的三大方法執(zhí)行順序
public class MyFrameLayout extends FrameLayout {
public MyFrameLayout(@NonNull Context context) {
super(context);
}
public MyFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(getClass().getSimpleName(), "onDraw: " + "MyFrameLayout父控件");
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(getClass().getSimpleName(), "onMeasure: " + "MyFrameLayout父控件");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.d(getClass().getSimpleName(), "onLayout: " + "MyFrameLayout父控件");
}
}
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setTextSize(20);
canvas.drawText("這是一個(gè)view", 10, 10, paint);
Log.d(getClass().getSimpleName(), "onDraw: " + "MyView子控件");
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d(getClass().getSimpleName(), "onMeasure: " + "MyView子控件");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.d(getClass().getSimpleName(), "onLayout: " + "MyView子控件");
}
}
在xml中使用MyFrameLayout和MyView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mazhan.customviewmethodorder.MainActivity">
<com.mazhan.customviewmethodorder.MyFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mazhan.customviewmethodorder.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</com.mazhan.customviewmethodorder.MyFrameLayout>
</LinearLayout>
10-22 07:20:51.088 6735-6735/com.mazhan.customviewmethodorder D/MyFrameLayout: onMeasure: MyFrameLayout父控件
10-22 07:20:51.150 6735-6735/com.mazhan.customviewmethodorder D/MyFrameLayout: onMeasure: MyFrameLayout父控件
10-22 07:20:51.153 6735-6735/com.mazhan.customviewmethodorder D/MyFrameLayout: onLayout: MyFrameLayout父控件
10-22 07:28:38.713 6928-6928/com.mazhan.customviewmethodorder D/MyFrameLayout: onMeasure: MyFrameLayout父控件
10-22 07:28:38.753 6928-6928/com.mazhan.customviewmethodorder D/MyFrameLayout: onMeasure: MyFrameLayout父控件
10-22 07:28:38.760 6928-6928/com.mazhan.customviewmethodorder D/MyFrameLayout: onLayout: MyFrameLayout父控件
10-22 07:30:32.941 7079-7079/com.mazhan.customviewmethodorder D/MyView: onMeasure: MyView子控件
10-22 07:30:32.943 7079-7079/com.mazhan.customviewmethodorder D/MyFrameLayout: onMeasure: MyFrameLayout父控件
10-22 07:30:32.995 7079-7079/com.mazhan.customviewmethodorder D/MyView: onMeasure: MyView子控件
10-22 07:30:32.995 7079-7079/com.mazhan.customviewmethodorder D/MyFrameLayout: onMeasure: MyFrameLayout父控件
10-22 07:30:32.996 7079-7079/com.mazhan.customviewmethodorder D/MyView: onLayout: MyView子控件
10-22 07:30:32.996 7079-7079/com.mazhan.customviewmethodorder D/MyFrameLayout: onLayout: MyFrameLayout父控件
10-22 07:30:33.188 7079-7079/com.mazhan.customviewmethodorder D/MyView: onDraw: MyView子控件
5.VierPager的使用
1.在MainActivity的xml中添加ViewPager
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.mazhan.viewpagerdemo.MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/viewPagerId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_purple">
</android.support.v4.view.ViewPager>
</LinearLayout>
2.給ViewPager設(shè)置PagerAdapter
public class MainActivity extends AppCompatActivity {
private ArrayList<TextView> mDatas;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initViews();
}
private void initViews() {
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPagerId);
viewPager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return mDatas.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView textView = mDatas.get(position);
container.addView(textView);
return textView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
});
}
private void initData() {
mDatas = new ArrayList<>();
for (int i = 0; i < 10; i++) {
TextView textView = new TextView(getApplicationContext());
textView.setTextSize(20);
textView.setText("這是第" + i + "個(gè)頁(yè)面");
mDatas.add(textView);
}
}
}
6.ViewFliper的使用
使用viewFlipper完成文字向上滾動(dòng)的效果(只要設(shè)置滾動(dòng)的動(dòng)畫(huà)即可)
在xml中直接使用系統(tǒng)的ViewFlipper即可
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.mazhan.viewfliperdemo.MainActivity">
<ViewFlipper
android:id="@+id/ViewFlipperId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/myColorRed">
</ViewFlipper>
</android.support.constraint.ConstraintLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
ViewFlipper viewFlipper = (ViewFlipper) findViewById(R.id.ViewFlipperId);
viewFlipper.startFlipping();
//設(shè)置viewFlipper的一些屬性
viewFlipper.setFlipInterval(5000);
// 設(shè)置viewFlipper的的入場(chǎng)動(dòng)畫(huà)和離場(chǎng)動(dòng)畫(huà)
// 組合動(dòng)畫(huà) 透明度 位移
// 離場(chǎng)動(dòng)畫(huà)
AnimationSet inAnimationSet = new AnimationSet(false);
AnimationSet outAnimationSet = new AnimationSet(false);
AlphaAnimation inAlphaAnimation = new AlphaAnimation(0, 1);
TranslateAnimation inTranslateAnimation = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_SELF, 0,
TranslateAnimation.RELATIVE_TO_SELF, 0,
TranslateAnimation.RELATIVE_TO_SELF, 1,
TranslateAnimation.RELATIVE_TO_SELF, 0);
inAnimationSet.addAnimation(inAlphaAnimation);
inAnimationSet.addAnimation(inTranslateAnimation);
inAnimationSet.setDuration(3000);
AlphaAnimation outAlphaAnimation = new AlphaAnimation(1, 0);
TranslateAnimation outTranslateAnimation = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_SELF, 0,
TranslateAnimation.RELATIVE_TO_SELF, 0,
TranslateAnimation.RELATIVE_TO_SELF, 0,
TranslateAnimation.RELATIVE_TO_SELF, -1);
outAnimationSet.addAnimation(outAlphaAnimation);
outAnimationSet.addAnimation(outTranslateAnimation);
outAnimationSet.setDuration(2000);
viewFlipper.setInAnimation(inAnimationSet);
viewFlipper.setOutAnimation(outAnimationSet);
//向ViewFlipper中添加要滾動(dòng)的view
for (int i = 0; i < 10; i++) {
TextView textView = new TextView(getApplicationContext());
textView.setText("這是第" + i + "個(gè)textView");
viewFlipper.addView(textView);
}
}
}
7.自定義控件自定義屬性
1.比如說(shuō)自定義一個(gè)只滾動(dòng)text的ViewFlipper,叫做textViewFlipper。如果要給這個(gè)控件添加自定義屬性,那么需要新建一個(gè)文件

2.那么如何在xml中使用自定義的屬性呢

3.這樣在自定義的控件類(lèi)被加載的時(shí)候,就會(huì)調(diào)用構(gòu)造方法,那么就可以得到自定義的屬性名和屬性值

4.將自定義的屬性設(shè)置給textView
這里自定義屬性的獲取方式并不是這樣遍歷的,是直接通過(guò)字段獲取的,R.styleable,R.styleable.TextViewFlipper_fontColor,R.styleable.TextViewFlipper_fontSize,這些都是系統(tǒng)根據(jù)自定義屬性的文件生成的。
注意點(diǎn):這里獲取屬性的時(shí)機(jī)是要注意的,只能在構(gòu)造方法里進(jìn)行獲取,只有在構(gòu)造方法被調(diào)用的時(shí)候,attrs的值才是正確的,當(dāng)構(gòu)造方法執(zhí)行完成之后,attrs的值會(huì)發(fā)生變化,也就得不到自定義的屬性了。
public class TextViewFlipper extends ViewFlipper {
private AttributeSet mAttrs;
private Context mContext;
private float mTextSize;
private int mTextColor;
public TextViewFlipper(Context context) {
super(context);
}
public TextViewFlipper(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mAttrs = attrs;
int attributeCount = attrs.getAttributeCount();
for (int i = 0; i < attributeCount; i++) {
String attributeName = attrs.getAttributeName(i);
String attributeValue = attrs.getAttributeValue(i);
Log.d(getClass().getSimpleName(), "TextFlipperView: attributeName: " + attributeName
+" attributeValue: "+attributeValue);
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TextViewFlipper);
mTextColor = typedArray.getColor(R.styleable.TextViewFlipper_fontColor, Color.BLACK);
mTextSize = typedArray.getDimension(R.styleable.TextViewFlipper_fontSize, 100);
//銷(xiāo)毀typedArray
typedArray.recycle();
}
void setViews (ArrayList<TextView> textViews) {
for (int i = 0; i < textViews.size(); i++) {
TextView textView = textViews.get(i);
textView.setTextColor(mTextColor);
textView.setTextSize(mTextSize);
addView(textView);
}
}
}
8.制作垂直滑動(dòng)的滑塊demo
自定義VerticalSlideView,里面放一個(gè)ImageView,讓imageView跟隨手指拖動(dòng)而滑動(dòng)
public class VerticalSlideView extends ViewGroup {
private View mChildView;
private float mStartY;
private float mStartX;
public VerticalSlideView(Context context) {
super(context);
}
public VerticalSlideView(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 確定控件的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mChildView = getChildAt(0);
//1. 獲得mode和size
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int withSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 測(cè)量自控件的寬度
measureChildren(widthMeasureSpec, heightMeasureSpec);
// 2.判斷mode,根據(jù)mode來(lái)設(shè)置size
int childWidth = mChildView.getMeasuredWidth();
// 設(shè)置自身控件的大小
setMeasuredDimension(childWidth, heightSize - dip2px(10));
}
// 對(duì)子控件進(jìn)行布局
int mTop = 0;
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 位置:左上右下
mChildView.layout(0, mTop, mChildView.getMeasuredWidth(), mTop+mChildView.getMeasuredHeight());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 這里沒(méi)有判斷action的類(lèi)型,是點(diǎn)擊還是滑動(dòng)還是抬起
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// do sth
break;
case MotionEvent.ACTION_MOVE:
// do sth
break;
case MotionEvent.ACTION_UP:
// do sth
break;
default:
break;
}
// 直接設(shè)置滑塊的y值跟隨觸摸的y值變化
float y = event.getY();
mTop = (int) y;
requestLayout();
if (y < 0) {
mTop = 0;
}
if (y > getMeasuredHeight() - mChildView.getMeasuredHeight()) {
mTop = getMeasuredHeight() - mChildView.getMeasuredHeight();
}
requestLayout();
return true;
}
public int px2dip(float pxValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
// 將do轉(zhuǎn)化為像素尺寸
public int dip2px( float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
在xml中使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context="com.mazhan.verticalslideview.MainActivity">
<com.mazhan.verticalslideview.VerticalSlideView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:background="#f00">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/tabicon"/>
</com.mazhan.verticalslideview.VerticalSlideView>
</LinearLayout>
9.無(wú)限滾動(dòng)的背景View
通過(guò)繪制背景圖片來(lái)實(shí)現(xiàn)
1.創(chuàng)建自定義的屬性,來(lái)設(shè)置圖片滾動(dòng)的參數(shù),創(chuàng)建attrs屬性文件,設(shè)置參數(shù)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollingView" >
<attr name="speed" format="integer"/>
<attr name="src" format="reference"/>
</declare-styleable>
</resources>
2.在xml中使用自定義的屬性
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:mazhan="http://schemas.android.com/apk/res-auto"
tools:context="com.mazhan.scrollingview.MainActivity">
<com.mazhan.scrollingview.ScrollingView
android:layout_width="match_parent"
android:layout_height="wrap_content"
mazhan:src="@drawable/scrolling_background"
mazhan:speed="3"/>
</LinearLayout>
3.創(chuàng)建自定義的ScrollingView,在里面繪制bitmap,通過(guò)刷新bitmap的繪制來(lái)提現(xiàn)出動(dòng)畫(huà)效果
public class ScrollingView extends View {
private int mSpeed;
private Paint mPaint;
private Bitmap mBitmap;
public ScrollingView(Context context) {
super(context);
}
public ScrollingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 獲取自定義的屬性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScrollingView);
mSpeed = typedArray.getInteger(R.styleable.ScrollingView_speed, 1);
int resourceId = typedArray.getResourceId(R.styleable.ScrollingView_src, 0);
mBitmap = BitmapFactory.decodeResource(getResources(), resourceId);
typedArray.recycle();
mPaint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(widthSize, mBitmap.getHeight());
}
int mOffset = 0;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//1 畫(huà)圖片
//節(jié)約效率,避免控件在看不見(jiàn)的地方瘋狂繪制,對(duì)偏移量需要控制
//負(fù)數(shù)到了一定的范圍,就重置,范圍設(shè)置為bitmap的寬度
if(mOffset<-mBitmap.getWidth()){
mOffset = 0;
}
//2 填充控件剩下的寬度,多繪制幾個(gè)bitmap上去
int left = (int) mOffset;
while (left<getMeasuredWidth()){
// 一直往右邊去繪制,填充
canvas.drawBitmap(mBitmap,left,0,mPaint);
left+=mBitmap.getWidth();
}
mOffset -= mSpeed;
//4 讓控件重新繪制
invalidate();
}
}