一. 自定义View的种类 1. 继承原生控件或者布局 2. 组合原生控件 3. 继承View 二. 简单的自定义View例子 1. 基础搭建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public class CircleView extends View { Paint mPaint; public CircleView (Context context) { super (context); init(); } public CircleView (Context context, @Nullable AttributeSet attrs) { super (context, attrs); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super (context, attrs, defStyleAttr, defStyleRes); init(); } private void init () { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(5f ); mPaint.setStyle(Paint.Style.FILL); } @Override protected void onDraw (Canvas canvas) { super .onDraw(canvas); int width = getWidth(); int height = getHeight(); int radius = Math.min(width,height) / 2 ; canvas.drawCircle(width >> 1 , height >> 1 , radius, mPaint); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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 =".MainActivity" > <swu.xl.selfview_base.CircleView android:layout_width="match_parent" android:layout_height="150dp" android:background="#000000" /> </RelativeLayout >
运行效果:
2. 支持wrap_content功能
在自定义的View内部重写onMeasure方法。
在onMeasure方法中设置wrap_content的默认宽 / 高值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 public class CircleView extends View { Paint mPaint; public CircleView (Context context) { super (context); init(); } public CircleView (Context context, @Nullable AttributeSet attrs) { super (context, attrs); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super (context, attrs, defStyleAttr, defStyleRes); init(); } private void init () { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(5f ); mPaint.setStyle(Paint.Style.FILL); } @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { super .onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int mWidth = 400 ; int mHeight = 400 ; if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) { setMeasuredDimension(mWidth, mHeight); } else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) { setMeasuredDimension(mWidth, heightSize); } else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) { setMeasuredDimension(widthSize, mHeight); } } @Override protected void onDraw (Canvas canvas) { super .onDraw(canvas); int width = getWidth(); int height = getHeight(); int radius = Math.min(width,height) / 2 ; canvas.drawCircle(width >> 1 , height >> 1 , radius, mPaint); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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 =".MainActivity" > <swu.xl.selfview_base.CircleView android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#000000" /> </RelativeLayout >
运行效果:
3. 支持Padding功能 绘制时考虑传入的padding属性值(四个方向)。在自定义View类的重写onDraw进行设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 public class CircleView extends View { Paint mPaint; public CircleView (Context context) { super (context); init(); } public CircleView (Context context, @Nullable AttributeSet attrs) { super (context, attrs); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super (context, attrs, defStyleAttr, defStyleRes); init(); } private void init () { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(5f ); mPaint.setStyle(Paint.Style.FILL); } @Override protected void onDraw (Canvas canvas) { super .onDraw(canvas); int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int paddingBottom = getPaddingBottom(); int width = getWidth() - paddingLeft - paddingRight; int height = getHeight() - paddingTop - paddingBottom; int radius = Math.min(width,height) / 2 ; canvas.drawCircle(paddingLeft + (width >> 1 ), paddingTop + (height >> 1 ), radius, mPaint); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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 =".MainActivity" > <swu.xl.selfview_base.CircleView android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="150dp" android:background="#000000" android:padding="10dp" /> </RelativeLayout >
运行效果:
4. 自定义属性 系统自带的属性在xml里面基本上都是以android开头,有时候我们需要自定义一些属性,其步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 package swu.xl.selfview_base;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import androidx.annotation.Nullable;public class CircleView extends View { Paint mPaint; public CircleView (Context context) { super (context); init(); } public CircleView (Context context, @Nullable AttributeSet attrs) { super (context, attrs); init(); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleView); int color = typedArray.getColor(R.styleable.CircleView_circle_color, Color.GRAY); mPaint.setColor(color); typedArray.recycle(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super (context, attrs, defStyleAttr); init(); } public CircleView (Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super (context, attrs, defStyleAttr, defStyleRes); init(); } private void init () { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); mPaint.setStrokeWidth(5f ); mPaint.setStyle(Paint.Style.FILL); } @Override protected void onDraw (Canvas canvas) { super .onDraw(canvas); int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int paddingRight = getPaddingRight(); int paddingBottom = getPaddingBottom(); int width = getWidth() - paddingLeft - paddingRight; int height = getHeight() - paddingTop - paddingBottom; int radius = Math.min(width,height) / 2 ; canvas.drawCircle(paddingLeft + (width >> 1 ), paddingTop + (height >> 1 ), radius, mPaint); } }
1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8"?> <resources > <declare-styleable name ="CircleView" > <attr name ="circle_color" format ="color" /> </declare-styleable > </resources >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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 =".MainActivity" > <swu.xl.selfview_base.CircleView android:layout_centerInParent="true" android:layout_width="match_parent" android:layout_height="150dp" android:background="#000000" android:padding="10dp" app:circle_color="#ff4081" /> </RelativeLayout >
运行方式:
自定义属性更多的内容参考:自定义View-自定义属性
参考文章 Android自定义View的三种方式:继承布局,继承原生控件,继承View
手把手教你写一个完整的自定义View