[Android] Android自定义View修炼之路(2)

前言

上一篇文章中介绍了自定义View的基本流程,今天我们仍然跟随洋神脚步学习自定义View,本篇文章参考自洋神的 Android 自定义View (三) 圆环交替 等待效果一文,有兴趣的可以去洋神的博客看看哈!

举例

自定义View流程回顾

上一篇文章中介绍了下自定义View的流程,在本篇文章中我们仍然将这4个步骤列出来加深印象:

  • 自定义View的属性
  • 在View的构造方法中获得自定义的属性
  • 重写onMeasure
  • 重写onDraw

下面我们按照这4个步骤一步步完成我们的自定义View。

自定义View的属性

在工程目录下的res/values子目录下建立一个attrs.xml文件,在里面定义我们需要的属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="firstColor" format="color"/>
    <attr name="secondColor" format="color"/>
    <attr name="circleWidth" format="dimension"/>
    <attr name="speed" format="integer"/>
    <declare-styleable name="CustomView">
        <attr name="firstColor"/>
        <attr name="secondColor"/>
        <attr name="circleWidth"/>
        <attr name="speed"/>
    </declare-styleable>
</resources>

如上面这段代码,我们定义了4个属性,分别是第一个圆环的颜色、第二个圆环的颜色、圆环的宽度以及圆环更新的速度,然后我们就可以在layout布局文件中这样使用我们的自定义View及自定义属性:

<?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="com.glemontree.customview04.MainActivity">

    <com.glemontree.customview04.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

</RelativeLayout>

这里我没有添加自定义属性,全都使用的默认属性,如果你有兴趣,可以在layout布局文件中的CustomView子结点下添加app:xxx="xxx"来使用自定义属性。

在View的构造方法中获取自定义属性

紧接着我们可以在自定义View的构造方法中获取自定义的属性,如下:

public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0);
    mFirstColor = a.getColor(R.styleable.CustomView_firstColor, Color.GREEN);
    mSecondColor = a.getColor(R.styleable.CustomView_secondColor, Color.RED);
    mCircleWidth = a.getDimensionPixelOffset(R.styleable.CustomView_circleWidth,
           (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()));
    mSpeed = a.getInt(R.styleable.CustomView_speed, 20);
    a.recycle();
    mPaint = new Paint();

    // 开启线程更新进度并刷新界面
    new Thread() {
      public void run() {
        while (true) {
          mProgress++;
          if (mProgress == 360) {
            mProgress = 0;
            if (!mIsNext) {
              mIsNext = true;
            } else {
              mIsNext = false;
            }
          }

          // 刷新界面
          postInvalidate();

          // Thread.sleep()函数需要使用try-catch语句进行包围
          try {
            Thread.sleep(mSpeed);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }.start();
}

在View的构造方法中我们分别获取了我们自定义的4个属性,并且设置了默认值,这样即使在layout布局文件中不设置这4个自定义属性,仍然可以保证有值。

在构造方法中还开启了一个线程,这个线程主要的目的就是更新mProgress并且刷新界面,mProgress每增加1,就刷新界面一次,刷新界面是通过postInvalidate()方法来实现的。

因为这里并不需要onMeasure()操作,因此省去onMeasure()的实现,我们直接看onDraw()函数。

重写onDraw

onDraw()函数的内容如下:

@Override
protected void onDraw(Canvas canvas) {

    // 获得中心点
    int centre = getWidth() / 2;

    // 获得半径
    int radius = centre - mCircleWidth / 2;

    // 设置空心线宽
    mPaint.setStrokeWidth(mCircleWidth);

    // 抗锯齿
    mPaint.setAntiAlias(true);

    // 只绘制图形轮廓(描边)
    mPaint.setStyle(Paint.Style.STROKE);

    // 指定圆弧的外轮廓矩形区域
    RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);

    if (!mIsNext) {

      // 设置圆环的颜色
      mPaint.setColor(mFirstColor);

      // 画出圆环
      canvas.drawCircle(centre, centre, radius, mPaint);

      // 设置圆弧的颜色
      mPaint.setColor(mSecondColor);

      // 根据进度画圆弧
      canvas.drawArc(oval, -90, mProgress, false, mPaint);
    } else {

      // 设置圆环的颜色
      mPaint.setColor(mSecondColor);

      // 画出圆环
      canvas.drawCircle(centre, centre, radius, mPaint);

      // 设置圆弧的颜色
      mPaint.setColor(mFirstColor);

      // 根据进度画圆弧
      canvas.drawArc(oval, -90, mProgress, false, mPaint);
    }
    //        canvas.drawRect(oval, mPaint);
}

其实也没啥特别要说的,就是绘制圆环和圆弧,下面我想介绍下drawArc()函数。

`drawArc()函数的原型如下:

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

其各个参数的含义如下:

  • oval:指定圆弧的外轮廓矩形区域
  • startAngle:圆弧起始角度,单位为度
  • sweepAngle:圆弧扫过的角度,顺时针方向,单位为度
  • useCenter:当为true时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形
  • paint:绘制圆弧的画笔

第一个参数oval我纠结了蛮久,一直没搞懂是什么意思,其实呢,就是圆弧的外切圆,比如在我们的程序中:

RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);

绘制的矩形正好是圆弧的外切矩形,在上面这段程序中你可以取消最后一行的注释,查看绘制的矩形到底是什么样的。

总结

其实我是想上一张动图的,但是尼玛使用GifCam录制动图得到的动图总是颜色凌乱,还没搞懂,等我搞懂了搞张图上来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值