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-styleablename:属性汇合名称attrname:属性名称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的应用的自定义属性
结尾:每一个小小的提高,都是日后的财产