Android仿京東金融的數(shù)值滾動(dòng)尺功能
現(xiàn)在就講講自定義數(shù)值滾動(dòng)尺,這個(gè)用的還是挺多的,例如京東金融的通過滾動(dòng)尺選擇金額等,而這次就是高仿京東金融的數(shù)值滾動(dòng)尺。首先看看下效果圖,如下:

首先先給你們各個(gè)變量的含義,以免在后面的講解中不知變量的意思,代碼如下:
//最小值private int minValue;//最大值private int maxValue;//當(dāng)前值private int currentValue;//最小單位值private int minUnitValue;//最小當(dāng)前值private int minCurrentValue;//字體大小private int textSize;//字體顏色private int textColor;//線顏色private int dividerColor;//指示線顏色private int indicatrixColor;//畫線的畫筆private Paint linePaint;//控價(jià)的寬度private int slideRulerWidth=0;//滑動(dòng)的寬度private int rollingWidth;//屏幕的寬private int wrapcontentWidth;//屏幕的高private int wrapcontentHeight;//一屏顯示Itemprivate int showItemSize;//刻度和數(shù)值的間距private int marginCursorData;//長刻度的大小private int longCursor;//短刻度的大小private int shortCursor;//計(jì)算每個(gè)刻度的間距private int marginWidth=0;//數(shù)據(jù)回調(diào)接口private SlideRulerDataInterface slideRulerDataInterface;//正在滑動(dòng)狀態(tài)private int isScrollingState=1;//快速一滑private int fastScrollState=2;//結(jié)束滑動(dòng)private int finishScrollState=3;private GestureDetector mDetector;private Display display =null;private Scroller scroller;public SlideRuler(Context context, AttributeSet attrs, int defStyleAttr) {super(context,attrs,defStyleAttr);display=((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();//屏幕寬高wrapcontentWidth=display.getWidth();wrapcontentHeight=display.getHeight();//初始化自定義的參數(shù)TypedArray typedArray=context.getTheme().obtainStyledAttributes(attrs,R.styleable.slideruler,defStyleAttr,0);textSize = typedArray.getDimensionPixelSize(R.styleable.slideruler_textSize,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,15,getResources().getDisplayMetrics()));textColor=typedArray.getColor(R.styleable.slideruler_textColor,Color.DKGRAY);dividerColor=typedArray.getColor(R.styleable.slideruler_dividerColor,Color.BLACK);indicatrixColor=typedArray.getColor(R.styleable.slideruler_indicatrixColor,Color.BLACK);minValue=typedArray.getInteger(R.styleable.slideruler_min_value,0);maxValue=typedArray.getInteger(R.styleable.slideruler_max_value,199000);currentValue=typedArray.getInteger(R.styleable.slideruler_current_value,10000);minUnitValue=typedArray.getInteger(R.styleable.slideruler_min_unitValue,1000);minCurrentValue=typedArray.getInteger(R.styleable.slideruler_min_currentValue,1000);showItemSize=typedArray.getInteger(R.styleable.slideruler_show_itemSize,30);marginCursorData=typedArray.getDimensionPixelSize(R.styleable.slideruler_margin_cursor_data,(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,10,getResources().getDisplayMetrics()));longCursor=typedArray.getDimensionPixelSize(R.styleable.slideruler_longCursor,(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,25,getResources().getDisplayMetrics()));shortCursor=typedArray.getDimensionPixelSize(R.styleable.slideruler_shortCursor,(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,15,getResources().getDisplayMetrics()));scroller=new Scroller(context);mDetector=new GestureDetector(context,myGestureListener);//初始化PaintlinePaint=new Paint();linePaint.setAntiAlias(true);linePaint.setTextAlign(Paint.Align.CENTER);linePaint.setStyle(Paint.Style.STROKE);linePaint.setTextSize(textSize);//檢查當(dāng)前值是不是正確值checkCurrentValue();}
其次自定義View也好自定義控價(jià)也好
protected void onMeasure(int widthMeasureSpec, int heigh)也是蠻重要的所以照例也講講,用來確定控件的大小,代碼如下:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthModel=MeasureSpec.getMode(widthMeasureSpec);int heightModel=MeasureSpec.getMode(heightMeasureSpec);int widthSize=MeasureSpec.getSize(widthMeasureSpec);int heightSize=MeasureSpec.getSize(heightMeasureSpec);int width;int height;if(widthModel==MeasureSpec.EXACTLY){width=widthSize;}else{width=wrapcontentWidth;}if(heightModel==MeasureSpec.EXACTLY){height=heightSize;}else{height=(getPaddingBottom()+getPaddingTop()+(wrapcontentHeight/4));}setMeasuredDimension(width,height);}
代碼的意思也很簡單,當(dāng)MeasureSpec里的specMode類型是EXACTLY時(shí),即設(shè)置了明確的值或者是MATCH_PARENT時(shí),就直接把MeasureSpec.getSize()的值賦進(jìn)去,如果不是即為WARP_CONTENT時(shí),就直接賦給屏幕的寬高??丶膶捀叨际峭粯拥淖龇?。
當(dāng)控件大小確定之后,我們再利用
protected void onSizeChanged(int w, int h, int oldw, int oldh)進(jìn)行一些變量的賦值,代碼如下:
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {//計(jì)算每個(gè)刻度的間距marginWidth=getWidth()/showItemSize;//開始時(shí)的距離rollingWidth=(int)(marginWidth*cursorNum());//整個(gè)控件的寬度slideRulerWidth=(maxValue/minUnitValue)*marginWidth;super.onSizeChanged(w, h, oldw, oldh);}
到此我們就可以在onDraw(Canvas canvas)方法里畫出初始的界面,而以后的動(dòng)態(tài)只是通過不斷的改變數(shù)值再進(jìn)行繪畫而已,代碼如下:
@Overrideprotected void onDraw(Canvas canvas){//畫最基礎(chǔ)的兩條線drawBaseView(canvas);//畫初始的界面drawBaseLine(canvas);}//畫最基礎(chǔ)的兩條線public void drawBaseLine(Canvas canvas){//畫中間的線linePaint.setColor(indicatrixColor);canvas.drawLine(getWidth()/2,0,getWidth()/2,getHeight(),linePaint);//畫底部的直線linePaint.setColor(dividerColor);canvas.drawLine(0,getHeight(),slideRulerWidth,getHeight(),linePaint);}//畫初始的界面public void drawBaseView(Canvas canvas){//整數(shù)刻度的個(gè)數(shù)int integerWidth= (int)Math.rint((currentValue-minValue)/minUnitValue);//剩余不整一個(gè)刻度的數(shù)值int residueWidth=(currentValue-minValue)%minUnitValue;//開始畫圖的X軸位置int startCursor=(getWidth()/2)-(marginWidth*integerWidth)-(int)(marginWidth*(float)residueWidth/minUnitValue);for(int i=0;i<(maxValue/minUnitValue)+1;i++){float xValue=startCursor+(marginWidth*i);if(i%10==0){//畫長刻度linePaint.setColor(textColor);canvas.drawText((minCurrentValue*i)+"",xValue,getHeight()-longCursor-marginCursorData,linePaint);linePaint.setColor(dividerColor);canvas.drawLine(xValue,getHeight(),xValue,getHeight()-longCursor,linePaint);}else{//畫短刻度canvas.drawLine(xValue,getHeight(),xValue,getHeight()-shortCursor,linePaint);}}}
在drawBaseView()方法里,也很簡單,就是在二分之一寬度,畫一條直線,然后在控價(jià)的底部畫出寬度為整個(gè)控件的寬度的底線。接著在
drawBaseView(Canvas canvas)方法里
首先用當(dāng)前值(currentValue)-最小值(minValue)之后再除于最小單位值(minUnitValue)以獲取整數(shù)刻度的個(gè)數(shù)
因?yàn)橛杏鄶?shù)的情況,我們再當(dāng)前值(currentValue)-最小值(minValue)之后求余與最小單位值(minUnitValue)以獲取余數(shù)
接著我們要獲取我們畫圖的X軸開始的位置,因?yàn)樽钚≈抵荒芑街虚g,所以開始的位置為控件一半的寬度(getWidth()/2)
減去計(jì)算每個(gè)刻度的間距(marginWidth)乘以整數(shù)刻度的個(gè)數(shù)(integerWidth)即marginWidth*integerWidth再減去余數(shù)對應(yīng)所產(chǎn)生的X軸距離即 :
(int)(marginWidth*(float)residueWidth/minUnitValue)4、再通過For循環(huán)刻度的個(gè)數(shù),不同的進(jìn)行刻度的繪畫,當(dāng)i%10==0時(shí)即為一個(gè)大的單位刻度否者為一個(gè)小的單位刻度,具體代碼我上面已有注釋,原理和畫中間線一直就不在贅述。
到此我們就已經(jīng)把自定義控價(jià)靜態(tài)的部分寫完了,效果如下:

接著我們用GestureDetector綁定手勢事件,根據(jù)回調(diào)手勢事件的方法來改變數(shù)據(jù)和刷新頁面,在GestureDetector里,我們只會(huì)回調(diào):
//手指在觸摸屏上滑動(dòng)public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
//手指在觸摸屏上迅速移動(dòng),并松開的動(dòng)作public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
這兩個(gè)方法就可以了。
具體代碼如下:
private GestureDetector.SimpleOnGestureListener myGestureListener =new GestureDetector.SimpleOnGestureListener(){@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {//滑動(dòng)刷新UIupdateView(rollingWidth+(int)distanceX,isScrollingState);return true;}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {//快速滑動(dòng)的動(dòng)畫scroller.fling(rollingWidth,0,(int)(-velocityX/1.5),0,0,(maxValue/minUnitValue)*marginWidth,0,0);return true;}};//動(dòng)態(tài)更新滑動(dòng)Viewpublic void updateView(int srcollWidth,int action){if(action==isScrollingState){//正在滑動(dòng)狀態(tài)(onScroll())rollingWidth=srcollWidth;float itemNum=(float)srcollWidth/marginWidth;currentValue=(int)(minUnitValue*itemNum);}else if(action==fastScrollState){//快速一滑(onFling())rollingWidth=srcollWidth;int itemNum=(int)Math.rint((float)rollingWidth/marginWidth);currentValue=(minUnitValue*itemNum);}else if(action==finishScrollState){//結(jié)束滑動(dòng)(ACTION_UP)int itemNum=(int)Math.rint((float)rollingWidth/marginWidth);currentValue=minUnitValue*itemNum;}//判斷是否在最小選擇值if(currentValue<=minCurrentValue){rollingWidth=(minCurrentValue/minUnitValue)*marginWidth;currentValue=minCurrentValue;}//判斷是否在最大值if(currentValue>=maxValue){rollingWidth=marginWidth*allCursorNum();currentValue=maxValue;}//回調(diào)數(shù)值if(slideRulerDataInterface!=null){slideRulerDataInterface.getText(currentValue+"");}invalidate();}
1.當(dāng)我們滑動(dòng)我們的控件是,就會(huì)回調(diào)GestureDetector里的onScroll()方法,然后rollingWidth+(int)distanceX即當(dāng)前滑動(dòng)的寬度(rollingWidth)加上滑動(dòng)產(chǎn)生的寬度(distanceX)為動(dòng)態(tài)產(chǎn)生的寬度,再除于計(jì)算每個(gè)刻度的間距(marginWidth)從而得到刻度的數(shù)量,有了刻度的數(shù)量即可得到當(dāng)前值
currentValue=(int)(minUnitValue*itemNum);有了當(dāng)前值調(diào)用invalidate();刷新onDraw()即可完成連續(xù)滑動(dòng)時(shí)動(dòng)態(tài)繪制。
2.當(dāng)我們快速一劃時(shí),就會(huì)回調(diào)GestureDetector里的onFling()方法,在方法里用
scroller.fling(rollingWidth,0,(int)(-velocityX/1.5),0,0,(maxValue/minUnitValue)*marginWidth,0,0);以實(shí)現(xiàn)滑動(dòng)有一個(gè)好的動(dòng)畫效果,此時(shí)在如下代碼里:
@Overridepublic void computeScroll() {if(scroller.computeScrollOffset()){//快滑刷新UIupdateView(scroller.getCurrX(),fastScrollState);}}
scroller.computeScrollOffset()==true;而scroller.getCurrX()就相當(dāng)于為動(dòng)態(tài)產(chǎn)生的滑動(dòng)寬度剩下的也是調(diào)用updateView()方法不斷的刷新,當(dāng)
scroller.computeScrollOffset()==false就滑動(dòng)動(dòng)畫結(jié)束了。
3.最后當(dāng)我們滑動(dòng)結(jié)束手指抬起時(shí):
@Overridepublic boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_UP:updateView(0,finishScrollState);default:mDetector.onTouchEvent(event);break;}return true;}
我們也要掉updateView(),以保持滑動(dòng)的最后結(jié)構(gòu)都指在指針上。
源碼地址:
https://github.com/gaojuanjuan/MaterialDesign_V7
到這里就結(jié)束啦
