返回

轻松定制刻度尺——Android绘制工具大盘点

Android

在上一篇文章中,我们讨论了Android事件传递机制,这次我们来实战一下,自定义一把可以滑动的刻度尺。通过这个小案例,你将能够掌握Canvas、Paint、触摸反馈、Scroller、VelocityTracker的基本使用。

1. 绘制刻度

每十个刻度有一个粗长的刻度线,刻度值从-100到100,刻度尺总长度为200dp。

private void drawScale() {
    Canvas canvas = new Canvas(mBitmap);
    Paint paint = new Paint();
    paint.setColor(Color.BLACK);
    paint.setStrokeWidth(2);
    int width = mBitmap.getWidth();
    int height = mBitmap.getHeight();

    // 绘制刻度线
    for (int i = -100; i <= 100; i += 10) {
        if (i % 50 == 0) {
            paint.setStrokeWidth(4);
            canvas.drawLine(i * dp + width / 2, 0, i * dp + width / 2, height, paint);
        } else {
            paint.setStrokeWidth(2);
            canvas.drawLine(i * dp + width / 2, 0, i * dp + width / 2, height / 2, paint);
        }
    }

    // 绘制刻度值
    paint.setTextSize(20);
    paint.setStrokeWidth(1);
    for (int i = -100; i <= 100; i += 10) {
        if (i % 50 == 0) {
            canvas.drawText(String.valueOf(i), i * dp + width / 2 - 10, height - 10, paint);
        }
    }
}

2. 触摸反馈

当用户触摸刻度尺时,刻度尺应该能够平滑地滑动。实现这一功能需要用到Scroller和VelocityTracker。

private void initTouchListener() {
    mScaleView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    // 记录按下的位置
                    mDownX = event.getX();
                    mScroller.abortAnimation();
                    return true;
                case MotionEvent.ACTION_MOVE:
                    // 计算移动的距离
                    float dx = event.getX() - mDownX;
                    // 更新刻度尺的位置
                    mScrollX += dx;
                    invalidate();
                    return true;
                case MotionEvent.ACTION_UP:
                    // 计算速度
                    mVelocityTracker.computeCurrentVelocity(1000);
                    float velocityX = mVelocityTracker.getXVelocity();
                    // 启动惯性滑动
                    mScroller.fling(mScrollX, 0, (int) velocityX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
                    invalidate();
                    return true;
                default:
                    break;
            }
            return false;
        }
    });
}

3. 绘制刻度尺

在onDraw方法中,将刻度尺的Bitmap绘制到画布上,并根据mScrollX的值平移刻度尺。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(mBitmap, -mScrollX, 0, null);
}

4. 惯性滑动

Scroller负责惯性滑动的实现。在computeScroll方法中,如果Scroller还没有结束滑动,则更新刻度尺的位置并重绘刻度尺。

@Override
public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
        mScrollX = mScroller.getCurrX();
        invalidate();
    }
}

5. 完整代码

public class ScaleView extends View {

    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Paint mPaint;

    private int mWidth;
    private int mHeight;

    private float mDownX;
    private float mScrollX;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;

    public ScaleView(Context context) {
        super(context);
        init();
    }

    private void init() {
        mBitmap = Bitmap.createBitmap(2000, 200, Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(2);

        mWidth = getResources().getDisplayMetrics().widthPixels;
        mHeight = 200;

        mScroller = new Scroller(getContext());
        mVelocityTracker = VelocityTracker.obtain();

        drawScale();
        initTouchListener();
    }

    private void drawScale() {
        Canvas canvas = new Canvas(mBitmap);
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(2);
        int width = mBitmap.getWidth();
        int height = mBitmap.getHeight();

        // 绘制刻度线
        for (int i = -100; i <= 100; i += 10) {
            if (i % 50 == 0) {
                paint.setStrokeWidth(4);
                canvas.drawLine(i * dp + width / 2, 0, i * dp + width / 2, height, paint);
            } else {
                paint.setStrokeWidth(2);
                canvas.drawLine(i * dp + width / 2, 0, i * dp + width / 2, height / 2, paint);
            }
        }

        // 绘制刻度值
        paint.setTextSize(20);
        paint.setStrokeWidth(1);
        for (int i = -100; i <= 100; i += 10) {
            if (i % 50 == 0) {
                canvas.drawText(String.valueOf(i), i * dp + width / 2 - 10, height - 10, paint);
            }
        }
    }

    private void initTouchListener() {
        mScaleView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        // 记录按下的位置
                        mDownX = event.getX();
                        mScroller.abortAnimation();
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        // 计算移动的距离
                        float dx = event.getX() - mDownX;
                        // 更新刻度尺的位置
                        mScrollX += dx;
                        invalidate();
                        return true;
                    case MotionEvent.ACTION_UP:
                        // 计算速度
                        mVelocityTracker.computeCurrentVelocity(1000);
                        float velocityX = mVelocityTracker.getXVelocity();
                        // 启动惯性滑动
                        mScroller.fling(mScrollX, 0, (int) velocityX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
                        invalidate();
                        return true;
                    default:
                        break;
                }
                return false;
            }
        });
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBitmap, -mScrollX, 0, null);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            mScrollX = mScroller.getCurrX();
            invalidate();
        }
    }
}

这个小案例涵盖了Canvas、Paint、触摸反馈、Scroller、VelocityTracker等基本知识,希望对你有所帮助。