自定义View学习笔记

261 阅读3分钟

一.如何创建一个自定义View?

  1. 首先继承View,然后写入到布局中
public class CircleLoadingView extends View {
    private Paint paint;
    public CircleLoadingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int radius = DpPxUtils.dp2px(80);
        canvas.drawCircle(0,0,radius,paint);
    }
}

2.然后写入到布局中

        <LinearLayout
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <com.smallcake.template.custom.CircleLoadingView
                android:background="@color/red"
                android:layout_width="100dp"
                android:layout_height="100dp"/>
        </LinearLayout>

3.运行,效果就出来了 image.png

注意: 1.自定义的View构造方法是两个参数的构造方法,不写比报错 2.默认画笔paint最好写出来,因为onDraw可能会多次执行,画笔默认黑色

二.如何获取自定义View的宽高?

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
    }

或者:onDraw()方法中通过getWidth()getHeight()

注意:

  • 1.onMeasure方法会执行三次
  • 2.首先要明白MeasureSpec中三种模式:UNSPECIFIED,AT_MOST,EXACTLY
  • 3.int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式,一共32位,前两位标示模式,后面30位标示空间大小
  • 4.通常来说,我们在自定义 View 的时候会经常地接触到 AT_MOSTEXACTLY,UNSPECIFIED 这个模式被应用在系统源码中。如 NestedScrollViewScrollView
  • 5.如何获取对应的模式 :int heightMode = MeasureSpec.getMode(heightMeasureSpec); 参考:www.cnblogs.com/liushilin/p…
模式描述对应
精确模式01(MeasureSpec.EXACTLY在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。对应 MATCH_PARENT确定的值
最大模式11(MeasureSpec.AT_MOST这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。对应 WRAP_CONETNT
未指定模式00(MeasureSpec.UNSPECIFIED这个就是说,当前组件,可以随便用空间,不受限制。所有

三.一些样式设置?

效果代码
抗锯齿paint.setAntiAlias(true);
空心paint.setStyle(Paint.Style.STROKE);
线宽 paint.setStrokeWidth(8);

四.RectF和Rect区别?

  • 1.它们都是用于构造一个矩形区域
  • 2.精度不一样,Rect是使用int类型作为数值,RectF是使用float类型作为数值
  • 3.两个类型提供的方法也不是完全一致,比如 RectF(Rect r)根据给定的Rect对象来构造一个RectF对象, 进而扩展Rect不具备的功能

五.画圆

 canvas.drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

image.png

  • 1.绘制都是从画笔中心点开始画,画笔粗细设置过宽,会出现上下左右少一块,我们应该设置半径radius-画笔组粗/2

六.画圆弧

RectF rectF = new RectF(0, 0, width, height);
canvas.drawArc(rectF,0,30,false,paint);

drawArc参数:1.绘制区域,2.开始角度,3.结束角度,4.是否连接中心点,5,画笔

  • 1.如果你想绘制的是一段圆弧,不是一段圆弧切面,可以设置画笔样式为空心的 paint.style = Paint.Style.STROKE

  • Demo:绘制一个加载进度的圆弧,并结合动画,让其动起来

class CircleLoaindView : View {

    private val paint = Paint()
    private val paint2 = Paint()
    private var rectF: RectF? = null
    private var position = 0//从0到100的进度位置
    private val paintWidth = 4f//画笔粗细

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        paint.setColor(Color.parseColor("#DDDDDD"))
        paint.isAntiAlias = true
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = paintWidth

        paint2.setColor(Color.parseColor("#535353"))
        paint2.isAntiAlias = true
        paint2.style = Paint.Style.STROKE
        paint2.strokeWidth = paintWidth
        paint2.strokeCap = Paint.Cap.ROUND

    }

    fun setPosition(position: Int) {
        this.position = position;
        invalidate()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        val centerX = width / 2
        val strokeWidth = centerX / radius
        canvas?.let {
            //绘制灰色背景圆环
            canvas.drawCircle(
                (width / 2).toFloat(), (height / 2).toFloat(),
                (width / 2).toFloat() - paintWidth / 2, paint
            )
            //设置圆弧段绘制区域
            if (rectF == null) rectF = RectF(
                (strokeWidth / 2).toFloat() + paintWidth / 2,
                (strokeWidth / 2).toFloat() + paintWidth / 2,
                (2 * centerX - strokeWidth / 2).toFloat() - paintWidth / 2,
                (2 * centerX - strokeWidth / 2).toFloat() - paintWidth / 2
            )
            //绘制圆弧段
            canvas.drawArc(rectF!!, 3.6f * position, 360f / 30, false, paint2)


        }

    }

}
<com.smallcake.test.CircleLoaindView
        android:id="@+id/circleLoading"
        android:layout_width="200dp"
        android:layout_height="200dp"
        />
val animator: ObjectAnimator = ObjectAnimator.ofInt(circleLoading, "position", 0, 100)
        animator.duration = 1000
        animator.repeatCount = Animation.INFINITE
        animator.interpolator = LinearInterpolator()
        animator.start()

loading