1.概述
很长的一个寒假,难免无聊,就温习一下自定义view,一直用虾
米音乐,觉得虾米的进度条挺好看的,而且也简单,方便练手,
就仿着做了一个。没图说个X。先上图。
如图,支持拖动,改变字体,背景,进度条颜色时间,加载中等功能。
2.功能实现
2.1 自定义属性
自定义view常规步骤,首先自定义需要的属性。
分析需要的控件属性,发现需要时间,字体大小,字体颜色,背
景颜色,进度条的起始颜色和终止颜色。
在attr文件下定义
<declare-styleable name="TimeProgressView" >
<attr name="totalTime" format="integer"/>
<attr name="currentTime" format="integer" />
<attr name="textSize" format="dimension"/>
<attr name="textColor" format="color" />
<attr name="bgColor" format="color" />
<attr name="lineStartColor" format="color" />
<attr name="lineEndColor" format="color" />
2.2 在view中初始化属性
public TimeProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TimeProgressView, defStyleAttr, 0);
mTotalTime = a.getInt(R.styleable.TimeProgressView_totalTime, DEFAULT_TOTAL_TIME);
mCurrentTime = a.getInt(R.styleable.TimeProgressView_currentTime, DEFAULT_CURRENT_TIME);
mBgColor = a.getColor(R.styleable.TimeProgressView_bgColor, DEFAULT_BGCOLOR);
mTextColor = a.getColor(R.styleable.TimeProgressView_textColor, DEFAULT_COLOR);
mLineStartColor = a.getColor(R.styleable.TimeProgressView_lineStartColor, DEFAULT_STARTCOLOR);
mLineEndColor = a.getColor(R.styleable.TimeProgressView_lineEndColor, DEFAULT_ENDCOLOR);
mTextSize = a.getDimensionPixelSize(R.styleable.TimeProgressView_textSize,
(int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
DEFAULT_TEXT_SIZE,
getResources().getDisplayMetrics()));
a.recycle();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
;
mPaint.setTextSize(mTextSize);
threadDraw();
}
private void threadDraw() {
// 绘图线程
new Thread() {
public void run() {
while (isDraw) {
changePosition(); //点的位置计算
postInvalidate();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
首先获取自定义的属性,对自定义属性赋默认值。然后开启一个
线程负责绘图 。
在线程中通过判断isDraw的值来确定是否需要绘图。在绘图线
程中改变当前空控件位置。这个方法在onMesure()讲完后再
讲。
2.3 onMesure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Rect rect = getTextRect();
int textWidth = rect.width();
int textHeight = rect.height();
int width = measureWidth(widthMeasureSpec, textWidth);
int height = measureHeight(heightMeasureSpec, textHeight);
setMeasuredDimension(width, height);
}
通过一个矩形来测量文字占的空间,然后分别测量高度和宽度。
测量高度
private int measureHeight(int heightMeasureSpec, int textHeight) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
int height = 0;
//无论什么模式大小都只受字体大小影响
if (mode == MeasureSpec.EXACTLY) {
height = textHeight + DEFAULT_PADDINGTOP + DEFAULT_PADDINGBOTTOM;
} else if (mode == MeasureSpec.AT_MOST) {
height = textHeight + DEFAULT_PADDINGTOP + DEFAULT_PADDINGBOTTOM;
}
return height;
}
在高度中将高度写死,把高度的大小变成只受字体大小影响
测量宽度
private int measureWidth(int widthMeasureSpec, int textWidth) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);
int width = 0;
if (mode == MeasureSpec.EXACTLY) {
width = size;
} else if (mode == MeasureSpec.AT_MOST) {
width = textWidth + DEFAULT_PADDINGRIGHT + DEFAULT_PADDINGLEFT;
}
return width;
}
控件位置计算
private void changePosition() {
if (mBound != null) {
if (mCurrentTime <= mTotalTime) {
position = (getMeasuredWidth() - (mBound.width() + DEFAULT_PADDINGLEFT + DEFAULT_PADDINGRIGHT)) * (mCurrentTime) / mTotalTime;
}
}
}
当前控件的位置受屏幕宽度,文字的宽度,左右的边距以及当前时间占比影响。
2.4 onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBg(canvas);
drawText(canvas);
drawLine(canvas);
}
在onDraw方法中分别用三个方法画了文字背景的滑块,文字,和进度线段。
drawBg方法
private void drawBg(Canvas canvas) {
//画时间背景
mPaint.setColor(mBgColor);
mPaint.setStyle(Paint.Style.FILL);
int viewHeight = getMeasuredHeight();
if (position + mBound.width()> getMeasuredWidth()){ //对控件位置进行控制
position = getMeasuredWidth() -mBound.width()- DEFAULT_PADDINGRIGHT -DEFAULT_PADDINGLEFT;
}
RectF ovalRect = new RectF(position, 0, mBound.width() + DEFAULT_PADDINGLEFT + DEFAULT_PADDINGRIGHT + position, viewHeight);
canvas.drawRoundRect(ovalRect, viewHeight / 2, viewHeight / 2, mPaint);
}
首先对画笔的颜色,风格等进行设置。
对控件的位置进行控制,保证控件的位置不会超过屏幕。
drawText方法
private void drawText(Canvas canvas) {
//画时间文字
int viewHeight = getMeasuredHeight();
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
int x = position + DEFAULT_PADDINGLEFT;
int y = (int) (viewHeight / 2 +
(fontMetrics.descent - fontMetrics.ascent) / 2
- fontMetrics.descent);
mPaint.setColor(mTextColor);
canvas.drawText(intToTime(mCurrentTime) + "/" + intToTime(mTotalTime), x, y, mPaint);
if (mCurrentTime < mTotalTime) {
//当前时间递加*
mCurrentTime++;
if (mCurrentTime == 600) {
//刷新大小及界面
requestLayout();
}
if (isDraw ==false){
setDraw(isDraw);
isDraw=!isDraw;
setDraw(isDraw);
}
} else {
isDraw = false;
}
}
确定了text的x,y的位置,将text画在屏幕上,递加时间。
drawLine方法
private void drawLine(Canvas canvas) {
//画线段
Paint linePaint = new Paint();
linePaint.setStrokeWidth(getMeasuredHeight()/7);
Point startPoint = new Point(0,getMeasuredHeight()/2);
Point endPoint = new Point(position,getMeasuredHeight()/2);
LinearGradient lg = new LinearGradient(
startPoint.x, startPoint.y, endPoint.x, endPoint.y,
mLineStartColor, mLineEndColor, Shader.TileMode.CLAMP);
linePaint.setShader(lg);
canvas.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y,linePaint);
}
确定起始点和终止点的位置,通过线性渐变对线段的颜色进行控制。
3 .代码地址
代码已上传到github