一、简介
最近产品经理总想试一些新鲜东西,于是需求中就添加了一些自定义控件。好久没写过自定义控件了,之前道长也写过一些自定义控件,详见:《属性动画:如何自定义View》、《自定义View:自定义CircleImageView实现及图形渲染》、《自定义View:用Canvas实现转盘View》等。这次有几个转轮的变形,其实这几个自定义控件的实现方式是相似的,其他不说先上图,如下所示:
二、实现
由于这三个控件的实现是相似的,也就不一一详说了,就说其中道长遇到的问题。
2.1 每个单元的滑动计算以及重绘
- 首先定义柱状图的初始位置
这三个控件的初始位置都是屏幕中间,所以需要把初始位置定义为屏幕的二分之一,这里贴的FoodCalView的代码,如下所示:
if (!setStart) {
setStart = false;
startOriganalX = measureWidth / 2;
}
又因为这三个空间的左滑右滑不一致,所以对第一个单元的位置矫正也不同,如下所示:
if (startOriganalX == 0) {
startOriganalX = measureWidth / 2;
}
int startX = (int) (paddingLeft + startOriganalX - barWidth / 2);
注意:在滚轮滑动期间,界面会随着滑动而更新界面,如果处理不好就会一直在初始位置鬼畜。
- 把每个柱状图的bar和间隔定义为一个单元,滑动时计算出当前单元、滑动方式等
if ((currX - lastX) < 0) {
Log.e("yushan", "向左滑动");
if (startOriganalX < measureWidth / 2) {
startOriganalX = measureWidth / 2;
isBoundary = true;
}
moveTo = "Left";
centerPosition = (int) (startOriganalX - measureWidth / 2 + barInterval) / (barWidth + barInterval);
nextDis = (startOriganalX - measureWidth / 2) % (barWidth + barInterval);
} else {
if (startOriganalX > measureWidth / 2 + (barWidth + barInterval) * (innerData.size() - 1)) {
startOriganalX = measureWidth / 2 + (barWidth + barInterval) * (innerData.size() - 1);
isBoundary = true;
}
Log.e("yushan", "向右滑动");
moveTo = "Right";
centerPosition = (int) (startOriganalX - measureWidth / 2 + barWidth) / (barWidth + barInterval);
nextDis = (startOriganalX - measureWidth / 2) % (barWidth + barInterval);
}
注意:如果计算不正确,用户体验非常差
- 在手指抬起时,计算滑动速度和处理
在手指抬起时,要计算手指抬起时移动的速度。当手指移动速度在100~1000之间,并且填充的数据大于屏幕宽度时,逐渐停止移动。
当移动完毕时,如果柱状图bar不在屏幕中间,则根据nextDis让邻近的柱状图移动到屏幕中间。代码如下所示:
case MotionEvent.ACTION_UP:
long endTime = System.currentTimeMillis();
//计算猛滑动的速度,如果是大于某个值,并且数据的长度大于整个屏幕的长度,那么就允许有flIng后逐渐停止的效果
float speed = tempLength / (endTime - startTime) * 1000;
if (Math.abs(speed) > 100 && Math.abs(speed) < 1000 && !isFling && measureWidth < innerData.size() * (barWidth + barInterval)) {
this.post(horizontalScrollRunnable = new HorizontalScrollRunnable(speed));
} else if (nextDis > 0) {
this.post(scroll2CenterRunnable = new Scroll2CenterRunnable(nextDis));
}
isMove = false;
break;
- WeightWheelView的绘制要求在手指移动时刻度尺和拳头的移动填充要实时更新,由于拳头是不规则形状,道长绘制的时候废了不少心思,效果也不错,绘制拳头的代码如下:
private Bitmap drawFistImage() {
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap finalBmp = Bitmap.createBitmap(middleBitmap.getWidth(), middleBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(finalBmp);
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
canvas.drawBitmap(middleBitmap, 0, 0, paint);
return finalBmp;
}
- 在最后说一下,在手指移动时屏幕重绘,重绘的画布会越来越长,然后控件会越来越卡,由于任务时间紧急,这里道长只是简单处理了一下,如下所示:
if (centerPosition % 15 == 0 && centerPosition != 0) {
refreshList.clear();
if (innerData.size() > 20) {
refreshList.addAll(innerData.subList(0, centerPosition + 20));
} else {
refreshList.addAll(innerData);
}
}
关于滚轮绘制,道长暂时说道这里,由于时间紧迫,难免会有问题,希望小伙伴可以指出。如果需要参考一下的小伙伴,可以点击下面的传送门:
附加:
临近年末,公司打算对这个功能做全盘优化,之前有小伙伴说这个自定义控件滑动卡顿,道长抽空对这几个控件做了优化。其实细心地小伙伴发现道长的这个Demo中的三个自定义控件实现方式是相近的,就是相应的变种而已,主要是在OnDraw做一下算法而已,第一个控件具体的OnDraw变动如下:
for (int i = centerPosition - 50; i < centerPosition + 50; i++) {
if (i >= innerData.size() || i < 0) {
continue;
}
float barHeight = 0;
if (scaleTimes != 0) {
float barValue;
if (innerData.get(i).getCount() > maxValue * 2) {
barValue = maxValue * 2;
} else {
barValue = innerData.get(i).getCount();
}
barHeight = barValue / scaleTimes;
}
int startY = (int) (defaultHeight - bottom_view_height - barHeight);
//绘制下面的文字
float bottomTextWidth = mTopTextPaint.measureText(innerData.get(i).date);
float bottomStartX = startX + barWidth / 2 - bottomTextWidth / 2 - i * (barWidth + barInterval);
Rect rect = new Rect();
mTopTextPaint.getTextBounds(innerData.get(i).getDate(), 0, innerData.get(i).getDate().length(), rect);
float bottomStartY = defaultHeight - bottom_view_height + 10 + rect.height();//rect.height()是获取文本的高度;
//绘制线
drawBottomLine(canvas, startX + barWidth / 2 - i * (barWidth + barInterval), bottomStartY);
if (innerData.get(i).count != 0) {
//绘制bar
if (i == centerPosition) {
drawCenterBar(canvas, startX - i * (barWidth + barInterval), startY + dp2Px(30), endY);
} else {
drawBar(canvas, startX - i * (barWidth + barInterval), startY + dp2Px(30), endY);
}
}
//绘制底部的文字
drawTopText(canvas, innerData.get(i).getDate(), bottomStartX, bottomStartY);
}
第二个控件OnDraw代码变动如下:
for (int i = centerPosition - 50; i < centerPosition + 50; i++) {
if (i >= refreshList.size() || i < 0) {
continue;
}
float barHeight = 0;
int startY = (int) (defaultHeight - bottom_view_height - barHeight);
if (i == 0) {
drawText = "今日";
} else {
drawText = refreshList.get(i).getDate();
}
//绘制下面的文字
float bottomTextWidth = mTopTextPaint.measureText(drawText);
float bottomStartX = startX + barWidth / 2 - bottomTextWidth / 2 - (barWidth + barInterval) * i;
Rect rect = new Rect();
mTopTextPaint.getTextBounds(refreshList.get(i).getDate(), 0, refreshList.get(i).getDate().length(), rect);
float bottomStartY = defaultHeight - bottom_view_height + 10 + rect.height();//rect.height()是获取文本的高度;
//绘制底部的文字
drawText(canvas, drawText, bottomStartX, bottomStartY);
}
重点说一下第三个控件,因为前两个控件就是在OnDraw代码有了变动,其他没有变动。而且第三个控件的全部代码也会贴在最后。
- 在OnDraw中的for循环由之前的循环加载所有数据改为了加载数据段,数据段的长度为200,这个可以改动。代码如下:
@Override
protected void onDraw(Canvas canvas) {
if (setStart) {
setStart = false;
startOriganalX = measureWidth / 2 - barWidth / 2 - startChangeX;
}
if (startOriganalX == 0) {
startOriganalX = measureWidth / 2 - barWidth / 2;
}
int startX = (int) (paddingLeft + startOriganalX);
int endY = defaultHeight - bottom_text_size;
backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, bitmapMin, bitmapMin, false);
upBitmap = Bitmap.createScaledBitmap(upBitmap, bitmapMin, bitmapMin, false);
if (onWeightWheelChangedListener != null) {
onWeightWheelChangedListener.weightWheelChanged(centerPosition);
}
for (int i = refreshSize + 0; i <= refreshSize + 200; i++) {
if (i > maxSize) {
break;
}
int startY = (int) (defaultHeight - bottom_view_height);
//绘制下面的文字
float bottomTextWidth = mBottomTextPaint.measureText(i / 10 + "");
float bottomStartX = startX + (barWidth + barInterval) * i;
Rect rect = new Rect();
mBottomTextPaint.getTextBounds(i + "", 0, (i + "").length(), rect);
float bottomStartY = defaultHeight;//rect.height()是获取文本的高度;
if (i % 10 == 0) {
//绘制线
drawBottomLine(canvas, bottomStartX + barWidth / 2, bottomStartY - bottom_text_size, dp2Px(0));
//绘制底部的文字
drawBottomText(canvas, i / 10 + "", bottomStartX - bottomTextWidth / 2, bottomStartY);
} else if (i % 10 == 5) {
//绘制线
drawBottomLine(canvas, bottomStartX + barWidth / 2, bottomStartY - bottom_text_size, dp2Px(5));
if (i / 10 < centerPosition / 10) {
middleBitmap = Bitmap.createScaledBitmap(middleBitmap, bitmapMin, bitmapMin, false);
canvas.drawBitmap(drawFistImage(), bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), null);
} else if (i / 10 == centerPosition / 10) {
if (scale == 1.0f) {
canvas.drawBitmap(upBitmap, bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), mBarPaint);
} else {
middleBitmap = Bitmap.createScaledBitmap(middleBitmap, (int) (bitmapMin * scale), bitmapMin, false);
canvas.drawBitmap(drawFistImage(), bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), null);
canvas.drawBitmap(upBitmap, bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), mBarPaint);
}
} else {
canvas.drawBitmap(upBitmap, bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), mBarPaint);
}
} else {
//绘制线
drawBottomLine(canvas, bottomStartX + barWidth / 2, bottomStartY - bottom_text_size, dp2Px(10));
}
}
drawCenterLine(canvas, endY, dp2Px(0));
}
- 另外一个就是在滑动时进行预加载,左滑右滑都有相应数据段进行加载,代码如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
isBoundary = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getX();
lastY = event.getY();
startTime = System.currentTimeMillis();
//当点击的时候,判断如果是在fling的效果的时候,就停止快速滑动
if (isFling) {
removeCallbacks(horizontalScrollRunnable);
tempLength = 0;
isFling = false;
}
break;
case MotionEvent.ACTION_MOVE:
float currX = event.getX();
float currY = event.getY();
startOriganalX += currX - lastX;
//这是向右滑动
if ((currX - lastX) > 0) {
Log.e("TAG", "向右滑动");
if (startOriganalX > measureWidth / 2) {
startOriganalX = measureWidth / 2 - barWidth / 2;
isBoundary = true;
}
centerPosition = (int) (measureWidth / 2 - startOriganalX) / (barWidth + barInterval);
if (centerPosition - 100 <= refreshSize) {
Log.e("yushan", "refreshSize:" + refreshSize + " maxSize:" + maxSize);
if (refreshSize > 0) {
refreshSize -= 50;
} else {
refreshSize = 0;
}
}
} else {//这是向右滑动
Log.e("TAG", "向左滑动");
if (Math.abs(startOriganalX) > getMoveLength() + (measureWidth + barInterval) / 2) {
startOriganalX = -(getMoveLength() + (measureWidth + barInterval) / 2);
}
centerPosition = (int) (measureWidth / 2 - startOriganalX) / (barWidth + barInterval);
if (refreshSize + 200 - centerPosition <= 100) {
Log.e("yushan", "refreshSize:" + refreshSize + " maxSize:" + maxSize);
if (refreshSize < maxSize) {
refreshSize += 50;
} else {
refreshSize = maxSize;
}
}
}
if (centerPosition % 10 == 0) {
scale = 1.0f;
} else {
scale = (centerPosition % 10) / 10f;
}
tempLength = currX - lastX;
// Log.e("yushan", "startOriganalX:" + startOriganalX + " barWidth:" + scale + " measureWidth / 2:" + (centerPosition % 10));
//如果数据量少,根本没有充满横屏,就没必要重新绘制,
if (measureWidth < maxSize * (barWidth + barInterval)) {
invalidate();
}
lastX = currX;
lastY = currY;
break;
case MotionEvent.ACTION_UP:
long endTime = System.currentTimeMillis();
//计算猛滑动的速度,如果是大于某个值,并且数据的长度大于整个屏幕的长度,那么就允许有flIng后逐渐停止的效果
float speed = tempLength / (endTime - startTime) * 1000;
if (Math.abs(speed) > 100 && Math.abs(speed) < 1000 && !isFling && measureWidth < maxSize * (barWidth + barInterval)) {
this.post(horizontalScrollRunnable = new HorizontalScrollRunnable(speed));
}
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
- 整体代码如下:
/**
* autour : yushan
* date : 2019/1/10
* description :
*/
public class WeightWheelView extends View {
private int barInterval;
private int barWidth;
private int bottom_text_size;
private int center_line_color;
private int bottom_line_color;
private int bottom_text_color;
private Paint mBottomTextPaint;
private Paint mBottomLinePaint;
private int paddingTop;
private int paddingLeft;
private int paddingBottom;
private int paddingRight;
private int defaultHeight = dp2Px(85);
private int bottom_view_height = dp2Px(30);
private float scaleTimes = 1;
private float lastX = 0;
private float lastY = 0;
private int measureWidth = 0;
//这是最初的的位置
private float startOriganalX = 0;
private HorizontalScrollRunnable horizontalScrollRunnable;
//临时滑动的距离
private float tempLength = 0;
private long startTime = 0;
private boolean isFling = false;
private float dispatchTouchX = 0;
private float dispatchTouchY = 0;
//是否到达边界
private boolean isBoundary = false;
// 最大刻度
private int maxSize = 20 * 10;
private Paint mCenterLinePaint;
private int centerPosition;
private int refreshSize;
private Matrix mMatrix;
private Paint mBitmapPaint;
private Paint mBarPaint;
private BitmapShader mBitmapShader;
private int bitmapMin;
private float scale = 1.0f;
private Bitmap backgroundBitmap;
private Bitmap middleBitmap;
private Bitmap upBitmap;
private int pos;
private Handler mHandler;
private boolean setStart;
private int startChangeX;
public WeightWheelView(Context context) {
this(context, null);
}
public WeightWheelView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WeightWheelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.barchar_style);
barInterval = (int) typedArray.getDimension(R.styleable.barchar_style_barInterval, dp2Px(2));
center_line_color = typedArray.getColor(R.styleable.barchar_style_bar_color, Color.parseColor("#333333"));
barWidth = (int) typedArray.getDimension(R.styleable.barchar_style_barWidth, dp2Px(2));
bottom_text_size = (int) typedArray.getDimension(R.styleable.barchar_style_bottom_text_size, sp2Px(9));
bottom_text_color = typedArray.getColor(R.styleable.barchar_style_bottom_text_color, Color.parseColor("#c7c7cc"));
bottom_line_color = typedArray.getColor(R.styleable.barchar_style_bottom_line_color, Color.parseColor("#c7c7cc"));
typedArray.recycle();
initPaint();
initBitmap();
}
private int dp2Px(float dipValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
private int sp2Px(float spValue) {
final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private void initPaint() {
mBottomTextPaint = new Paint();
mBottomTextPaint.setTextSize(bottom_text_size);
mBottomTextPaint.setColor(bottom_text_color);
mBottomTextPaint.setStrokeCap(Paint.Cap.ROUND);
mBottomTextPaint.setStyle(Paint.Style.FILL);
mBottomTextPaint.setDither(true);
mBottomLinePaint = new Paint();
mBottomLinePaint.setColor(bottom_line_color);
mBottomLinePaint.setStrokeCap(Paint.Cap.ROUND);
mBottomLinePaint.setStyle(Paint.Style.FILL);
mBottomLinePaint.setDither(true);
//设置底部线的宽度
mBottomLinePaint.setStrokeWidth(dp2Px(0.6f));
mCenterLinePaint = new Paint();
mCenterLinePaint.setColor(center_line_color);
mCenterLinePaint.setStrokeCap(Paint.Cap.ROUND);
mCenterLinePaint.setStyle(Paint.Style.FILL);
mCenterLinePaint.setDither(true);
mCenterLinePaint.setStrokeWidth(dp2Px(1f));
mBarPaint = new Paint();
mBarPaint.setColor(Color.parseColor("#f58c28"));
mBarPaint.setStrokeCap(Paint.Cap.ROUND);
mBarPaint.setStyle(Paint.Style.FILL);
mBarPaint.setDither(true);
mMatrix = new Matrix();
mBitmapPaint = new Paint();
mBitmapPaint.setColor(Color.BLUE);
mBitmapPaint.setStrokeCap(Paint.Cap.ROUND);
mBitmapPaint.setStyle(Paint.Style.FILL);
mBitmapPaint.setAntiAlias(true);
}
private void initBitmap() {
// 创建Bitmap渲染对象
backgroundBitmap = drawable2Bitmap(getResources().getDrawable(R.drawable.icon_food_fist));
middleBitmap = drawable2Bitmap(getResources().getDrawable(R.drawable.bg_food_fist));
upBitmap = drawable2Bitmap(getResources().getDrawable(R.drawable.icon_fist));
bitmapMin = Math.min(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
}
//进行滑动的边界处理
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("TAG", "MyBarChartView===dispatchTouchEvent==" + ev.getAction());
int dispatchCurrX = (int) ev.getX();
int dispatchCurrY = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//父容器不拦截点击事件,子控件拦截点击事件。如果不设置为true,外层会直接拦截,从而导致motionEvent为cancle
getParent().requestDisallowInterceptTouchEvent(true);
dispatchTouchX = getX();
dispatchTouchY = getY();
break;
case MotionEvent.ACTION_MOVE:
float deltaX = dispatchCurrX - dispatchTouchX;
float deltaY = dispatchCurrY - dispatchTouchY;
if (Math.abs(deltaY) - Math.abs(deltaX) > 0) {//竖直滑动的父容器拦截事件
getParent().requestDisallowInterceptTouchEvent(false);
}
//这是向右滑动,如果是滑动到边界,那么就让父容器进行拦截
if ((dispatchCurrX - dispatchTouchX) > 0 && startOriganalX == 0) {
getParent().requestDisallowInterceptTouchEvent(false);
} else if ((dispatchCurrX - dispatchTouchX) < 0 && startOriganalX == -getMoveLength()) {//这是向右滑动
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
dispatchTouchX = dispatchCurrX;
dispatchTouchY = dispatchCurrY;
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
isBoundary = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getX();
lastY = event.getY();
startTime = System.currentTimeMillis();
//当点击的时候,判断如果是在fling的效果的时候,就停止快速滑动
if (isFling) {
removeCallbacks(horizontalScrollRunnable);
tempLength = 0;
isFling = false;
}
break;
case MotionEvent.ACTION_MOVE:
float currX = event.getX();
float currY = event.getY();
startOriganalX += currX - lastX;
//这是向右滑动
if ((currX - lastX) > 0) {
Log.e("TAG", "向右滑动");
if (startOriganalX > measureWidth / 2) {
startOriganalX = measureWidth / 2 - barWidth / 2;
isBoundary = true;
}
centerPosition = (int) (measureWidth / 2 - startOriganalX) / (barWidth + barInterval);
if (centerPosition - 100 <= refreshSize) {
Log.e("yushan", "refreshSize:" + refreshSize + " maxSize:" + maxSize);
if (refreshSize > 0) {
refreshSize -= 50;
} else {
refreshSize = 0;
}
}
} else {//这是向右滑动
Log.e("TAG", "向左滑动");
if (Math.abs(startOriganalX) > getMoveLength() + (measureWidth + barInterval) / 2) {
startOriganalX = -(getMoveLength() + (measureWidth + barInterval) / 2);
}
centerPosition = (int) (measureWidth / 2 - startOriganalX) / (barWidth + barInterval);
if (refreshSize + 200 - centerPosition <= 100) {
Log.e("yushan", "refreshSize:" + refreshSize + " maxSize:" + maxSize);
if (refreshSize < maxSize) {
refreshSize += 50;
} else {
refreshSize = maxSize;
}
}
}
if (centerPosition % 10 == 0) {
scale = 1.0f;
} else {
scale = (centerPosition % 10) / 10f;
}
tempLength = currX - lastX;
// Log.e("yushan", "startOriganalX:" + startOriganalX + " barWidth:" + scale + " measureWidth / 2:" + (centerPosition % 10));
//如果数据量少,根本没有充满横屏,就没必要重新绘制,
if (measureWidth < maxSize * (barWidth + barInterval)) {
invalidate();
}
lastX = currX;
lastY = currY;
break;
case MotionEvent.ACTION_UP:
long endTime = System.currentTimeMillis();
//计算猛滑动的速度,如果是大于某个值,并且数据的长度大于整个屏幕的长度,那么就允许有flIng后逐渐停止的效果
float speed = tempLength / (endTime - startTime) * 1000;
if (Math.abs(speed) > 100 && Math.abs(speed) < 1000 && !isFling && measureWidth < maxSize * (barWidth + barInterval)) {
this.post(horizontalScrollRunnable = new HorizontalScrollRunnable(speed));
}
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
if (setStart) {
setStart = false;
startOriganalX = measureWidth / 2 - barWidth / 2 - startChangeX;
}
if (startOriganalX == 0) {
startOriganalX = measureWidth / 2 - barWidth / 2;
}
int startX = (int) (paddingLeft + startOriganalX);
int endY = defaultHeight - bottom_text_size;
backgroundBitmap = Bitmap.createScaledBitmap(backgroundBitmap, bitmapMin, bitmapMin, false);
upBitmap = Bitmap.createScaledBitmap(upBitmap, bitmapMin, bitmapMin, false);
if (onWeightWheelChangedListener != null) {
onWeightWheelChangedListener.weightWheelChanged(centerPosition);
}
for (int i = refreshSize + 0; i <= refreshSize + 200; i++) {
if (i > maxSize) {
break;
}
int startY = (int) (defaultHeight - bottom_view_height);
//绘制下面的文字
float bottomTextWidth = mBottomTextPaint.measureText(i / 10 + "");
float bottomStartX = startX + (barWidth + barInterval) * i;
Rect rect = new Rect();
mBottomTextPaint.getTextBounds(i + "", 0, (i + "").length(), rect);
float bottomStartY = defaultHeight;//rect.height()是获取文本的高度;
if (i % 10 == 0) {
//绘制线
drawBottomLine(canvas, bottomStartX + barWidth / 2, bottomStartY - bottom_text_size, dp2Px(0));
//绘制底部的文字
drawBottomText(canvas, i / 10 + "", bottomStartX - bottomTextWidth / 2, bottomStartY);
} else if (i % 10 == 5) {
//绘制线
drawBottomLine(canvas, bottomStartX + barWidth / 2, bottomStartY - bottom_text_size, dp2Px(5));
if (i / 10 < centerPosition / 10) {
middleBitmap = Bitmap.createScaledBitmap(middleBitmap, bitmapMin, bitmapMin, false);
canvas.drawBitmap(drawFistImage(), bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), null);
} else if (i / 10 == centerPosition / 10) {
if (scale == 1.0f) {
canvas.drawBitmap(upBitmap, bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), mBarPaint);
} else {
middleBitmap = Bitmap.createScaledBitmap(middleBitmap, (int) (bitmapMin * scale), bitmapMin, false);
canvas.drawBitmap(drawFistImage(), bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), null);
canvas.drawBitmap(upBitmap, bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), mBarPaint);
}
} else {
canvas.drawBitmap(upBitmap, bottomStartX + barWidth / 2 - bitmapMin / 2, bottomStartY - bottom_text_size - dp2Px(60), mBarPaint);
}
} else {
//绘制线
drawBottomLine(canvas, bottomStartX + barWidth / 2, bottomStartY - bottom_text_size, dp2Px(10));
}
}
drawCenterLine(canvas, endY, dp2Px(0));
}
private void drawBottomLine(Canvas canvas, float bottomStartX, float bottomStartY, float startDis) {
canvas.drawLine(bottomStartX, bottomStartY - startDis, bottomStartX, bottomStartY - dp2Px(20), mBottomLinePaint);
}
private void drawNoDataText(Canvas canvas) {
String text = "loading...";
float textWidth = mBottomTextPaint.measureText(text);
canvas.drawText(text, measureWidth / 2 - textWidth / 2, defaultHeight / 2 - 10, mBottomTextPaint);
}
//绘制中心bar
private void drawCenterLine(Canvas canvas, int bottomStartY, int startDis) {
Rect mRect = new Rect((measureWidth - barWidth) / 2, bottomStartY, (measureWidth - barWidth) / 2 + barWidth, bottomStartY - dp2Px(20));
canvas.drawRect(mRect, mCenterLinePaint);
}
private void drawBottomText(Canvas canvas, String text, float bottomStartX, float bottomStartY) {
canvas.drawText(text, bottomStartX, bottomStartY, mBottomTextPaint);
}
private Bitmap drawFistImage() {
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap finalBmp = Bitmap.createBitmap(middleBitmap.getWidth(), middleBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(finalBmp);
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
canvas.drawBitmap(middleBitmap, 0, 0, paint);
return finalBmp;
}
/**
* drawable转bitmap
*
* @param drawable
* @return
*/
private Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
}
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
if (widthMode == MeasureSpec.EXACTLY) {
measureWidth = width = widthSize;
} else {
width = getAndroiodScreenProperty().get(0);
}
if (heightMode == MeasureSpec.EXACTLY) {
defaultHeight = height = heightSize;
} else {
height = defaultHeight;
}
setMeasuredDimension(width, height);
paddingTop = getPaddingTop();
paddingLeft = getPaddingLeft();
paddingBottom = getPaddingBottom();
paddingRight = getPaddingRight();
}
public void setCenterPosition(int centerPosition){
setStart = true;
if (centerPosition > maxSize) {
centerPosition = maxSize;
}
this.startChangeX = centerPosition * (barWidth + barInterval);
Log.e("yushan", "startOriganalX1:" + startOriganalX + "measureWidth / 2 - barWidth / 2:" + (measureWidth / 2 - barWidth / 2));
this.centerPosition = centerPosition;
if (centerPosition == 0){
refreshSize = 0;
} else {
refreshSize = centerPosition - 50;
if (refreshSize < 0) {
refreshSize = 0;
}
}
if (centerPosition % 10 == 0) {
scale = 1.0f;
} else {
scale = (centerPosition % 10) / 10f;
}
}
private ArrayList<Integer> getAndroiodScreenProperty() {
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels; // 屏幕宽度(像素)
int height = dm.heightPixels; // 屏幕高度(像素)
float density = dm.density; // 屏幕密度(0.75 / 1.0 / 1.5)
int densityDpi = dm.densityDpi; // 屏幕密度dpi(120 / 160 / 240)
// 屏幕宽度算法:屏幕宽度(像素)/屏幕密度
int screenWidth = (int) (width / density); // 屏幕宽度(dp)
int screenHeight = (int) (height / density);// 屏幕高度(dp)
ArrayList<Integer> integers = new ArrayList<>();
integers.add(screenWidth);
integers.add(screenHeight);
return integers;
}
private int getMoveLength() {
return (barWidth + barInterval) * maxSize - measureWidth;
}
public boolean isBoundary() {
return isBoundary;
}
public void setHandler(Handler handler) {
mHandler = handler;
}
private class HorizontalScrollRunnable implements Runnable {
private float speed = 0;
public HorizontalScrollRunnable(float speed) {
this.speed = speed;
}
@Override
public void run() {
if (Math.abs(speed) < 50) {
isFling = false;
return;
}
isFling = true;
startOriganalX += speed / 15;
speed = speed / 1.15f;
//这是向右滑动
if ((speed) > 0) {
Log.e("TAG", "向右滑动");
if (startOriganalX > measureWidth / 2) {
startOriganalX = measureWidth / 2 - barWidth / 2;
isBoundary = true;
}
centerPosition = (int) (measureWidth / 2 - startOriganalX) / (barWidth + barInterval);
if (centerPosition - 100 <= refreshSize) {
Log.e("yushan", "refreshSize:" + refreshSize + " maxSize:" + maxSize);
if (refreshSize > 0) {
refreshSize -= 50;
} else {
refreshSize = 0;
}
}
} else {//这是向右滑动
Log.e("TAG", "向左滑动");
if (Math.abs(startOriganalX) > getMoveLength() + (measureWidth + barInterval) / 2) {
startOriganalX = -(getMoveLength() + (measureWidth + barInterval) / 2);
}
centerPosition = (int) (measureWidth / 2 - startOriganalX) / (barWidth + barInterval);
if (refreshSize + 200 - centerPosition <= 100) {
Log.e("yushan", "refreshSize:" + refreshSize + " maxSize:" + maxSize);
if (refreshSize < maxSize) {
refreshSize += 50;
} else {
refreshSize = maxSize;
}
}
}
if (centerPosition % 10 == 0) {
scale = 1.0f;
} else {
scale = (centerPosition % 10) / 10f;
}
postDelayed(this, 20);
invalidate();
}
}
private OnWeightWheelChangedListener onWeightWheelChangedListener;
public void setOnWeightWheelChangedListener(OnWeightWheelChangedListener onWeightWheelChangedListener) {
this.onWeightWheelChangedListener = onWeightWheelChangedListener;
}
public interface OnWeightWheelChangedListener {
public void weightWheelChanged(int weight);
}
}
因为道长做的功能做了相关的改动,所以代码其他地方有些修改,但是不影响大局,以后道长有时间的话会把这次优化放到Demo中,供小伙伴参考。最后新年快乐_