Android Custom Ruler Control Rulerview

Source: Internet
Author: User
Tags drawtext getcolor

It's a terrible experience to let users enter their height and weight directly. We might as well let the user start the finger slide ruler to determine his height and weight, isn't it more interesting?

Package Com.lw.widget;import Android.content.context;import Android.content.res.typedarray;import Android.graphics.canvas;import Android.graphics.color;import Android.graphics.paint;import Android.graphics.Rect; Import Android.os.build;import Android.support.annotation.integerres;import Android.util.attributeset;import Android.view.gravity;import Android.view.motionevent;import Android.view.velocitytracker;import Android.view.View ; Import Android.view.viewconfiguration;import android.widget.overscroller;import com.lw.r;/** * Ruler Classes */public class    Rulerview extends View {//getsimplename () returns the short name of the underlying class given in the source code.    Final String TAG = RulerView.class.getSimpleName ();    Start range private int mbeginrange;    End range private int mendrange;    /** internal width, which is the width of the ruler per bar */private int minnerwidth;    The interval between ruler entries private int mindicatepadding;    The brush that is displayed is private Paint mindicatepaint;    Text brush private paint mtextpaint;    The width of the display is private int mindicatewidth; Size of the display private float mindicatescale;    The x-coordinate of the last gesture is private int mlastmotionx;    Whether the/** can slide */private Boolean misdragged;    Whether to automatically match private Boolean misautoalign = true;    Whether the text needs to be displayed private Boolean miswithtext = true;    Text color private int mtextcolor;    Text size private float mtextsize;    The color of the ruler is private int mindicatecolor;    Size ratio listener private onscalelistener mlistener;    Position of ruler Bar display: top,bottom private int mgravity;    /** Ruler rectangle (tick bar) */private Rect mindicateloc;    /** Scrolling related parameters, this class encapsulates the bounds of scrolling and super capabilities */private Overscroller Moverscroller; /** helps track the speed of touch events and is used to perform actions such as throwing.    */Private Velocitytracker mvelocitytracker;    /** Touch Overflow */private int mtouchslop;    Minimum sliding rate private int mminimumvelocity;    Maximum rate private int mmaximumvelocity;    Public Rulerview (Context context) {This (context, NULL);    } public Rulerview (context context, AttributeSet Attrs) {This (context, attrs, 0); }/** * is ultimately called this constructor method * * @param context * @param attrs * @param defstyLEATTR */Public Rulerview (context context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, de        FSTYLEATTR);        Gets the custom attribute data set and writes the default value, customizing 8 properties TypedArray TA = context.obtainstyledattributes (attrs, R.styleable.rulerview);        Mindicatecolor = Ta.getcolor (R.styleable.rulerview_indicatecolor, Color.Black);        Mtextcolor = Ta.getcolor (R.styleable.rulerview_textcolor, Color.gray);        Mtextsize = Ta.getdimension (r.styleable.rulerview_textsize, 18);        Mbeginrange = Ta.getint (r.styleable.rulerview_begin, 0);        Mendrange = Ta.getint (r.styleable.rulerview_end, 100);        Ruler width mindicatewidth = (int) ta.getdimension (r.styleable.rulerview_indicatewidth, 5);        Clearance of the ruler mindicatepadding = (int) ta.getdimension (r.styleable.rulerview_indicatepadding, 15);        Ta.recycle (); The ruler bar displays the position, the default value is displayed at the bottom int[] indices = new int[]{android.        R.attr.gravity};       Ta = context.obtainstyledattributes (attrs, indices); mgravity = Ta.getint (Ta.getindex (0), gravity.bottom);        Ta.recycle ();        The default display scale is 0.7 times times Mindicatescale = 0.7f;    InitValue (); }/** * Initialize the value */private void InitValue () {/** Create this scroll class and set the scrolling mode to: 1. Over_scroll_always Standard mode * There are also two scrolling modes: 2. Over_scroll_if_content_scrolls Content Scrolling * 3.over_scroll_never does not scroll */Moverscroller = new Overscroller (g        Etcontext ());        Setoverscrollmode (over_scroll_always);        Get view configuration, set touch overflow, and minimum and maximum touch rate final viewconfiguration configuration = Viewconfiguration.get (GetContext ());        Mtouchslop = Configuration.getscaledtouchslop ();        Mminimumvelocity = Configuration.getscaledminimumflingvelocity ();        Mmaximumvelocity = Configuration.getscaledmaximumflingvelocity ();        Set the brush for the ruler, solid picture mindicatepaint = new paint ();        Mindicatepaint.setstyle (Paint.Style.FILL);        Set the text brush, solid picture, and anti-aliasing mtextpaint = new paint (Paint.anti_alias_flag); Mtextpaint.setstyLe (Paint.Style.FILL);        Mtextpaint.setcolor (Mtextcolor);        Mtextpaint.settextalign (Paint.Align.CENTER);        Mtextpaint.settextsize (mtextsize);        Internal width (ruler end range-Ruler start range) * indicates width minnerwidth = (mendrange-mbeginrange) * Getindicatewidth ();    The ruler is positioned as a rectangle mindicateloc = new Rect ();         }/** * Rewrite drawing method * * @param canvas */@Override protected void OnDraw (canvas canvas) {/** * When we are rotating, zooming, panning, and so on, we actually want to operate on a particular element, a slice, a rectangle, etc, but when you do this with the canvas, the whole canvas is actually manipulated, and then The elements on the canvas are affected, * so we call Canvas.save () before the operation to save the current state of the canvas, * when the action is taken to remove the previously saved state, so that no other elements will be affected */I        NT count = Canvas.save (); Loop-Draw the ruler bar (scale) to draw a for (int value = mbeginrange, Position = 0; value <= mendrange; value++, position++) based on maximum and minimum values            {drawindicate (canvas, position); If you need a number, you also need to draw the number if (Miswithtext) drawText (canvas, Position, string.valueof (VALue));    }//Restore the state of the canvas Canvas.restoretocount (count); }/** * Draw the ruler bar (scale), 0 to 100 will display 100 ticks * @param canvas canvas * @param position */private void Drawindicate (        Canvas canvas, int position) {Computeindicateloc (mindicateloc, position);        int left = Mindicateloc.left + mindicatepadding;        int right = mindicateloc.right-mindicatepadding;        int top = Mindicateloc.top;        int bottom = Mindicateloc.bottom;            if (position% 5 = 0) {int indicateheight = Bottom-top;            if (Isaligntop ()) {bottom = (int) (top + indicateheight * mindicatescale);            } else {top = (int) (bottom-indicateheight * mindicatescale);        }} mindicatepaint.setcolor (Mindicatecolor);    Canvas.drawrect (left, top, right, bottom, mindicatepaint); }/** * Draw text, draw one text per 5 ticks for tips * @param canvas * @param position * @param text */private voidDrawText (canvas canvas, int position, String text) {if (position% 5 = 0) return;        Computeindicateloc (mindicateloc, position);        int textHeight = Computetextheight ();        Mtextpaint.setcolor (Mtextcolor);        Mtextpaint.settextsize (mtextsize);        Mtextpaint.settextalign (Paint.Align.CENTER);        int x = (mindicateloc.left + mindicateloc.right)/2;        int y = mindicateloc.bottom + textHeight;            if (!isaligntop ()) {y = mindicateloc.top;            Mtextpaint.gettextbounds (text, 0, Text.length (), mindicateloc);  Y + = MINDICATELOC.TOP/2;    Add some offsets} canvas.drawtext (text, x, y, mtextpaint);     /** * Calculates the position of the indicator: set left upper right Bottom * End is set this rectangle (tick left upper right bottom) * @param outrect rectangle * @param position position value (representing the first tick) */        private void Computeindicateloc (Rect outrect, int position) {if (Outrect = = null) return;        int height = getheight ();     int indicate = Getindicatewidth ();   int left = (indicate * position);        int right = left + indicate; int top = Getpaddingtop ();//Gets the top padding of the current view int bottom = Height-getpaddingbottom ();//view height-View low padding if (miswithte            XT) {int textHeight = Computetextheight (); if (Isaligntop ()) Bottom-= textheight;//If the scale is displayed at the top, the bottom of the text is subtracted from the height of else top + = Texthe        ight;//If the scale is displayed at the bottom, the top is added with the text height}//text offset, the left and right side are added an offset int offsets = getstartoffsets ();        Left + = offsets;        Right + = offsets;    Outrect.set (left, top, right, bottom);     }/** * Starts the offset and needs to be offset if you want to include text. * * @return */private int getstartoffsets () {if (miswithtext) {String Text = String.valueo            f (mbeginrange);            Returns the width of the literal int textWidth = (int) mtextpaint.measuretext (text, 0, text.length ());    Return textwidth/2;//The actual offset of half the text width so that it is centered in the display} return 0;   }/** * Touch related events * @param event  * @return */@Override public boolean ontouchevent (Motionevent event) {//If no initial speed trace exists INITVELOCI        Tytrackerifnotexists ();        Speed Tracker adds mobile event mvelocitytracker.addmovement (event); Switch (event.getaction ()) {case Motionevent.action_down://Press If slide has not ended if (MISD                        Ragged =!moverscroller.isfinished ()) {if (getParent () = null)//required to block touch events                GetParent (). Requestdisallowintercepttouchevent (True); }//If the animation is not finished, end the animation if (!moverscroller.isfinished ()) Moverscroller.abortani                Mation ();                Record the coordinates of the x pressed Mlastmotionx = (int) event.getx ();            return true;                Case Motionevent.action_move://Move the value of x and get (press X-value-Move x) the difference of value int curx = (int) event.getx ();                int deltax = Mlastmotionx-curx; If the slide does not end, and the distance to move is neverThe value is greater than the amount of touch overflow if (!misdragged && math.abs (deltax) > Mtouchslop) {if (GetParent () ! = NULL)//If there is a parent control, tell the parent control not to intercept my Touch event getParent (). Requestdisallowinterceptto                    Uchevent (TRUE);                    and set the sliding end misdragged = true;                    If the touch difference is 0, the touch difference needs-Touch overflow amount, otherwise plus if (DeltaX > 0) {deltax-= Mtouchslop;                    } else {deltax + = Mtouchslop; }}//If the slide ends, the last x-coordinate is the current touch of the point if (misdragged) {mlastmotion                    X = CurX;                        If the X value of the scroll is 0 or greater than the maximum scroll value, let the touch difference *0.7 if (getscrollx () <= 0 | | getscrollx () >= getmaximumscroll ())                    DeltaX *= 0.7;                    Scrolling beyond normal standard behavior of the view, rate listening to clear????????????? if (Overscrollby (deltax, 0, Getscrollx (), getscrolly (), GetmaximuMscroll (), 0, getwidth (), 0, True) {mvelocitytracker.clear ();            }} break; Case MOTIONEVENT.ACTION_UP: {if (misdragged) {//Check sliding speed, 1000 units Mvel                    Ocitytracker.computecurrentvelocity (mmaximumvelocity);                    Get the velocity int initialvelocity = (int) mvelocitytracker.getxvelocity () on the x-axis; If x-axis velocity is the minimum flow rate if ((Math.Abs (initialvelocity) > Mminimumvelocity)) {Fling (-in                    itialvelocity);                        } else {//aligncenter ();                    Rebound to the end of Sprintback ();                }}//sliding end misdragged = false;                Release Tracker resource Recyclevelocitytracker ();            Break                } Case Motionevent.action_cancel: {If the slide ends and the scroll ends, roll back if (misdragged && moverscroller.isfinished ()) {sprintback ();                } misdragged = false;                Recyclevelocitytracker ();            Break    }} return true; }/** * Refresh parameter value */private void Refreshvalues () {//Internal width = (max-start value) * Scale width minnerwidth = (Mendran        Ge-mbeginrange) * Getindicatewidth ();    Invalidateview (); }/** * Final indication Width: scale width + scale inner margin + scale inner margin * * @return */private int getindicatewidth () {return Mindi    Catewidth + mindicatepadding + mindicatepadding;     }/** * Gets the minimum scroll value. * * @return * * * private int Getminimumscroll () {return-(getwidth ()-getindicatewidth ())/2 + Getstart    Offsets ();     }/** * Gets the maximum scroll value.    * * @return */private int getmaximumscroll () {return minnerwidth + getminimumscroll ();     }/** * Adjust the scale so that it is centered. */private void ADJustindicate () {if (!moverscroller.isfinished ()) moverscroller.abortanimation ();        int position = Computeselectedposition ();        int scrollx = getscrollbyposition (position);        SCROLLX-= GETSCROLLX ();            if (scrollx! = 0) {//scroll boundary starts rolling moverscroller.startscroll (GETSCROLLX (), getscrolly (), SCROLLX, 0);        Invalidateview (); }}/** * throw * @param Velocityx * Roll Back Refresh interface */public void Fling (int velocityx) {m) according to the x-axis sliding rate Overscroller.fling (Getscrollx (), getscrolly (), Velocityx, 0, Getminimumscroll (), Getmaximumscroll (), 0, 0, getWidth ()/        2, 0);    Invalidateview (); }/** * Rebound */public void Sprintback () {Moverscroller.springback (Getscrollx (), getscrolly (), Getmini        Mumscroll (), Getmaximumscroll (), 0, 0);    Invalidateview (); } public void Setonscalelistener (Onscalelistener listener) {if (listener! = null) {Mlistener = List        Ener; }   }/** * Gets the absolute scroll position of the position. * * @param position * @return * */private int getscrollbyposition (int position) {Computeindicateloc (        Mindicateloc, position);        int scrollx = mindicateloc.left-getstartoffsets () + Getminimumscroll ();    return SCROLLX;        /** * Calculates the currently selected position * * @return */public int computeselectedposition () {//calculates the middle position of two ticks        int CenterX = GETSCROLLX ()-getminimumscroll () + getindicatewidth ()/2;        Determine the selected tick position CenterX = Math.max (0, Math.min (minnerwidth, CenterX)) by the median position.        int position = Centerx/getindicatewidth ();    return position; }/** * Smooth scrolling * @param position */public void Smoothscrollto (int position) {//If selected location <0 or start value +        Choose a location larger than the final value, and return directly if (Position < 0 | | Mbeginrange + position > Mendrange) return;    If scrolling is not complete, break it's animation bar if (!moverscroller.isfinished ()) moverscroller.abortanimation ();    int scrollx = getscrollbyposition (position);        Moverscroller.startscroll (Getscrollx (), getscrolly (), SCROLLX-GETSCROLLX (), 0);    Invalidateview (); }/** * Smooth scroll to the value * @param value */public void Smoothscrolltovalue (int value) {int position = Val        Ue-mbeginrange;    Smoothscrollto (position); }/** * Trigger Zoom Out event * @param scale */private void onscalechanged (int scale) {if (Mlistener! = null    ) mlistener.onscalechanged (scale);     }/** * Re-scrolling event * @param scrollx * @param scrolly * @param clampedx fixed x * @param clampedy fixed y        */@Override protected void onoverscrolled (int scrollx, int scrolly, Boolean clampedx, Boolean clampedy) {            If scrolling is not complete, set the scroll x parameter and listen for the scrolling if (!moverscroller.isfinished ()) {final int oldx = GETSCROLLX ();            Final int oldY = getscrolly ();            SETSCROLLX (SCROLLX);  Onscrollchanged (SCROLLX, scrolly, OLDX, OldY);          if (Clampedx) {//sprintback ();        }} else {Super.scrollto (scrollx, scrolly); }//If the listener is not NULL, assigns the current selected position and triggers the scaling Change event if (Mlistener! = null) {int position = Computeselectedpositi            On ();        Onscalechanged (position + mbeginrange); }} @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (Widt    Hmeasurespec, Heightmeasurespec);        /** * Calculate Text Height * @return */private int computetextheight () {//Use FontMetrics object to calculate the coordinates of the text.        Paint.fontmetrics FontMetrics = Mtextpaint.getfontmetrics ();        float textHeight = fontmetrics.descent-fontmetrics.ascent;    return (int) textHeight; The private Boolean isaligntop () {//& is a bitwise operator, which is a 32-bit binary worth comparing return (mgravity & gravity.top) = = Gravity .    TOP;        } public void setgravity (int gravity) {this.mgravity = gravity;  Invalidateview ();  }/** * Calculate scrolling */@Override public void Computescroll () {if (Moverscroller.computescrolloffset ())            {int oldx = GETSCROLLX ();           int oldY = getscrolly ();            Returns the current offset in the scroll, Baidu actually translates int x = Moverscroller.getcurrx ();            int y = Moverscroller.getcurry ();            Scrolling too much operation Overscrollby (X-OLDX, Y-oldy, OLDX, OldY, Getmaximumscroll (), 0, getwidth (), 0, false);        Invalidateview ();        } else if (!misdragged && misautoalign) {//If the auto-align Adjustindicate () is no longer scrolled and turned on;    }} @Override protected int computehorizontalscrollrange () {return getmaximumscroll (); }/** * Refresh interface * If version is greater than 16 (4.1) * Use Postinvalidate to update interface directly in thread * invalidate () must be used in UI threads */Public V        OID Invalidateview () {if (Build.VERSION.SDK_INT >=) {postinvalidateonanimation ();    } else invalidate (); }/** * Get turnover tracker */Private void Initvelocitytrackerifnotexists () {if (Mvelocitytracker = = null) {//Get current turnover tracking Mveloc        Itytracker = Velocitytracker.obtain ();            }}/** * Release turnover tracker resource */private void Recyclevelocitytracker () {if (Mvelocitytracker! = null) {            Mvelocitytracker.recycle ();        Mvelocitytracker = null;    }}/** * Zoom Out Listener Interface */public interface Onscalelistener {void onscalechanged (int scale);        }/** * Sets the width of the scale * @param indicatewidth */public void setindicatewidth (@IntegerRes int indicatewidth) {        This.mindicatewidth = Indicatewidth;    Refreshvalues (); }/** * Set the spacing within the scale * @param indicatepadding */public void setindicatepadding (@IntegerRes int Indicatepaddi        NG) {this.mindicatepadding = indicatepadding;    Refreshvalues ();        The public void Setwithtext (Boolean withtext) {this.miswithtext = Withtext;    Refreshvalues ();    }public void Setautoalign (Boolean autoalign) {this.misautoalign = autoalign;    Refreshvalues ();    }/** * Whether to display text * @return */public boolean iswithtext () {return miswithtext;    }/** * Automatically aligns ticks * @return */public boolean isautoalign () {return misautoalign; }}

Source Address: Rulerview

Android Custom Ruler Control Rulerview

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.