自定义控件:玩转滚轮(柱状图、日期滚轮、刻度尺)

一、简介

最近产品经理总想试一些新鲜东西,于是需求中就添加了一些自定义控件。好久没写过自定义控件了,之前道长也写过一些自定义控件,详见:《属性动画:如何自定义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);
                    }
                }

关于滚轮绘制,道长暂时说道这里,由于时间紧迫,难免会有问题,希望小伙伴可以指出。如果需要参考一下的小伙伴,可以点击下面的传送门:

WheelDemo

附加:
临近年末,公司打算对这个功能做全盘优化,之前有小伙伴说这个自定义控件滑动卡顿,道长抽空对这几个控件做了优化。其实细心地小伙伴发现道长的这个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中,供小伙伴参考。最后新年快乐_

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值