垂直和水平滑動刻度尺

公司有個需求需要自定義的滑動設置升高體重的界面。首先感謝github上的這個開源讓我學習。https://github.com/LichFaker/ScaleView。因爲需求有點不一樣。需要自定義顏色。所以在它的基礎上改了畫線的位置和刻度的顏色。看一下界面。

上代碼。首先需要有個基礎的基類BaseScaleView。

public abstract class BaseScaleView extends View {

    public static final int[] ATTR = {
            R.attr.scale_view_min,
            R.attr.scale_view_max,
            R.attr.scale_view_margin,
            R.attr.scale_view_height,
    };

    public static final int SCALE_MIN = 0;
    public static final int SCALE_MAX = 1;
    public static final int SCALE_MARGIN = 2;
    public static final int SCALE_HEIGHT = 3;

	private static final String TAG = null;

    protected int mMax; //最大刻度
    protected int mMin; // 最小刻度
    protected int mCountScale; //滑動的總刻度

    protected int mScaleScrollViewRange;

    protected int mScaleMargin; //刻度間距
    protected int mScaleHeight; //刻度線的高度
    public int getmScaleMargin() {
		return mScaleMargin;
	}

	public void setmScaleMargin(int mScaleMargin) {
		this.mScaleMargin = mScaleMargin;
	}

	public int getmScaleHeight() {
		return mScaleHeight;
	}

	public void setmScaleHeight(int mScaleHeight) {
		this.mScaleHeight = mScaleHeight;
	}

	protected int mScaleMaxHeight; //整刻度線高度

    protected int mRectWidth; //總寬度
    protected int mRectHeight; //高度

    protected Scroller mScroller;
    protected int mScrollLastX;

    protected int mTempScale; // 用於判斷滑動方向
    protected int mMidCountScale; //中間刻度
    
    private int  width;
    

    protected OnScrollListener mScrollListener;

    public interface OnScrollListener {
        void onScaleScroll(int scale);
    }

    public BaseScaleView(Context context) {
        super(context);
        init(null);
    }

    public BaseScaleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public BaseScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

//    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
//    public BaseScaleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
//        super(context, attrs, defStyleAttr, defStyleRes);
//        init(attrs);
//    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    	// TODO Auto-generated method stub
    	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    	
    }

    protected void init(AttributeSet attrs) {
        // 獲取自定義屬性
        TypedArray ta = getContext().obtainStyledAttributes(attrs, ATTR);
        mMin = ta.getInteger(SCALE_MIN, 0);
        mMax = ta.getInteger(SCALE_MAX, 240);
        mScaleMargin = ta.getDimensionPixelOffset(SCALE_MARGIN, 15);
        mScaleHeight = ta.getDimensionPixelOffset(SCALE_HEIGHT, 20);
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        width = wm.getDefaultDisplay().getWidth();
        ta.recycle();
        mScroller = new Scroller(getContext());

        initVar();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 畫筆
        Paint paint = new Paint();
        paint.setColor(Color.GRAY);
        // 抗鋸齒
        paint.setAntiAlias(true);
        // 設定是否使用圖像抖動處理,會使繪製出來的圖片顏色更加平滑和飽滿,圖像更加清晰
        paint.setDither(true);
        // 空心
        paint.setStyle(Paint.Style.STROKE);
        // 文字居中
        paint.setTextAlign(Paint.Align.CENTER);

        onDrawLine(canvas, paint);
        onDrawScale(canvas, paint); //畫刻度
        onDrawPointer(canvas, paint); //畫指針
        
        
        
        
        super.onDraw(canvas);
    }

    

	protected abstract void initVar();

    // 畫線
    protected abstract void onDrawLine(Canvas canvas, Paint paint);

    // 畫刻度
    protected abstract void onDrawScale(Canvas canvas, Paint paint);

    // 畫指針
    protected abstract void onDrawPointer(Canvas canvas, Paint paint);
    
    
    

    /**
     * 使用Scroller時需重寫
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        // 判斷Scroller是否執行完畢
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            // 通過重繪來不斷調用computeScroll
            invalidate();
        }
    }

    public void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
    }

    public void smoothScrollTo(int fx, int fy) {
        int dx = fx - mScroller.getFinalX();
        int dy = fy - mScroller.getFinalY();
        smoothScrollBy(dx, dy);
    }

    /**
     * 設置回調監聽
     *
     * @param listener
     */
    public void setOnScrollListener(OnScrollListener listener) {
        this.mScrollListener = listener;
    }
    
    public void setmMidCountScale(int mMidCountScale) {
		this.mMidCountScale = mMidCountScale;
	}
    
    public int getmMidCountScale() {
		return mMidCountScale;
	}

	public int getmScaleScrollViewRange() {
		return mScaleScrollViewRange;
	}

	
    
    
    
    
}
其中垂直刻度尺和水平刻度尺都繼承於它。首先看一下垂直刻度尺的類。
public class VerticalScaleScrollView extends BaseScaleView {

    private static final String TAG = "VerticalScaleScrollView";
    public static int defaultY = 0;


	public VerticalScaleScrollView(Context context) {
        super(context);
    }

    public VerticalScaleScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerticalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //@TargetApi(Build.VERSION_CODES.LOLLIPOP)
//    public VerticalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
//        super(context, attrs, defStyleAttr, defStyleRes);
//    }

    @Override
    protected void initVar() {
        mRectHeight = (mMax - mMin) * mScaleMargin;
        
        Log.e(TAG, "mRectHeight"+mRectHeight+"mMax"+mMax+"mMin"+mMin+":::"+mScaleScrollViewRange);
        mRectWidth = mScaleHeight * 8;
        mScaleMaxHeight = mScaleHeight * 2;

        // 設置layoutParams
        ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(mRectWidth, mRectHeight);
        this.setLayoutParams(lp);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        int width = MeasureSpec.makeMeasureSpec(mRectWidth, MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mScaleScrollViewRange = getMeasuredHeight();
        mTempScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
        mMidCountScale = mScaleScrollViewRange / mScaleMargin / 2 + mMin;
    }

    @Override
    protected void onDrawLine(Canvas canvas, Paint paint) {
        //canvas.drawLine(0, 0, 0, mRectHeight, paint);
    }

    @Override
    protected void onDrawScale(Canvas canvas, Paint paint) {
        paint.setTextSize(mRectWidth / 4);
        Paint linePaint = new Paint();
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        int width = wm.getDefaultDisplay().getWidth();
        int height = wm.getDefaultDisplay().getHeight();
        for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
            if (i % 10 == 0) { //整值,第三個參數可以修改線的長短

                linePaint.setTextSize(mRectWidth / 6);
                linePaint.setColor(Color.GREEN);
                linePaint.setStrokeWidth(10);
                //畫字的筆
                Paint textPaint = new Paint();
                textPaint.setTextSize(mRectWidth /5);
                textPaint.setColor(Color.BLACK);
                textPaint.setAntiAlias(true);
                textPaint.setStrokeWidth(7);

                canvas.drawLine(width-130, i * mScaleMargin, width, i * mScaleMargin, linePaint);
                //整值文字
                canvas.drawText(String.valueOf(k), width-150, i * mScaleMargin + paint.getTextSize()+paint.getTextSize()/3, textPaint);
                canvas.drawText("cm", width-100, i * mScaleMargin + paint.getTextSize()+paint.getTextSize()/3, textPaint);
                k += 10;
            } else if(i%5==0){
                linePaint.setColor(Color.GRAY);
                linePaint.setTextSize(mRectWidth/4);
                linePaint.setStrokeWidth(5);
                canvas.drawLine(width-50, i *mScaleMargin, width, i * mScaleMargin, linePaint);
            }

            else {
                linePaint.setColor(Color.GRAY);
                linePaint.setTextSize(mRectWidth/4);
                linePaint.setStrokeWidth(5);
                canvas.drawLine(width-20, i * mScaleMargin, width, i * mScaleMargin, linePaint);
            }
        }
    }
    private boolean flag = true;
    @Override
    protected void onDrawPointer(Canvas canvas, Paint paint) {

        paint.setColor(Color.RED);
        paint.setStrokeWidth(7);
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        int width = wm.getDefaultDisplay().getWidth();

        //每一屏幕刻度的個數/2
        int countScale = mScaleScrollViewRange / mScaleMargin / 2;
        
        
        //根據滑動的距離,計算指針的位置【指針始終位於屏幕中間】
        int finalY = mScroller.getFinalY();
        int tmpCountScale = 0;
			
		
        
        //滑動的刻度
        tmpCountScale = (int) Math.rint((double) finalY / (double) mScaleMargin); //四捨五入取整
        //總刻度  countScale
        mCountScale = tmpCountScale + countScale + mMin;
        if (mScrollListener != null) { //回調方法
            mScrollListener.onScaleScroll(mCountScale);
        }
			canvas.drawLine(width-150, countScale * mScaleMargin + finalY,
                //mScaleMaxHeight + mScaleHeight,
                width,
                countScale * mScaleMargin + finalY, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mScroller != null && !mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mScrollLastX = y;
                return true;
            case MotionEvent.ACTION_MOVE:
                int dataY = mScrollLastX - y;
                if (mCountScale - mTempScale < 0) { //向下邊滑動
                    if (mCountScale <= mMin && dataY <= 0) //禁止繼續向下滑動
                        return super.onTouchEvent(event);
                } else if (mCountScale - mTempScale > 0) { //向上邊滑動
                    if (mCountScale >= mMax && dataY >= 0) //禁止繼續向上滑動
                        return super.onTouchEvent(event);
                }
                smoothScrollBy(0, dataY);
                mScrollLastX = y;
                postInvalidate();
                mTempScale = mCountScale;
                return true;
            case MotionEvent.ACTION_UP:
                if (mCountScale < mMin) mCountScale = mMin;
                if (mCountScale > mMax) mCountScale = mMax;
                int finalY = (mCountScale - mMidCountScale) * mScaleMargin;
                mScroller.setFinalY(finalY); //糾正指針位置
                postInvalidate();
                return true;
        }
        return super.onTouchEvent(event);
    }
    
    
}
其中onDrawScale()方法是畫刻度和數字的方法。而onDrawPointer()是畫中間紅色指標的方法。

接下來是水平的刻度尺。

public class HorizontalScaleScrollView extends BaseScaleView {

    public HorizontalScaleScrollView(Context context) {
        super(context);
    }

    public HorizontalScaleScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HorizontalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

//    public HorizontalScaleScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
//        super(context, attrs, defStyleAttr, defStyleRes);
//    }

    @Override
    protected void initVar() {
        mRectWidth = (mMax - mMin) * mScaleMargin;
        mRectHeight = mScaleHeight * 8;
        mScaleMaxHeight = mScaleHeight * 2;

        // 設置layoutParams
        ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(mRectWidth, mRectHeight);
        this.setLayoutParams(lp);
    }

    @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;
    }

    @Override
    protected void onDrawLine(Canvas canvas, Paint paint) {
        //canvas.drawLine(0, mRectHeight, mRectWidth, mRectHeight, paint);
    }

    @Override
    protected void onDrawScale(Canvas canvas, Paint paint) {

        paint.setTextSize(mRectHeight / 4);
        paint.setStrokeWidth(5);
        paint.setColor(Color.GRAY);
        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        int height = wm.getDefaultDisplay().getHeight();


        Paint linePaint = new Paint();
        for (int i = 0, k = mMin; i <= mMax - mMin; i++) {
            if (i % 10 == 0) { //整值

                linePaint.setTextSize(mRectHeight / 6);
                linePaint.setColor(Color.GREEN);
                linePaint.setStrokeWidth(10);

                Paint textPaint = new Paint();
                textPaint.setTextSize(30);
                textPaint.setAntiAlias(true);
                textPaint.setColor(Color.BLACK);

                canvas.drawLine(i * mScaleMargin, height/2, i * mScaleMargin, mRectHeight - mScaleMaxHeight-20, linePaint);
                //整值文字
                canvas.drawText(String.valueOf(k), i * mScaleMargin-textPaint.getTextSize()+linePaint.getStrokeWidth(), mRectHeight - mScaleMaxHeight - 20, textPaint);
                k += 10;
            }else if(i%5==0){
                canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight-10, paint);
            }
            else {
                canvas.drawLine(i * mScaleMargin, mRectHeight, i * mScaleMargin, mRectHeight - mScaleHeight, paint);
            }
        }

    }

    @Override
    protected void onDrawPointer(Canvas canvas, Paint paint) {

        paint.setColor(Color.RED);

        //每一屏幕刻度的個數/2
        int countScale = mScaleScrollViewRange / mScaleMargin / 2;
        
        Log.e("TAG", "屏幕刻度的個數"+countScale);
        //根據滑動的距離,計算指針的位置【指針始終位於屏幕中間】
        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-30, 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);
    }

}
其實垂直和水平的刻度尺的寫法都大同小異。只是把刻度和文字的畫的位置不同而已。線的粗細長短顏色都可以根據需求自己修改。
對了,忘了加上刻度尺的幾個自定義屬性。在values文件夾中加上scale_attrs.xml屬性文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
        <attr name="scale_view_max" format="integer" />
        <attr name="scale_view_min" format="integer" />
        <attr name="scale_view_height" format="dimension" />
        <attr name="scale_view_margin" format="dimension" />
</resources>

目前支持的自定義屬性:

  • scale_view_max 最大值
  • scale_view_min 最小值
  • scale_view_height 刻度的高度
  • scale_view_margin 刻度的間距
  • layout_width 可動態調整

因爲無法設置默認選中刻度。這點有點不好。公司需求是需要默認選中170釐米和60公斤。後來通過各種方法把默認選中的問題解決了。這個後面說。下面是在佈局中的使用,添加使用下面代碼。
<com.hyx.fitbit.view.VerticalScaleScrollView
        android:id="@+id/verticalScale"
        android:layout_marginTop="60dp"
        app:scale_view_max="240"
        app:scale_view_min="140"
        app:scale_view_height="15dip"
        app:scale_view_margin="15dip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        />
下面是在代碼中使用垂直刻度尺
VerticalScaleScrollView scaleScrollView = (VerticalScaleScrollView) findViewById(R.id.verticalScale);
下面是滑動時間刻度的監聽


scaleScrollView.setOnScrollListener(new HorizontalScaleScrollView.OnScrollListener() {
            @Override
            public void onScaleScroll(int scale) {
            	height_text.setText(String.valueOf(scale));
            	//float y = scaleScrollView.getScaleY();
            }
        });

水平刻度尺跟垂直刻度尺的用法也是一樣的。就不一一貼代碼了。

接下來說說默認選中的問題,就比如要選中170釐米來說,一開始我是去算最小值到170的每一個刻度指尖的距離。然後還要得到這個控件的高度。就是這個高度問題困擾了我很久。

直接用scaleScrollView.getHeight();scaleScrollView.getMeasuredHeight();無法得到控件的高度。試過好多方法還是不行。後來查到了是控件還沒初始化完全無法得到高度。後來查到了一個方法,是控件畫完之後的回調接口。在該接口裏面獲取了控件的高度。

ViewTreeObserver vto = scaleScrollView.getViewTreeObserver();   
		vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
		    @Override   
		    public void onGlobalLayout() { 
		    	scaleScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
		    	viewHeight = scaleScrollView.getHeight();
		    	countScale = viewHeight/ getmScaleMargin / 2;
		    	countScale = 30-countScale;
		    	scaleScrollView.smoothScrollTo(0, countScale*getmScaleMargin);
		    	Log.e(TAG, "高:"+viewHeight);
		    	scaleScrollView.getWidth();
		    }   
		});   
這樣設置了的話。刻度尺就會默認滑動到170。其中的30是最小值140到默認選中170間的值。然後水平公斤刻度尺的那個 也是同樣的做法讓它默認選中60公斤。代碼如下
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 
		    @Override   
		    public void onGlobalLayout() { 
		    	scaleScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
		    	width = scaleScrollView.getWidth();
		    	countScale = width/getmScaleMargin/2;
		    	countScale = 30 - countScale;
		    	scaleScrollView.smoothScrollTo(countScale*getmScaleMargin, 0);
		    }   
		});
其實原理一樣。只不過獲取高度變成了獲取寬度。

就寫到這裏了,因爲工作有點忙,代碼也沒好好整理。請多多指出不對的地方,共勉。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章