【一看就懂】自定义View入门实践,实现外方内圆效果

本文作为自定义View的初步实践,取材自《android开发艺术探索》

效果如下

在这里插入图片描述

方法

activity_main.xml

这是我们自己设计的CircleView控件,其在xml中如下表示

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">

    <com.example.makeview.CircleView
        android:id="@+id/circle_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:circle_color="#FFFF00"
        android:padding="20dp"
        android:background="#000000"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

其中

 xmlns:app="http://schemas.android.com/apk/res-auto"

表示自定义属性,使得我们可以在CircleView中使用

         app:circle_color="#FFFF00"

那circle哪来的呢?这里我们要为CircleView构建属于自己的attr.xml属性

attr.xml属性

在values新建attr.xml
写上

<?xml version="1.0" encoding="utf-8"?>
<!--给CircleView自定义属性,属性名为circle_color,
格式为color,id=R.styleable.CircleView_circle_color-->
<resources>
    <declare-styleable name="CircleView">
        <attr name="circle_color" format="color" />
    </declare-styleable>
</resources>

其中资源为styleable ,和id,layout同级,名字为CircleView,这里我们定义的属性名为circle_color,属性是color,当然也可以定义其他format

CircleView

接下来再在新建的CircleView类中取到color,在构造方法中

  public CircleView(Context context, AttributeSet attributeSet) {
        super(context,attributeSet);
        TypedArray a = context.obtainStyledAttributes(attributeSet,R.styleable.CircleView);
        mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
        a.recycle();
        Log.d(TAG, "CircleView: Second");
        init();
    }
   private void init() {
        mPaint.setColor(mColor);
    }

用TypedAtrray获取,如图
在这里插入图片描述

重写onDraw()

然后我们再将已经获取颜色的Paint用canvas画出圆形,重写onDraw方法

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingLeft();
        int paddingTop = getPaddingLeft();
        int paddingBottom = getPaddingLeft();
        int width = getWidth() - paddingLeft - paddingRight; //缩减宽高
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width,height)/2;
        canvas.drawCircle(width/2+paddingLeft,height/2+paddingBottom,radius,mPaint);
    }

这里我们画了很大的精力取计算Padding,因为我们在activity_main中设置了padding,但是自定义控件默认是不支持的,所以我们要自己改,具体是将width左右都减小padding,由于我们的圆心是由width和height决定,为了不影响圆心位置,我们在画的时候加上一个padding即可,这样看起来就是画的圆形两边都缩小了,但是位置不变

重写onMeasure()

另外一个需要重写的是onMeasure(),主要用来做尺寸的测量,因为我们在activity_main中设置了wrap_content,但是自定义控件不知道wrap_content是多少,只当match_content,所以我们要在这里设定

 @Override
    protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize,200);
        }
    }

这里默认设置成200

java源代码

package com.example.makeview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;



public class CircleView extends View {
    final String TAG = "CircleView";
    private int mColor =Color.RED;
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        Log.d(TAG, "CircleView: First");
        init();
    }

    public CircleView(Context context, AttributeSet attributeSet) {
        super(context,attributeSet);
        TypedArray a = context.obtainStyledAttributes(attributeSet,R.styleable.CircleView);
        mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
        a.recycle();
        Log.d(TAG, "CircleView: Second");
        init();
    }

    public CircleView(Context context, AttributeSet attributeSet, int defStyleAttr) {
        super(context, attributeSet, defStyleAttr);
        Log.d(TAG, "CircleView: Third");

        init();
    }

    private void init() {
        mPaint.setColor(mColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingLeft();
        int paddingTop = getPaddingLeft();
        int paddingBottom = getPaddingLeft();
        int width = getWidth() - paddingLeft - paddingRight; //缩减宽高
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width,height)/2;
        canvas.drawCircle(width/2+paddingLeft,height/2+paddingBottom,radius,mPaint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize,200);
        }
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章