自定义View 一 (继承VIew重写onDraw方法)
项目:具有圆形效果的自定义View
一、继承View并重写onDraw方法
public class CircleView extends View{ private static final int COLOR = Color.RED; private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private int mWidth = 0; private int mHeight = 0; public CircleView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CircleView(Context context) { super(context); init(); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init(){ mPaint.setColor(COLOR); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //获取当前View的宽/高 mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); //获取半径 int radium = Math.min(mWidth,mHeight)/2; //画圆 canvas.drawCircle(mWidth/2,mHeight/2,radium,mPaint); } }
在xml中测试margin发现可以用,说明margin是由父容器控制的(想起measureChildMarginLayout源码)
但是wrap_content和padding都不生效。
二、让wrap_content生效
根据上一章View的工作原理:①、重写onMeasure方法 ②、给CircleView设定一个固定的宽高
//设定wrap_content时候的宽度 private static final int AT_WIDTH = 30; private static final int AT_HEIGHT = 30; //重写onMeasure()方法 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //获取子View的范围 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //判断当属性为wrap_content的时候 if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){ setMeasuredDimension(AT_WIDTH,AT_HEIGHT); } else if (widthMode == MeasureSpec.AT_MOST){ setMeasuredDimension(AT_WIDTH,heightSize); } else if (heightMode == MeasureSpec.AT_MOST){ setMeasuredDimension(widthSize,AT_HEIGHT); } else { super.onMeasure(widthMeasureSpec,heightMeasureSpec); } }
三、解决无法padding的问题
原理:只需要在onDraw中,获取padding的参数就可以了
//重写onDraw方法 protected void onDraw(Canvas canvas) { super.onDraw(canvas); //获取padding int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingTop = getPaddingTop(); int paddingBottom = getPaddingBottom(); //获取当前View的宽/高 减去padding mWidth = getMeasuredWidth() - paddingLeft - paddingRight; mHeight = getMeasuredHeight() - paddingTop - paddingBottom; //获取半径 int radium = Math.min(mWidth,mHeight)/2; //画圆 canvas.drawCircle(paddingLeft+mWidth/2,paddingTop - mHeight/2,radium,mPaint); }
四、自定义属性
步骤:①、在values目录中创建xml文件名,文件名必须以attr_开头。②、内容的编写:<declare-styleadable>标签中:name代表自定义属性(该为CircleView类)
<attr>标签中 name代表之后使用的属性名(circle_color),format代表格式(color)
<resources> <declare-styleable name="CircleView"> <attr name="color_circle" format="color"/> </declare-styleable> </resources>
步骤③、在布局文件中使用自定义属性 必须在schemas声明:xmlns:app="http://schemas.android.com/apk/res-auto" 其中app名字可以随便替换。
但是circleView中自定义属性名的前缀必须是和这里一致(一般习惯使用app)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" //这一段必须要加 app名字可以替换
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.maikefengchao.circleview.MainActivity"> <com.maikefengchao.circleview.CircleView android:layout_width="80dp" android:layout_height="80dp"
//前缀与添加的声明前缀一致
app:color_circle="#9999"/> </LinearLayout>
步骤④:在CircleView中获取自定义属性参数
//在构造方法中使用 public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //加载自定义属性集合CircleView TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.CircleView); //解析集合中的circle_circle,设置默认颜色 mColor = a.getColor(R.styleable.CircleView_color_circle,Color.RED); init(); }
全部代码:(P209 ①)