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的使用的自定义属性
结尾:每一个小小的进步,都是日后的财富
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。