自定义View
打折标签,数量增减可带动画效果。底部下载链接里的代码不是最新,本文才是。
学习心得
- 创建自定义view,首先分析所需的界面显示以及功能需求,然后确定自身所需的自定义属性,创建attr文件。
- 接下来就是绘制部分的设计了。根据界面效果,拆分或者组合出所需要的效果。要注意的是我一般先写onDraw方,然后据此编写onMeasure进行测量自适应操作。按照调用顺序onMeasure、onDraw、onLayout进行调用,由于布局的层级深度可能会多次调用onMeasure等。一般我们要编写的就是这三个。
- 一般都是在构造函数里创建一个init方法,来获取自定义属性(attr),在获取结束后记得a.recycle();释放掉。
- 注意:在onDraw不要创建新的实例,这样减少不必要的开支。
attr类
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="OffFlagView">
<attr name="offTopTitleTextSize" format="dimension"/>
<attr name="offCenterTitleTextSize" format="dimension"/>
<attr name="offTextBackgroundColor" format="color"/>
<attr name="offIsAnim" format="boolean"/>
</declare-styleable>
</resources>
- view类
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
/**
* author: tc
* date: 2015/12/11 & 16:04
* version 1.0
* description 折扣标签
* modify by
*/
public class OffFlagView extends View {
public static final String TAG = OffFlagView.class.getSimpleName();
private int mTextBackgroundColor = -1;
private String mTopText = "00%";
private String mCenterText = "OFF";
/**
* op文本的大小
*/
private int mTopTitleTextSize = 0;
/**
* center文本的大小
*/
private int mCenterTitleTextSize = 0;
private TextPaint mPaint;
/**
* 绘制时控制文本绘制的范围
*/
private Rect mBound;
/**
* 是否启用数值变化动画
*/
private boolean mIsAnim;
private Path mPath;
private int mPaddingTop;
private int mPaddingLeft;
private int mPaddingRight;
private int mPaddingBottom;
public OffFlagView(Context context) {
this(context, null);
}
public OffFlagView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public OffFlagView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public OffFlagView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(attrs);
}
public int getTopValue() {
return mTopValue;
}
public void setTopValue(int topValue) {
this.mTopText = String.valueOf(topValue + "%");
this.mTopValue = topValue;
if (mIsAnim) {
this.startAnimation(anim);//动画形式的动态显示数值
}
}
public String getCenterText() {
return mCenterText;
}
public void setCenterText(String centerText) {
this.mCenterText = centerText;
}
public int getTextBackgroundColor() {
return mTextBackgroundColor;
}
public void setTextBackgroundColor(int textBackgroundColor) {
this.mTextBackgroundColor = textBackgroundColor;
}
public int getTopTitleTextSize() {
return mTopTitleTextSize;
}
public void setTopTitleTextSize(int topTitleTextSize) {
this.mTopTitleTextSize = topTitleTextSize;
}
public int getCenterTitleTextSize() {
return mCenterTitleTextSize;
}
public void setCenterTitleTextSize(int centerTitleTextSize) {
this.mCenterTitleTextSize = centerTitleTextSize;
}
public boolean isIsAnim() {
return mIsAnim;
}
public void setIsAnim(boolean mIsAnim) {
this.mIsAnim = mIsAnim;
}
private void initView(AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.OffFlagView);
mTopTitleTextSize = a.getDimensionPixelSize(R.styleable.OffFlagView_offTopTitleTextSize, 0);
mCenterTitleTextSize = a.getDimensionPixelSize(R.styleable.OffFlagView_offCenterTitleTextSize, 0);
mTextBackgroundColor = a.getResourceId(R.styleable.OffFlagView_offTextBackgroundColor, -1);
mIsAnim = a.getBoolean(R.styleable.OffFlagView_offIsAnim, false);
a.recycle();
}
/**
* 获得绘制文本的宽和高
*/
mPaint = new TextPaint();
mBound = new Rect();
mPaint.setTextSize(mTopTitleTextSize);
mPaint.getTextBounds(mTopText, 0, mTopText.length(), mBound);
mPath = new Path();
anim = new BarAnimation();
anim.setDuration(1000);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
mPaint.setTextSize(mTopTitleTextSize);
if (mTopText.length() >= 3) {
mPaint.getTextBounds(mTopText, 0, mTopText.length(), mBound);
} else {
mPaint.getTextBounds("00%", 0, 3, mBound);//可能打折的数值是4%这种2位长度,所以宽度计算需要调整,避免小于OFF的宽度。
}
float textWidth = mBound.width();
mPaddingLeft = getPaddingLeft() <= 0 ? dip2px(getContext(), 2) : getPaddingLeft();
mPaddingRight = getPaddingRight() <= 0 ? dip2px(getContext(), 2) : getPaddingRight();
width = (int) (mPaddingLeft + textWidth + mPaddingRight);
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(mTopTitleTextSize);
mPaint.getTextBounds(mTopText, 0, mTopText.length(), mBound);
float textHeight = mBound.height();
mPaddingTop = getPaddingTop() <= 0 ? dip2px(getContext(), 5) : getPaddingTop();
mPaddingBottom = getPaddingBottom();
height = (int) (mPaddingTop * 2 + textHeight * 3 + mPaddingBottom);
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int threeCanvasHeight = canvasHeight / 3;
if (mTopTitleTextSize <= 0) {
mTopTitleTextSize = dip2px(getContext(), 10);
}
if (mCenterTitleTextSize <= 0) {
mCenterTitleTextSize = dip2px(getContext(), 10);
}
if (mTextBackgroundColor != -1) {
mPaint.setColor(getContext().getResources().getColor(mTextBackgroundColor));
}
mPaint.setAntiAlias(true);//无齿轮感
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);//设置为抗锯齿
mPath.moveTo(0, 0);// 此点为多边形的起点
mPath.lineTo(canvasWidth, 0);
mPath.lineTo(canvasWidth, threeCanvasHeight * 2 + mPaddingTop);
mPath.lineTo(0, threeCanvasHeight * 2 + mPaddingTop);
mPath.close(); // 使这些点构成封闭的多边形
canvas.drawPath(mPath, mPaint);
mPath.reset();
mPath.moveTo(0, threeCanvasHeight * 2 + mPaddingTop);// 此点为多边形的起点
mPath.lineTo(canvasWidth / 2, threeCanvasHeight * 2 + mPaddingTop);
mPath.lineTo(0, canvasHeight);
mPath.close(); // 使这些点构成封闭的多边形
canvas.drawPath(mPath, mPaint);
mPath.reset();
mPath.moveTo(canvasWidth / 2, threeCanvasHeight * 2 + mPaddingTop);// 此点为多边形的起点
mPath.lineTo(canvasWidth, threeCanvasHeight * 2 + mPaddingTop);
mPath.lineTo(canvasWidth, canvasHeight);
mPath.close(); // 使这些点构成封闭的多边形
canvas.drawPath(mPath, mPaint);
mPaint.reset();
mPaint.setTextSize(mTopTitleTextSize);
mPaint.setColor(getContext().getResources().getColor(R.color.white));
mPaint.setAntiAlias(true);//无齿轮感
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);//设置为抗锯齿
mPaint.setTypeface(Typeface.DEFAULT_BOLD);//加粗
//设置居中对齐
mPaint.setTextAlign(Paint.Align.CENTER);//设置居中对齐
canvas.save();
canvas.translate(canvasWidth / 2, threeCanvasHeight / 2 + mPaddingTop);
canvas.drawText(mTopText, 0, 0, mPaint);
canvas.restore();
mPaint.setTextSize(mCenterTitleTextSize);
mPaint.setTextAlign(Paint.Align.CENTER);//设置居中对齐
canvas.save();
canvas.translate(canvasWidth / 2, threeCanvasHeight + threeCanvasHeight / 2 + mPaddingTop);
canvas.drawText(mCenterText, 0, 0, mPaint);
canvas.restore();
}
private BarAnimation anim;
private int mTopValue;//正在动画中的打折数值
public class BarAnimation extends Animation {
/**
* Initializes expand collapse animation, has two types, collapse (1) and expand (0).
*/
public BarAnimation() {
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
mTopText = String.valueOf((int) (interpolatedTime * mTopValue) + "%");
} else {
mTopText = String.valueOf(mTopValue + "%");
}
postInvalidate();
}
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
private int dip2px(Context context, final float dpValue) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * density + 0.5f);
}
}