android自定义控件Android开发收藏夹安卓View学习

Android滚动刻度尺实现

2016-03-20  本文已影响3807人  LichFaker

缘起

最近在帮人做一个计步器,其中涉及到身高、体重等信息的采集;我参考了众多app的实现,觉得"乐动力"中滑动刻度的方式比较优雅。于是乎,反编译了该app,结果发现它是采用图片的方式实现的,即ScrollView内嵌了一张带刻度的图片。
个人觉得该方式太不灵活,且对美工的依赖较大,于是便想自定义一个刻度尺控件。

需求分析

  1. 绘制刻度,区分整值刻度和普通刻度

涉及的知识点

  1. View的机制

最终效果

由于简书上无法嵌入gif,为不影响效果,请移步github查看,如果觉得不错,帮忙给个star _
https://github.com/LichFaker/ScaleView

实现过程

  1. 新建一个class:HorizontalScaleScrollView, 继承自View
protected void init(AttributeSet attrs) {    
    // 获取自定义属性    
    TypedArray ta = getContext().obtainStyledAttributes(attrs, ATTR);   
    mMin = ta.getInteger(LF_SCALE_MIN, 0);    
    mMax = ta.getInteger(LF_SCALE_MAX, 200);    
    mScaleMargin = ta.getDimensionPixelOffset(LF_SCALE_MARGIN, 15);    
    mScaleHeight = ta.getDimensionPixelOffset(LF_SCALE_HEIGHT, 20);    
    ta.recycle();    
    mScroller = new Scroller(getContext());    
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int height=MeasureSpec.makeMeasureSpec(mRectHeight, MeasureSpec.AT_MOST);    
    super.onMeasure(widthMeasureSpec, height);        
    mScaleScrollViewRange = getMeasuredWidth();    
    mTempScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;    
    mMidCountScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
}
protected void onDrawScale(Canvas canvas, Paint paint) {    
    paint.setTextSize(mRectHeight / 4);
    for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
        if (i % 10 == 0) { 
            //整值
            canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleMaxHeight, paint); 
           //整值文字
            canvas.drawText(String.valueOf(k), i * mScaleMargin, mRectHeight - mScaleMaxHeight - 20, paint);
            k += 10;
        } else {
            canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight, paint); 
       }
    }
}
protected void onDrawPointer(Canvas canvas, Paint paint) {
    paint.setColor(Color.RED);
    //每一屏幕刻度的个数/2
    int countScale = mScaleScrollViewRange / mScaleMargin / 2;
    //根据滑动的距离,计算指针的位置【指针始终位于屏幕中间】
    int finalX = mScroller.getFinalX();
    //滑动的刻度
    int tmpCountScale = (int) Math.rint((double) finalX / (double) mScaleMargin);//四舍五入取整
    //总刻度
    mCountScale = tmpCountScale + countScale + mMin;
    if (mScrollListener != null) { //回调方法
        mScrollListener.onScaleScroll(mCountScale);
    }
    canvas.drawLine(countScale * mScaleMargin + finalX, mRectHeight,
            countScale * mScaleMargin + finalX, mRectHeight - mScaleMaxHeight - mScaleHeight, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (mScroller != null && !mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            mScrollLastX = x;
            return true;
        case MotionEvent.ACTION_MOVE:
            int dataX = mScrollLastX - x;
            if (mCountScale - mTempScale < 0) { //向右边滑动
                if (mCountScale <= mMin && dataX <= 0) //禁止继续向右滑动
                    return super.onTouchEvent(event);
            } else if (mCountScale - mTempScale > 0) { //向左边滑动
                if (mCountScale >= mMax && dataX >= 0) //禁止继续向左滑动
                    return super.onTouchEvent(event);
            }
            smoothScrollBy(dataX, 0);
            mScrollLastX = x;
            postInvalidate();
            mTempScale = mCountScale;
            return true;
        case MotionEvent.ACTION_UP:
            if (mCountScale < mMin) mCountScale = mMin;
            if (mCountScale > mMax) mCountScale = mMax;
            int finalX = (mCountScale - mMidCountScale) * mScaleMargin;
            mScroller.setFinalX(finalX); //纠正指针位置
            postInvalidate();
            return true;
    }
    return super.onTouchEvent(event);
}

最后的说明

以上只是针对水平滑动刻度的实现,垂直滑动原理一致,在源码中已经实现,其中也有许多不够完善的地方,如:

  1. 第一次快速滑动时,可以超出边界,之后则不会;
上一篇下一篇

猜你喜欢

热点阅读