The implementation of Android wheel scale

Source: Internet
Author: User

The effect of meeting a requirement is as follows:


A tape measure that selects different tick values by sliding left and right. This aspect of things have not been done before, in order to present your ability to think of several ideas are dead on the half way. For example, how to do the tick marks above, how to do when the slide, and how to do the following numbers, it looks like the effect of the circle how to do. Time is pressing, just for two nights. No good ideas to refer to other people's first, it is also clever, two days ago just saw a date selection control, as well as a previous look of an imitation iphone scrolling control, the effect is similar:


I want to find the author arrogant Colonel Exchange, but time is relatively tight, the source is not very good meaning. The following are some of the things that may be involved in a general glance:

1. Background: This is achieved with shape. Previous research, but also used, but has not achieved the required effect;

2, scale and numbers: This should not be confused, direct draw. Relatively simple, is to draw straight lines and numbers;

3, scrolling: When scrolling, the effect of a scrolling is continuously redrawn. , but not sure what kind of effect is achieved;

4. Fast scrolling: Scroller and Velocitytracker may be something that needs to be used. Almost completely did not, Sao years, study it (requirements, this priority can be the lowest);

5, demand: The units of the scale can be changed, such as 10 units a unit, or two units of a unit, or can be arbitrary (this early thinking did not think well, the realization of the difficulties, and finally only two kinds).

In fact, to this step is basically can be achieved, see a final effect first:



Here's a step-by-step. There's one more place to say before this is the interface of the control: provides a way to implement control initialization and receive control selection values: Display unit, maximum, minimum, current value, callback interface. With this, start with the hardest. First, the scale and number are implemented and can be slid. This place is very important, everyone has the idea of each person, and the idea of good or bad directly affect the face of different units after the realization. The current idea is to draw tick marks from the middle of the control to the mvalue of the current displayed value, while sliding while changing the displayed values Mvalue, the rounding of the minimum scale is insufficient:

@Overrideprotected void OnDraw (canvas canvas) {super.ondraw (canvas);d rawscaleline (canvas);//Drawwheel (canvas); Drawmiddleline (canvas);} private void Drawwheel (canvas canvas) {drawable wheel = getresources (). getdrawable (R.drawable.bg_wheel); Wheel.setbounds (0, 0, getwidth (), getheight ()); Wheel.draw (canvas);} /** * Start drawing the tick marks from the middle to the sides * * @param canvas */private void drawscaleline (canvas canvas) {canvas.save (); Paint Linepaint = new paint (); Linepaint.setstrokewidth (2); Linepaint.setcolor (Color.Black); Textpaint textpaint = new Textpaint (paint.anti_alias_flag); Textpaint.settextsize (text_size * mDensity); int width = Mwidth, Drawcount = 0;float xposition = 0, textWidth = Layout.getdesiredwidth ("0", Textpaint); for (int i = 0; Drawcount &l t;= 4 * WIDTH; i++) {int numsize = string.valueof (Mvalue + i). Length (); xposition = (WIDTH/2-mmove) + i * Mlinedivider * MDENSITY;IF (  XPosition + getpaddingright () < Mwidth) {if ((mvalue + i)% Mmodtype = = 0) {canvas.drawline (xposition, Getpaddingtop (), XPosition, Mdensity * item_max_height, Linepaint), if (Mvalue + i <= mmaxvalue) {switch (mmodtype) {case MOD_TYPE_HALF:canvas.draw Text (string.valueof ((Mvalue + i)/2), Countleftstart (Mvalue + i, xposition, textWidth), GetHeight ()-TextWidth, Textpain  t); Break;case MOD_TYPE_ONE:canvas.drawText (string.valueof (Mvalue + i), XPosition-(TextWidth * numsize/2), getheight () -TextWidth, textpaint); break;default:break;}}} else {canvas.drawline (xposition, Getpaddingtop (), XPosition, mdensity * item_min_height, Linepaint);}} XPosition = (WIDTH/2-mmove)-I * mlinedivider * mdensity;if (XPosition > Getpaddingleft ()) {if ((mvalue-i)% MMo DType = = 0) {canvas.drawline (xposition, Getpaddingtop (), XPosition, mdensity * item_max_height, Linepaint); if (mvalue-i  >= 0) {switch (mmodtype) {case MOD_TYPE_HALF:canvas.drawText (string.valueof ((mvalue-i)/2), Countleftstart (Mvalue- I, XPosition, textWidth), GetHeight ()-TextWidth, textpaint); Break;case MOD_TYPE_ONE:canvas.drawText (String.valueof ( MValue-i), XPosition-(TextWidth * numsize/2), getheight ()-TextWidth, textpaint); break;default:break;}}} else {canvas.drawline (xposition, Getpaddingtop (), XPosition, mdensity * item_min_height, Linepaint);}} Drawcount + = 2 * Mlinedivider * mdensity;} Canvas.restore ();}
Then there is the acceleration of the sliding problem, here are two classes Scroller and Velocitytracker, about these two classes will have a chance to detail, here simply mention: Velocitytracker is the role of the user to speed up the slide when the calculation of how far, After you get this, the calculation of the sliding process is performed by Scroller, and finally the real "move"--redraws according to the value of Mvalue:

@Overridepublic boolean ontouchevent (Motionevent event) {int action = event.getaction (); int xposition = (int) Event.getx ( ); if (Mvelocitytracker = = null) {Mvelocitytracker = Velocitytracker.obtain ();} Mvelocitytracker.addmovement (event); switch (action) {case MotionEvent.ACTION_DOWN:mScroller.forceFinished (true); MLASTX = Xposition;mmove = 0;break;case MotionEvent.ACTION_MOVE:mMove + = (mlastx-xposition); Changemoveandvalue (); Break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:countMoveEnd (); Countvelocitytracker (event); return false;//Break;default:break;} Mlastx = Xposition;return true;} private void Countvelocitytracker (Motionevent event) {mvelocitytracker.computecurrentvelocity (+); float xvelocity = Mvelocitytracker.getxvelocity (); if (Math.Abs (xvelocity) > Mminvelocity) {mscroller.fling (0, 0, (int) xvelocity, 0, Integer.min_value, Integer.max_value, 0, 0);}} private void Changemoveandvalue () {int tValue = (int) (Mmove/(Mlinedivider * mdensity)); if (Math.Abs (TValue) >0) {Mvalue + = tvalue;mmove-= tValue * Mlinedivider * mdensity;if (mvalue <= 0 | | mvalue > Mmaxvalue) {mvalue = Mval UE <= 0? 0:mmaxvalue;mmove = 0;mscroller.forcefinished (true);} Notifyvaluechange ();} Postinvalidate ();} private void Countmoveend () {int roundmove = Math.Round (Mmove/(Mlinedivider * mdensity)); mvalue = Mvalue + roundmove;mva Lue = Mvalue <= 0? 0:mvalue;mvalue = mvalue > Mmaxvalue? MMAXVALUE:MVALUE;MLASTX = 0;mmove = 0;notifyvaluechange ();p ostinvalidate ();} private void Notifyvaluechange () {if (null! = Mlistener) {if (Mmodtype = = Mod_type_one) {mlistener.onvaluechange (mvalue); }if (Mmodtype = = mod_type_half) {mlistener.onvaluechange (mvalue/2f);}}} @Overridepublic void Computescroll () {super.computescroll (); if (Mscroller.computescrolloffset ()) {if ( Mscroller.getcurrx () = = Mscroller.getfinalx ()) {//Overcountmoveend ();} else {int xposition = Mscroller.getcurrx (); Mmove + = (mlastx-xposition); Changemoveandvalue (); mlastx = XPosition;}}}
The last is the realization of the circle background. This is done with shape, can be done using setbackgrounddrawable (), or it can be drawn directly in draw with the same effect. Other details, such as the sliding time line over the boundary, the sliding distance when the display is incomplete and so on, this can only be found. Here is the code for the shape background:

<?xml version= "1.0" encoding= "Utf-8"? ><shape xmlns:android= "Http://schemas.android.com/apk/res/android"    android:shape= "Rectangle" >    <!--both set color and    <gradient        android:angle= "0"        Android:centercolor= "#66FFFFFF"        android:endcolor= "#66AAAAAA"        android:startcolor= "#66AAAAAA"/>    <corners android:radius= "6DP"/>    <stroke        android:width= "6DP"        android:color= "#FF666666"/ ></shape>
This can be written in code:

Private Gradientdrawable Createbackground () {Float strokewidth = 4 * mdensity;//Border width Float Roundradius = 6 * mdensity;// Fillet radius int strokecolor = Color.parsecolor ("#FF666666")///Border color//int fillcolor = Color.parsecolor ("#DFDFE0");// Internal Fill Color setpadding ((int) strokewidth, (int) strokewidth, (int) strokewidth, 0); int colors[] = {0xff999999, 0xFFFFFFFF, 0xff999999};//are start color, middle night, end color gradientdrawable bgdrawable = new Gradientdrawable ( GradientDrawable.Orientation.LEFT_RIGHT, colors);//Create drawable//Bgdrawable.setcolor (FillColor); Bgdrawable.setcornerradius (Roundradius); Bgdrawable.setstroke ((int) strokewidth, strokecolor);// Setbackgrounddrawable (GD); return bgdrawable;}
Finally, we'll post the complete code:

Package Com.ttdevs.wheel.widget;import Android.annotation.suppresslint;import Android.content.context;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.graphics.drawable.drawable;import Android.graphics.drawable.gradientdrawable;import android.text.Layout; Import Android.text.textpaint;import Android.util.attributeset;import Android.view.motionevent;import Android.view.velocitytracker;import Android.view.view;import Android.view.viewconfiguration;import Android.widget.scroller;import com.ttdevs.wheel.r;/** * Tape Control class. Because of the time is tight, only after work has the time, therefore only realizes the basic function. <br> * Details including the problem of the scale display on the edge of the widget during the sliding process <br> * * Time will continue to update on weekends <br> * * @author Ttdevs * @version create:2014 year August 26th */@SuppressLint ("clickableviewaccessibility") public class Tunewheel extends View {public interface Onvaluechangelistener {public void Onvaluechange (float value); public static final int mod_type_half = 2;public static final int mod_type_one = 10;private static final intItem_half_divider = 40;private static final int item_one_divider = 10;private static final int item_max_height = 50;privat e static final int item_min_height = 20;private static final int text_size = 18;private float mdensity;private int Mvalue = Mmaxvalue = +, Mmodtype = mod_type_half, Mlinedivider = item_half_divider;//private int mvalue =, Mmaxvalue = Mmodtype = mod_type_one,//Mlinedivider = item_one_divider;private int mlastx, mmove;private int mWidth, MHeight;priv ate int mminvelocity;private Scroller mscroller;private velocitytracker mvelocitytracker;private Onvaluechangelistener Mlistener; @SuppressWarnings ("deprecation") public tunewheel (context context, AttributeSet Attrs) {Super (context, attrs); mscroller = new Scroller (GetContext ()); mdensity = GetContext (). Getresources (). Getdisplaymetrics (). density;mminvelocity = Viewconfiguration.get (GetContext ()). Getscaledminimumflingvelocity (); /Setbackgroundresource (R.drawable.bg_wheel); Setbackgrounddrawable (Createbackground ());}  Private Gradientdrawable Createbackground () {Float strokewidth = 4 * mdensity;//Border width Float Roundradius = 6 * mdensity;// Fillet radius int strokecolor = Color.parsecolor ("#FF666666")///Border color//int fillcolor = Color.parsecolor ("#DFDFE0");// Internal Fill Color setpadding ((int) strokewidth, (int) strokewidth, (int) strokewidth, 0); int colors[] = {0xff999999, 0xFFFFFFFF, 0xff999999};//are start color, middle night, end color gradientdrawable bgdrawable = new Gradientdrawable ( GradientDrawable.Orientation.LEFT_RIGHT, colors);//Create drawable//Bgdrawable.setcolor (FillColor); Bgdrawable.setcornerradius (Roundradius); Bgdrawable.setstroke ((int) strokewidth, strokecolor);// Setbackgrounddrawable (GD); return bgdrawable;} /** * * Considerations are extensible, but time is tight, only two types can be supported in * * * @param value * Initial value * @param maxValue * Maximum value * @param model * Dial Accuracy:<br> * {@link mod_type_half}<br> * {@link mod_type_one}<br> */p ublic void Initviewparam (int defaultvalue, int maxValue, int model) {switch (model) {Case Mod_type_half:mmodtype = Mod_type_half;mlinedivider = Item_half_divider;mvalue = DefaultValue * 2;mMaxValue = MaxVal UE * 2;break;case mod_type_one:mmodtype = Mod_type_one;mlinedivider = Item_one_divider;mvalue = DefaultValue;mMaxValue = Maxvalue;break;default:break;} Invalidate (); mlastx = 0;mmove = 0;notifyvaluechange ();} /** * Sets the listener for receiving results * * @param listener */public void Setvaluechangelistener (Onvaluechangelistener listener) {Mlistener = Listener;} /** * Gets the current tick value * * @return */public float getValue () {return mvalue;}  @Overrideprotected void OnLayout (Boolean changed, int left, int top, int. right, int bottom) {mwidth = GetWidth (); mheight = GetHeight (); Super.onlayout (changed, left, top, right, bottom);} @Overrideprotected void OnDraw (canvas canvas) {super.ondraw (canvas);d rawscaleline (canvas);//Drawwheel (canvas); Drawmiddleline (canvas);} private void Drawwheel (canvas canvas) {drawable wheel = getresources (). getdrawable (R.drawable.bg_wheel); Wheel.setbounds (0, 0, getwidth (), getheight ());Wheel.draw (canvas);} /** * Start drawing the tick marks from the middle to the sides * * @param canvas */private void drawscaleline (canvas canvas) {canvas.save (); Paint Linepaint = new paint (); Linepaint.setstrokewidth (2); Linepaint.setcolor (Color.Black); Textpaint textpaint = new Textpaint (paint.anti_alias_flag); Textpaint.settextsize (text_size * mDensity); int width = Mwidth, Drawcount = 0;float xposition = 0, textWidth = Layout.getdesiredwidth ("0", Textpaint); for (int i = 0; Drawcount &l t;= 4 * WIDTH; i++) {int numsize = string.valueof (Mvalue + i). Length (); xposition = (WIDTH/2-mmove) + i * Mlinedivider * MDENSITY;IF (  XPosition + getpaddingright () < Mwidth) {if ((mvalue + i)% Mmodtype = = 0) {canvas.drawline (xposition, Getpaddingtop (), XPosition, Mdensity * item_max_height, Linepaint), if (Mvalue + i <= mmaxvalue) {switch (mmodtype) {case MOD_TYPE_HALF: Canvas.drawtext (string.valueof ((Mvalue + i)/2), Countleftstart (Mvalue + i, xposition, textWidth), GetHeight ()-Textwidt h, Textpaint); Break;case MOD_TYPE_ONE:canvas.drawText(string.valueof (Mvalue + i), XPosition-(TextWidth * numsize/2), getheight ()-TextWidth, textpaint); Break;default:brea k;}}} else {canvas.drawline (xposition, Getpaddingtop (), XPosition, mdensity * item_min_height, Linepaint);}} XPosition = (WIDTH/2-mmove)-I * mlinedivider * mdensity;if (XPosition > Getpaddingleft ()) {if ((mvalue-i)% MMo DType = = 0) {canvas.drawline (xposition, Getpaddingtop (), XPosition, mdensity * item_max_height, Linepaint); if (mvalue-i  >= 0) {switch (mmodtype) {case MOD_TYPE_HALF:canvas.drawText (string.valueof ((mvalue-i)/2), Countleftstart (Mvalue- I, XPosition, textWidth), GetHeight ()-TextWidth, textpaint); Break;case MOD_TYPE_ONE:canvas.drawText (String.valueof ( Mvalue-i), XPosition-(TextWidth * numsize/2), getheight ()-TextWidth, textpaint); break;default:break;}}} else {canvas.drawline (xposition, Getpaddingtop (), XPosition, mdensity * item_min_height, Linepaint);}} Drawcount + = 2 * Mlinedivider * mdensity;} Canvas.restore ();} /** * Calculated without digital display* @param value * @param xposition * @param textWidth * @return */private float countleftstart (int value, float XPosition, float textWidth) {float XP = 0f;if (Value <) {XP = XPosition-(TextWidth *);} else {XP = Xpositio N-(TextWidth * 2/2);} return XP;} /** * Draw the middle of the red indicator lines, shadows and so on. The indicator line ends with a simple two rectangle instead of a * * @param canvas */private void drawmiddleline (canvas canvas) {///Tood constants too much, temporarily put this, will eventually put in the beginning of the class, put away afraid to soon forget int Gap = Indexwidth = 8, Indextitlewidth =, Indextitlehight = ten, shadow = 6; String color = "#66999999"; Canvas.save (); Paint Redpaint = new paint (); Redpaint.setstrokewidth (indexwidth); Redpaint.setcolor (color.red); Canvas.drawline ( MWIDTH/2, 0, MWIDTH/2, Mheight, Redpaint); Paint Ovalpaint = new paint (); Ovalpaint.setcolor (color.red); Ovalpaint.setstrokewidth (indextitlewidth); Canvas.drawline (MWIDTH/2, 0, MWIDTH/2, Indextitlehight, Ovalpaint); Canvas.drawline (MWIDTH/2, Mheight-indextitlehig HT, MWIDTH/2, Mheight, ovalpaint);//RECTF OVALRECTF = new RECTF (Mwidth/2-10, 0, MWIDTH/2 + ten, 4 *//mdensity);  TODO Ellipse//Canvas.drawoval (OVALRECTF, ovalpaint);//Ovalrectf.set (MWIDTH/2-Ten, MHeight-8 * mdensity, MWIDTH/2 +// Ten, Mheight); Todopaint shadowpaint = new Paint (); Shadowpaint.setstrokewidth (shadow); Shadowpaint.setcolor (Color.parsecolor ( color); Canvas.drawline (Mwidth/2 + Gap, 0, MWIDTH/2 + Gap, Mheight, shadowpaint); Canvas.restore (); @Overridepublic boolean ontouchevent (Motionevent event) {int action = event.getaction (); int xposition = (int) Event.getx ( ); if (Mvelocitytracker = = null) {Mvelocitytracker = Velocitytracker.obtain ();} Mvelocitytracker.addmovement (event); switch (action) {case MotionEvent.ACTION_DOWN:mScroller.forceFinished (true); MLASTX = Xposition;mmove = 0;break;case MotionEvent.ACTION_MOVE:mMove + = (mlastx-xposition); Changemoveandvalue (); Break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:countMoveEnd (); Countvelocitytracker (event); return false;//Break;default:break;} Mlastx = Xposition;return true;} private void Countvelocitytracker (Motionevent event) {mvelocitytracker.computecurrentvelocity (+); float xVelocity = Mvelocitytracker.getxvelocity (); if (Math.Abs (xvelocity) > Mminvelocity) {mscroller.fling (0, 0, (int) xvelocity, 0, Integer.min_value, Integer.max_value, 0, 0);}} private void Changemoveandvalue () {int tValue = (int) (Mmove/(Mlinedivider * mdensity)); if (Math.Abs (TValue) > 0) {MV Alue + = Tvalue;mmove = TValue * Mlinedivider * mdensity;if (mvalue <= 0 | | mvalue > Mmaxvalue) {mvalue = Mvalue &lt ; = 0? 0:mmaxvalue;mmove = 0;mscroller.forcefinished (true);} Notifyvaluechange ();} Postinvalidate ();} private void Countmoveend () {int roundmove = Math.Round (Mmove/(Mlinedivider * mdensity)); mvalue = Mvalue + roundmove;mva Lue = Mvalue <= 0? 0:mvalue;mvalue = mvalue > Mmaxvalue? MMAXVALUE:MVALUE;MLASTX = 0;mmove = 0;notifyvaluechange ();p ostinvalidate ();} private void Notifyvaluechange () {if (null! = Mlistener) {if (Mmodtype = = Mod_type_one) {Mlistener.onvaluechAnge (Mvalue);} if (Mmodtype = = mod_type_half) {mlistener.onvaluechange (mvalue/2f);}}} @Overridepublic void Computescroll () {super.computescroll (); if (Mscroller.computescrolloffset ()) {if ( Mscroller.getcurrx () = = Mscroller.getfinalx ()) {//Overcountmoveend ();} else {int xposition = Mscroller.getcurrx (); Mmove + = (mlastx-xposition); Changemoveandvalue (); mlastx = XPosition;}}}

The implementation of Android wheel scale

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.