最近工作繁忙,一直都埋头在工作中,也不知这么热心工作究竟是为了什么,不知不觉的,到今天才晓得夏天已经来了。天气热,心也热。
网络上出个牛人,辞职信上写着:世界那么大,我想去看看。由衷的佩服她的勇气,我也想去看看这大千世界,可惜我们总是身不由己,有太多的放不下,或许哪天放下了,我也出去走走。
之前一直以为罗永浩是个逗比,直到前两天看了他几期演讲,才发现逗比一直是我。他有句话我很欣赏,这里也分享给大家:在什么样的年纪,就做什么样的事情,怎么能在每个阶段都做错呢!PS:该玩的年纪玩,该谈恋爱的年纪谈恋爱,该成家立业担起一个家的年纪就要挺起胸膛,总不能错过玩的年纪,错过谈恋爱的年纪,而成家后却又搞外遇,怎么能在每个阶段都做错呢?
在我有那个勇气也在辞职信上写着“世界那么大,我也想去看看!”之前,还是要把眼前的事情做完,现在开始规划。
正文
Android进度条是很多人都接触到的,然而面对各种各样的需求时,原生的很多东西都满足不了我们的需要,在各种压迫下,大家都选择自定义来解决问题。今天给大家介绍一下自定义进度条。
提到自定义,这曾经一直是使我头疼的问题,看过很多大牛的源码之后,发现一个道理,有些代码不应该记住,而有些代码也不应该忘记。所以我决定,今天的博客里给大家分享方法。(其实是代码逻辑实在是太糟糕了,我写过后回头看的时候都不知道自己为什么那么写,而且还必须那么写,哈哈。)
首先我们要知道自定义View中那些方法是我们需要用到的。
构造方法:
构造方法是必不可少的,所有在View对象一出来就需要存在的都要在这里初始化。这里就不多说了。
onDraw
@Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); }
onDraw方法用来绘制我们需要的东西,任何东西,bitmap、线、矩形、路径、等等。
onSizeChanged
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // TODO Auto-generated method stub super.onSizeChanged(w, h, oldw, oldh); }
这里通常处理跟控件宽高有关的数值。
以上这三个,就可以完成常规的自定义组件,往往我们的业务逻辑上,需要加上很多事件,比如onclick,ontouch等。用到这些事件监听时,通常需要invalidate方法来在需要的时候刷新界面。
需要注意的是,每个方法的执行顺序,建议大家自己用log日志实验一下。方便记忆。
接下来开始贴代码,现在大多数需求都是圆角进度条,这里也以此为例。
onDraw方法是用来绘制任何我们需要的东西的,所以进度条也在此绘制,我将进度条矩形抽取出一个方法,方便复用。
/** * 绘制矩形 * * @param canvas * @param paint * @param left * 左 * @param top * 上 * @param right * 右 * @param bottom * 下 */ private void drawRect(Canvas canvas, Paint paint, float left, float top, float right, float bottom) { RectF rectF = new RectF(left, top, right, bottom); if (isRoundRect) { canvas.drawRoundRect(rectF, mRadius, mRadius, paint); } else { canvas.drawRect(rectF, paint); } }
这个方法,我们就可以用任何颜色的画笔,在任何位置绘制矩形。
好了,我们的自定义进度条讲完了。
但是这也就是纯粹的进度条了,需要对应的set和get方法来设置和获取值。set后,记得调用invalidate方法来重绘界面。
我的进度条有拖拽功能,先绘制拖拽的圆圈。
/** * 绘制拖动点 * @param canvas */ private void drawDragClick(Canvas canvas) { mDragClickPaint.setColor(Color.BLACK); RectF oval = new RectF(mAboveProgressWidth - mDragClickPaintRadius, mStartY + mProgressRealHeight/2 - mDragClickPaintRadius, mAboveProgressWidth + mDragClickPaintRadius, mStartY + mProgressRealHeight/2 + mDragClickPaintRadius); canvas.drawOval(oval, mDragClickPaint); mDragClickPaint.setColor(Color.WHITE); RectF oval2 = new RectF(mAboveProgressWidth - mDragClickPaintRadius + 1, mStartY + mProgressRealHeight/2 - mDragClickPaintRadius + 1, mAboveProgressWidth + mDragClickPaintRadius - 1, mStartY + mProgressRealHeight/2 + mDragClickPaintRadius - 1); canvas.drawOval(oval2, mDragClickPaint); }
既然是拖拽,肯定是ontouch方法来处理业务。
private boolean isActionUp; @Override public boolean onTouch(View v, MotionEvent event) { int code = event.getAction(); // Log.i("progress", "onTouch"+mAboveProgressWidth); switch (code) { case MotionEvent.ACTION_DOWN: isActionUp = false; setOnTouchInit(event); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: isActionUp = true; setOnTouchInit(event); break; case MotionEvent.ACTION_MOVE: isActionUp = false; setOnTouchInit(event); break; } return true; } /** * 触摸时设置边界 */ private void setOnTouchInit(MotionEvent event){ if (isTransfer) { if (event.getX() < mDragClickPaintRadius) { mAboveProgressWidth = mDragClickPaintRadius; }else if (event.getX() > mCenterProgressWidth) { mAboveProgressWidth = mCenterProgressWidth; // Log.i("progress", "onTouch"+mAboveProgressWidth + "***" + mCenterProgressWidth); }else { mAboveProgressWidth = event.getX(); } }else { if (event.getX() < mDragClickPaintRadius) { mAboveProgressWidth = mDragClickPaintRadius; }else if (event.getX() > mWidth - mDragClickPaintRadius) { mAboveProgressWidth = mWidth - mDragClickPaintRadius; }else { mAboveProgressWidth = event.getX(); } } mFirstValue = mMaxValue *( mAboveProgressWidth - mDragClickPaintRadius) / mDrawScaleWidth; if (mDragListener != null && isActionUp) { mDragListener.onDrag(this, getmFirstValue()); } invalidate(); }
这样,就能实现拖拽的效果了,当然功能不止这些,放开脑洞,当基础条件具备时,一切都可以set和get。
贴上全部的控件代码
package com.qiyuan.activity.view; import java.util.Collections; import java.util.List; import android.R.integer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.View.OnClickListener; /** * 可以拖动的进度条 * * @author lizhipeng * */ public class CanDragProgressView extends View implements OnTouchListener,OnClickListener{ private int mWidth, mHeight;// 整个的宽和高 private long mDrawScaleWidth;// 画刻度时用的宽度(两边减去拖动点半径的宽度) private int mScaleHeight = 10;// 刻度线的高度 private int mProgressHeight;// 进度条的相对高度 private float mProgressRealHeight = 40;//进度条真实高度 private int mAverageScaleNumber = 10;// 设置均分刻度的个数(多少个单位) private int mScaleLineWidth, mScalerLineHeight;// 刻度线宽高 private int mRadius;// 进度条圆角半径 private float mDragClickPaintRadius;// 拖动点半径 private float mStartX; private float mStartY = 5; private float mFirstValue;//显示在拖动条上的值 private float mSecondValue;//第二个值 private float mBgProgressWidth, mCenterProgressWidth, mAboveProgressWidth;// 背景、中间、上层进度条宽度(长度) private Paint mBackgroundPaint, mCenterPaint, mAbovePaint, mScalePaint, mDragClickPaint;// 背景画笔,中间层画笔,上层画笔、刻度线、拖动点 private boolean isRoundRect = true;// 是否是圆角矩形 private boolean isAverageScale = true;// 是否均分刻度,如果不均分刻度,需要用设置刻度的集合mScaleList private boolean isDrawScaleUp = false;// 绘制刻度在进度条上或者下,默认是下 private boolean isDrawScale = true;// 是否绘制刻度 private boolean isDrawCenterScale = false;// 是否绘制中间部分的刻度 private boolean isTransfer = false;// 是否是Transfer界面 private float mMaxValue;//最大值 private List<Float> mScaleList; private DragListener mDragListener;//滑动监听 public interface DragListener{ public void onDrag(CanDragProgressView view,float value); } public DragListener getmDragListener() { return mDragListener; } public void setmDragListener(DragListener mDragListener) { this.mDragListener = mDragListener; } public boolean isTransfer() { return isTransfer; } public void setTransfer(boolean isTransfer) { this.isTransfer = isTransfer; invalidate(); } public boolean isDrawCenterScale() { return isDrawCenterScale; } public void setDrawCenterScale(boolean isDrawCenterScale) { this.isDrawCenterScale = isDrawCenterScale; invalidate(); } public float getmSecondValue() { return mSecondValue; } public void setmSecondValue(float mSecondValue) { this.mSecondValue = mSecondValue; invalidate(); } public float getmFirstValue() { return Float.parseFloat(String.format("%1.2f", mFirstValue)); } public void setmFirstValue(float mValue) { this.mFirstValue = mValue; invalidate(); } public List<Float> getmScaleList() { return mScaleList; } public void setmScaleList(List<Float> list) { Collections.sort(list); this.mScaleList = list; invalidate(); } public float getmProgressRealHeight() { return mProgressRealHeight; } public void setmProgressRealHeight(float mProgressRealHeight) { this.mProgressRealHeight = mProgressRealHeight; invalidate(); } public void setmScaleHeight(int mScaleHeight) { this.mScaleHeight = mScaleHeight; invalidate(); } public boolean isDrowScaleUp() { return isDrawScaleUp; } public void setDrowScaleUp(boolean isDrowScaleUp) { this.isDrawScaleUp = isDrowScaleUp; mStartY = mScaleHeight + getFontHeight(mScalePaint) + 5; invalidate(); } public int getmAverageScaleNumber() { return mAverageScaleNumber; } public void setmAverageScaleNumber(int mAverageScaleNumber) { this.mAverageScaleNumber = mAverageScaleNumber; invalidate(); } public int getmScaleLineWidth() { return mScaleLineWidth; } public void setmScaleLineWidth(int mScaleLineWidth) { this.mScaleLineWidth = mScaleLineWidth; invalidate(); } public int getmScalerLineHeight() { return mScalerLineHeight; } public void setmScalerLineHeight(int mScalerLineHeight) { this.mScalerLineHeight = mScalerLineHeight; invalidate(); } public boolean isAverageScale() { return isAverageScale; } public void setAverageScale(boolean isAverageScale) { this.isAverageScale = isAverageScale; invalidate(); } // private int getmWidth() { // return mWidth; // } // // private void setmWidth(int mWidth) { // this.mWidth = mWidth; // } public int getmHeight() { return mHeight; } public void setmHeight(int mHeight) { this.mHeight = mHeight; } public int getmProgressHeight() { return mProgressHeight; } public void setmProgressHeight(int mProgressHeight) { this.mProgressHeight = mProgressHeight; invalidate(); } public int getmRadius() { return mRadius; } public void setmRadius(int mRadius) { this.mRadius = mRadius; invalidate(); } public float getmBgProgressWidth() { return mBgProgressWidth; } public void setmBgProgressWidth(float mBgProgressWidth) { this.mBgProgressWidth = mBgProgressWidth; invalidate(); } public float getmCenterProgressWidth() { return mCenterProgressWidth; } public void setmCenterProgressWidth(float mCenterProgressWidth) { this.mCenterProgressWidth = mCenterProgressWidth; invalidate(); } public float getmAboveProgressWidth() { return mAboveProgressWidth; } public void setmAboveProgressWidth(float mAboveProgressWidth) { this.mAboveProgressWidth = mAboveProgressWidth; invalidate(); } // ------------------------------------------------------------------Paint------------------ public Paint getmDragClickPaint() { return mDragClickPaint; } public void setmDragClickPaint(Paint mDragClickPaint) { initPain(mDragClickPaint); this.mDragClickPaint = mDragClickPaint; invalidate(); } public Paint getmScalePaint() { return mScalePaint; } public void setmScalePaint(Paint mScalePaint) { initPain(mScalePaint); this.mScalePaint = mScalePaint; invalidate(); } public Paint getmBackgroundPaint() { return mBackgroundPaint; } public void setmBackgroundPaint(Paint mBackgroundPaint) { initPain(mBackgroundPaint); this.mBackgroundPaint = mBackgroundPaint; invalidate(); } public Paint getmCenterPaint() { return mCenterPaint; } public void setmCenterPaint(Paint mCenterPaint) { initPain(mCenterPaint); this.mCenterPaint = mCenterPaint; invalidate(); } public Paint getmAbovePaint() { return mAbovePaint; } public void setmAbovePaint(Paint mAbovePaint) { initPain(mAbovePaint); this.mAbovePaint = mAbovePaint; invalidate(); } // ------------------------------------------------------------------Paint------------------ public boolean isRoundRect() { return isRoundRect; } public void setRoundRect(boolean isRoundRect) { this.isRoundRect = isRoundRect; invalidate(); } public float getmMaxValue() { return mMaxValue; } public void setmMaxValue(float mMaxValue) { this.mMaxValue = mMaxValue; invalidate(); } public CanDragProgressView(Context context) { super(context); // TODO Auto-generated constructor stub init(); } public CanDragProgressView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub init(); } public CanDragProgressView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub init(); } /** * 初始化 */ private void init() { mBackgroundPaint = new Paint(); mBackgroundPaint.setColor(Color.GRAY); mScalePaint = new Paint(); mScalePaint.setColor(Color.GRAY); mScalePaint.setTextSize(16); mCenterPaint = new Paint(); mCenterPaint.setColor(Color.BLUE); mAbovePaint = new Paint(); mAbovePaint.setColor(Color.RED); mAbovePaint.setTextSize(45); mDragClickPaint = new Paint(); mDragClickPaint.setColor(Color.BLACK); this.setOnTouchListener(this); initPain(mDragClickPaint); initPain(mBackgroundPaint); initPain(mScalePaint); initPain(mCenterPaint); initPain(mAbovePaint); } /** * 初始化画笔 * * @param paint */ private void initPain(Paint paint) { paint.setStyle(Paint.Style.FILL_AND_STROKE); paint.setAntiAlias(true);// 抗锯齿效果 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(widthMeasureSpec, 150); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // TODO Auto-generated method stub mWidth = w; mHeight = h; mBgProgressWidth = mWidth; mCenterProgressWidth = 2 * mBgProgressWidth / 3; if (isDrawScaleUp) { mStartY = mScaleHeight + getFontHeight(mScalePaint) + 5; mProgressHeight = (int) (getFontHeight(mScalePaint) + mScaleHeight + mProgressRealHeight + 5 + mStartY); } else { mStartY = 60; mProgressHeight = (int) (mProgressRealHeight + mStartY); } mDragClickPaintRadius = (float) (mProgressRealHeight / 2 + 15); mRadius = (int) (mProgressRealHeight / 2 + 5); mDrawScaleWidth = (long) (mWidth - 2 * mDragClickPaintRadius); mScaleHeight = 10; mAboveProgressWidth = (mDrawScaleWidth*(mFirstValue/mMaxValue) + mDragClickPaintRadius); mCenterProgressWidth = (mDrawScaleWidth*(mSecondValue/mMaxValue) + mDragClickPaintRadius); super.onSizeChanged(w, h, oldw, oldh); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub // 绘制三个矩形 drawRect(canvas, mBackgroundPaint, 0, mStartY, mBgProgressWidth, mProgressHeight); // Log.i("progress", mBgProgressWidth+"**"+mProgressHeight+"**"+mStartY+"**"+getFontHeight(mScalePaint)); drawRect(canvas, mCenterPaint, 0, mStartY, mCenterProgressWidth, mProgressHeight); drawRect(canvas, mAbovePaint, 0, mStartY, mAboveProgressWidth, mProgressHeight); // 绘制刻度 drawScales(canvas); // 绘制拖动点 drawDragClick(canvas); // 绘制滚动数字 drawChangeValue(canvas); super.onDraw(canvas); } /** * 绘制滚动数字 * @param canvas */ private void drawChangeValue(Canvas canvas) { if (mAbovePaint.measureText(String.format("%1.2f", mFirstValue)) + mDrawScaleWidth*mFirstValue/mMaxValue > mWidth) { canvas.drawText(String.format("%1.2f", mFirstValue), mWidth - mAbovePaint.measureText(String.format("%1.2f", mFirstValue)), 3*getFontHeight(mAbovePaint)/4, mAbovePaint); }else { canvas.drawText(String.format("%1.2f", mFirstValue), mDrawScaleWidth*mFirstValue/mMaxValue, 3*getFontHeight(mAbovePaint)/4, mAbovePaint); } } /** * 绘制拖动点 * @param canvas */ private void drawDragClick(Canvas canvas) { mDragClickPaint.setColor(Color.BLACK); RectF oval = new RectF(mAboveProgressWidth - mDragClickPaintRadius, mStartY + mProgressRealHeight/2 - mDragClickPaintRadius, mAboveProgressWidth + mDragClickPaintRadius, mStartY + mProgressRealHeight/2 + mDragClickPaintRadius); canvas.drawOval(oval, mDragClickPaint); mDragClickPaint.setColor(Color.WHITE); RectF oval2 = new RectF(mAboveProgressWidth - mDragClickPaintRadius + 1, mStartY + mProgressRealHeight/2 - mDragClickPaintRadius + 1, mAboveProgressWidth + mDragClickPaintRadius - 1, mStartY + mProgressRealHeight/2 + mDragClickPaintRadius - 1); canvas.drawOval(oval2, mDragClickPaint); } /** * 绘制刻度 * * @param canvas */ private void drawScales(Canvas canvas) { if (isDrawScale) {// 是否绘制刻度 if (isAverageScale) {// 均分的刻度 long unitLenght = mDrawScaleWidth / mAverageScaleNumber;// 单位长度 if (isDrawScaleUp) { for (int i = 0; i < mAverageScaleNumber + 1; i++) { float startX = mDragClickPaintRadius + i * unitLenght; canvas.drawLine(startX, getFontHeight(mScalePaint), startX, mScaleHeight +getFontHeight(mScalePaint), mScalePaint); canvas.drawText(mMaxValue/mAverageScaleNumber*i+"", startX - mScalePaint.measureText(startX + "") / 2, getFontHeight(mScalePaint), mScalePaint); } } else { for (int i = 0; i < mAverageScaleNumber + 1; i++) { if (isDrawCenterScale) { float startX = mDragClickPaintRadius + i * unitLenght; float endY = mProgressHeight + 5 + mScaleHeight; canvas.drawLine(startX, mProgressHeight + 5, startX, endY, mScalePaint); canvas.drawText(mMaxValue/mAverageScaleNumber*i+"", startX - mScalePaint.measureText(startX + "") / 3, endY + getFontHeight(mScalePaint), mScalePaint); }else { if (i == 0 || i == mAverageScaleNumber) { float startX = mDragClickPaintRadius + i * unitLenght; float endY = mProgressHeight + 5 + mScaleHeight; canvas.drawLine(startX, mProgressHeight + 5, startX, endY, mScalePaint); canvas.drawText(mMaxValue/mAverageScaleNumber*i+"", startX - mScalePaint.measureText(startX + "") / 3, endY + getFontHeight(mScalePaint), mScalePaint); } } } } } else { if (isDrawScaleUp) { for (int i = 0; i < mScaleList.size(); i++) { double number = mScaleList.get(i); double unit = mDrawScaleWidth*(number/mMaxValue); float startX = (float) (mDragClickPaintRadius + unit); if (unit != 0) { canvas.drawLine(mDragClickPaintRadius, getFontHeight(mScalePaint), mDragClickPaintRadius, mScaleHeight +getFontHeight(mScalePaint), mScalePaint); canvas.drawText("0", mDragClickPaintRadius - mScalePaint.measureText("0") / 2, getFontHeight(mScalePaint), mScalePaint); } canvas.drawLine(startX, getFontHeight(mScalePaint), startX, mScaleHeight +getFontHeight(mScalePaint), mScalePaint); canvas.drawText(mScaleList.get(i)+"", startX - mScalePaint.measureText(""+mScaleList.get(i))/2, getFontHeight(mScalePaint), mScalePaint); } } else { for (int i = 0; i < mScaleList.size(); i++) { double number = mScaleList.get(i); double unit = mDrawScaleWidth*(number/mMaxValue); float startX = (float) (mDragClickPaintRadius + unit); float endY = mProgressHeight + 5 + mScaleHeight; if (unit != 0) { canvas.drawLine(mDragClickPaintRadius, mProgressHeight + 5, mDragClickPaintRadius, endY, mScalePaint); canvas.drawText("0", mDragClickPaintRadius - mScalePaint.measureText("0") / 2, endY + getFontHeight(mScalePaint), mScalePaint); } canvas.drawLine(startX, mProgressHeight + 5, startX, endY, mScalePaint); canvas.drawText(mScaleList.get(i)+"", startX - mScalePaint.measureText(""+mScaleList.get(i))/2, endY + getFontHeight(mScalePaint), mScalePaint); } } } } } /** * 绘制矩形 * * @param canvas * @param paint * @param left * 左 * @param top * 上 * @param right * 右 * @param bottom * 下 */ private void drawRect(Canvas canvas, Paint paint, float left, float top, float right, float bottom) { RectF rectF = new RectF(left, top, right, bottom); if (isRoundRect) { canvas.drawRoundRect(rectF, mRadius, mRadius, paint); } else { canvas.drawRect(rectF, paint); } } /** * 获取绘制的字体高度 * * @param paint * @return */ private int getFontHeight(Paint paint) { FontMetrics fm = paint.getFontMetrics(); return (int) Math.ceil(fm.descent - fm.ascent); } private boolean isActionUp; @Override public boolean onTouch(View v, MotionEvent event) { int code = event.getAction(); // Log.i("progress", "onTouch"+mAboveProgressWidth); switch (code) { case MotionEvent.ACTION_DOWN: isActionUp = false; setOnTouchInit(event); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: isActionUp = true; setOnTouchInit(event); break; case MotionEvent.ACTION_MOVE: isActionUp = false; setOnTouchInit(event); break; } return true; } /** * 触摸时设置边界 */ private void setOnTouchInit(MotionEvent event){ if (isTransfer) { if (event.getX() < mDragClickPaintRadius) { mAboveProgressWidth = mDragClickPaintRadius; }else if (event.getX() > mCenterProgressWidth) { mAboveProgressWidth = mCenterProgressWidth; // Log.i("progress", "onTouch"+mAboveProgressWidth + "***" + mCenterProgressWidth); }else { mAboveProgressWidth = event.getX(); } }else { if (event.getX() < mDragClickPaintRadius) { mAboveProgressWidth = mDragClickPaintRadius; }else if (event.getX() > mWidth - mDragClickPaintRadius) { mAboveProgressWidth = mWidth - mDragClickPaintRadius; }else { mAboveProgressWidth = event.getX(); } } mFirstValue = mMaxValue *( mAboveProgressWidth - mDragClickPaintRadius) / mDrawScaleWidth; if (mDragListener != null && isActionUp) { mDragListener.onDrag(this, getmFirstValue()); } invalidate(); } @Override public void onClick(View v) { // TODO Auto-generated method stub } }
That‘s all。