乐趣区

关于android:Android-自定义View之自定义属性

Android 自定义 View 之自定义属性

一:前言

1. 什么是命名空间呢
android 的命名空间和自定义命名空间

xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns: 前缀
android:名称,能够自定义
url: 代表的就是空间, 是个没有用的 url, 是对立资源标识符, 绝对于一个常量
2. 配置文件 attrs.xml
在 res 下的 values 文件加下创立一个 attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomNumAnimView">
        <attr name="round_radius" format="dimension" />
        <attr name="round_color" format="color" />
        <attr name="text_color" format="color" />
        <attr name="text_size" format="dimension" />
    </declare-styleable>
</resources>

// 格局解析
declare-styleable
name:属性汇合名称
attr
name:属性名称
format:格局

共有 11 种格局
        1.reference(资源 id)<ImageView android:background = "@drawable/ 图片 ID"/>
    2.color
    <TextView android:textColor = "#00FF00" />
    3.boolean
    4.dimension(尺寸)(dp)5.float(浮点值)6.integer(整形值)7.string(字符串)8.fraction(百分比)9.enum(枚举值)<declare-styleable name="名称">
          <attr name="orientation">
      <enum name="horizontal" value="0" />
      <enum name="vertical" value="1" />
       </attr>
    </declare-styleable>
    10.flag(位或运算)留神:位运算类型的属性在应用的过程中能够应用多个值
    11. 混合属性(应用 | 离开多个属性)

3. 获取属性值

  public CustomNumAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        // 获取自定义属性
        TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.CustomNumAnimView,defStyleAttr,0);
     int roundColor=array.getColor(R.styleable.CustomNumAnimView_round_color, ContextCompat.getColor(context,R.color.purple_200));
    float roundRadius=array.getDimension(R.styleable.CustomNumAnimView_round_radius,50);
     int  textColor=array.getColor(R.styleable.CustomNumAnimView_text_color, Color.WHITE);
    float  textSize=array.getDimension(R.styleable.CustomNumAnimView_text_size,30);
        array.recycle();}

二:自定义 View 应用自定义属性

public class CustomNumAnimView extends View {

    private int roundColor;    // 圆的色彩
    private int textColor;    // 数字的色彩
    private float textSize;    // 数字字体大小
    private float roundRadius;    // 圆的半径

    private Paint mPaint;     // 画笔
    private Rect textRect;    // 包裹数字的矩形

    private boolean isFirstInit = false;   // 是否是第一次初始化

    private CustomPoint leftPoint;    // 右边的数字的实时点
    private String leftNum = "9";
    private ValueAnimator leftAnim;   // 右边数字动画
    private boolean isLeftNumInvalidate = false;  // 右边数字是否重绘界面

    private CustomPoint middlePoint;   // 两头的数字的实时点
    private String middleNum = "9";
    private ValueAnimator middleAnim;   // 两头数字动画
    private boolean isMiddleNumInvalidate = false;    // 两头数字是否重绘界面

    private CustomPoint rightPoint;    // 左边的数字的实时点
    private String rightNum = "9";
    private ValueAnimator rightAnim;   // 左边数字动画
    private boolean isRightNumInvalidate = false;    // 左边数字是否重绘界面


    public CustomNumAnimView(Context context) {this(context, null);
    }

    public CustomNumAnimView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);
    }

    public CustomNumAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
        // 获取自定义属性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomNumAnimView, defStyleAttr, 0);
        roundColor = array.getColor(R.styleable.CustomNumAnimView_round_color, ContextCompat.getColor(context, R.color.purple_200));
        roundRadius = array.getDimension(R.styleable.CustomNumAnimView_round_radius, 50);
        textColor = array.getColor(R.styleable.CustomNumAnimView_text_color, Color.WHITE);
        textSize = array.getDimension(R.styleable.CustomNumAnimView_text_size, 30);
        array.recycle();
        // 创立画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);// 抗锯齿标记
        mPaint.setTextSize(textSize);// 画笔设置文本大小
        textRect = new Rect();
        // 失去数字矩形的宽高,以用来画数字的时候纠正数字的地位
        mPaint.getTextBounds(middleNum, 0, middleNum.length(), textRect);
    }

    /**
     * 测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int size;
        int mode;
        int width;
        int height;
        size = MeasureSpec.getSize(widthMeasureSpec);
        mode = MeasureSpec.getMode(widthMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {    // 确定的值或者 MATCH_PARENT
            width = size;
        } else {    // 示意 WARP_CONTENT
            width = (int) (2 * roundRadius);
        }

        mode = MeasureSpec.getMode(heightMeasureSpec);
        size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {    // 确定的值或者 MATCH_PARENT
            height = size;
        } else {    // 示意 WARP_CONTENT
            height = (int) (2 * roundRadius);
        }
        setMeasuredDimension(width, height);
    }

    /**
     * 重写 onDraw 办法
     */
    @Override
    protected void onDraw(Canvas canvas) {super.onDraw(canvas);
        if (!isFirstInit) {
            // 是
            // 初始化三串数字
            leftPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2 - roundRadius / 2, (float) (getMeasuredHeight() / 2 - roundRadius * (Math.sqrt(3) / 2) - textRect.height() / 2));
            middlePoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2, getMeasuredHeight() / 2 - roundRadius - textRect.height() / 2);
            rightPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2 + roundRadius / 2, (float) (getMeasuredHeight() / 2 - roundRadius * (Math.sqrt(3) / 2) - textRect.height() / 2));
            drawText(canvas);
            startAnimation();   // 开始动画
            isFirstInit = true;

        } else {drawText(canvas);
        }
    }
    private boolean isAnimStart(ValueAnimator anim) {return !anim.isStarted();
    }
    public void startAnim() {if (isAnimStart(leftAnim)) {leftAnim.start();
        }
        if (isAnimStart(middleAnim)) {middleAnim.start();
        }
        if (isAnimStart(rightAnim)) {rightAnim.start();
        }
    }
    /**
     * 在 onDestroy 办法中调用
     */
    public void stopAnim() {leftAnim.end();
        middleAnim.end();
        rightAnim.end();
        leftAnim = null;
        middleAnim = null;
        rightAnim = null;
    }

    /**
     * 画数字
     */
    private void drawText(Canvas canvas) {
        // 画圆
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(roundColor);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, roundRadius, mPaint);
        // 写数字
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        if (isLeftNumInvalidate) {canvas.drawText(leftNum, leftPoint.getX(), leftPoint.getY(), mPaint);
            isLeftNumInvalidate = false;
        }
        if (isMiddleNumInvalidate) {canvas.drawText(middleNum, middlePoint.getX(), middlePoint.getY(), mPaint);
            isMiddleNumInvalidate = false;
        }
        if (isRightNumInvalidate) {canvas.drawText(rightNum, rightPoint.getX(), rightPoint.getY(), mPaint);
            isRightNumInvalidate = false;
        }

    }

    /**
     * 开始动画
     */
    private void startAnimation() {startLeft();
        startMiddle();
        startRight();}

    private void startRight() {final CustomPoint startPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2 + roundRadius / 2, (float) (getMeasuredHeight() / 2 - roundRadius * (Math.sqrt(3) / 2) - textRect.height() / 2));
        final CustomPoint endPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2 + roundRadius / 2, (float) (getMeasuredHeight() / 2 + roundRadius * (Math.sqrt(3) / 2) + textRect.height() / 2));
        rightAnim = ValueAnimator.ofObject(new CustomPointEvaluator(), startPoint, endPoint);
        rightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {rightPoint = (CustomPoint) animation.getAnimatedValue();
                isRightNumInvalidate = true;
                invalidate();}
        });
        rightAnim.addListener(new CustomAnimListener() {
            @Override
            public void onAnimationRepeat(Animator animation) {rightNum = getRandom();
            }
        });
        rightAnim.setStartDelay(150);
        rightAnim.setDuration(300);
        rightAnim.setRepeatCount(ValueAnimator.INFINITE);

    }

    private void startMiddle() {
        // 初始化两头数字的开始点的地位
        final CustomPoint startPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2, getMeasuredHeight() / 2 - roundRadius - textRect.height() / 2);
        // 初始化两头数字的完结点的地位
        final CustomPoint endPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2, getMeasuredHeight() / 2 + roundRadius + textRect.height() / 2);
        middleAnim = ValueAnimator.ofObject(new CustomPointEvaluator(), startPoint, endPoint);
        // 监听从起始点到起点过程中点的变动, 并获取点而后从新绘制界面
        middleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {middlePoint = (CustomPoint) animation.getAnimatedValue();
                isMiddleNumInvalidate = true;
                invalidate();}
        });
        middleAnim.addListener(new CustomAnimListener() {
            @Override
            public void onAnimationRepeat(Animator animation) {middleNum = getRandom();
            }
        });
        middleAnim.setDuration(300);
        middleAnim.setRepeatCount(ValueAnimator.INFINITE);

    }

    private void startLeft() {
        // 属性动画
        final CustomPoint startPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2 - roundRadius / 2, (float) (getMeasuredHeight() / 2 - roundRadius * (Math.sqrt(3) / 2) - textRect.height() / 2));
        final CustomPoint endPoint = new CustomPoint(getMeasuredWidth() / 2 - textRect.width() / 2 - roundRadius / 2, (float) (getMeasuredHeight() / 2 + roundRadius * (Math.sqrt(3) / 2) + textRect.height() / 2));
        leftAnim = ValueAnimator.ofObject(new CustomPointEvaluator(), startPoint, endPoint);
        leftAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {leftPoint = (CustomPoint) animation.getAnimatedValue();
                isLeftNumInvalidate = true;
                invalidate();}
        });
        leftAnim.addListener(new CustomAnimListener() {
            @Override
            public void onAnimationRepeat(Animator animation) {middleNum = getRandom();
            }
        });
        leftAnim.setStartDelay(100);
        leftAnim.setDuration(300);
        leftAnim.setRepeatCount(ValueAnimator.INFINITE);
    }


    /**
     * 获取 0 - 9 之间的随机数
     *
     * @return
     */
    private String getRandom() {int random = (int) (Math.random() * 9);
        return String.valueOf(random);
    }
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lsp="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.ruan.mygitignore.CustomNumAnimView
        android:id="@+id/custom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        lsp:round_radius="50dp"
        lsp:text_size="16dp"/>
</LinearLayout>

这是一个自定 view 的应用的自定义属性

结尾:每一个小小的提高,都是日后的财产

退出移动版