There was a similar demand in the brothers project in the previous time group.
I see brothers suffering, unkind. And because of the indifferent, intends to an armchair. Is love and hate tangled, the day God said to me: nothing more to build some wheels, your life will have a lot of harvest. I was so touched by this chicken soup that I decided to save my brother from the fiery heat of the aquatic life.
Overriding the Onmeasure decision itself size
It is obvious that when the limit of the range that can be dragged is zero, that is, the Rangeseebar normal display can accept the limit, a cursory look: Width > 2 * Height
@Overrideprotected void onmeasure (int widthmeasurespec, int heightmeasurespec) {int widthsize = Measurespec.getsize ( WIDTHMEASURESPEC); int heightsize = Measurespec.getsize (Heightmeasurespec); if (Heightsize * 2 > Widthsize) { Setmeasureddimension (Widthsize, WIDTHSIZE/2);} else {super.onmeasure (widthmeasurespec, Heightmeasurespec);}}
draw a drag bar background everything starts from the simple first
public class Rangeseekbar extends View {private paint paint = new paint (Paint.anti_alias_flag); private int linetop, Linebottom, Lineleft, lineright; private int linecorners; private int linewidth; Private RECTF line = new RECTF (); Public Rangeseekbar (Context context) {This (context, NULL); } public Rangeseekbar (context context, AttributeSet Attrs) {Super (context, attrs); } @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {int widthsize = Measuresp Ec.getsize (WIDTHMEASURESPEC); int heightsize = measurespec.getsize (Heightmeasurespec); if (Heightsize * 2 > Widthsize) {setmeasureddimension (widthsize, (int) (WIDTHSIZE/2)); } else {super.onmeasure (widthmeasurespec, Heightmeasurespec); }} @Override protected void onsizechanged (int w, int h, int oldw, int oldh) {super.onsizechanged (W, H, O LDW, OLDH); int Seekbarradius = H/2; /** * Property Left Right top bottom describes the position of the Seekbar button * Blue after setting the three-dimensional RECTF line background according to their preset * Linecorn ERs sleek edges seem to look better than right angles */lineleft = Seekbarradius; Lineright = W-seekbarradius; Linetop = SEEKBARRADIUS-SEEKBARRADIUS/4; Linebottom = Seekbarradius + Seekbarradius/4; LineWidth = Lineright-lineleft; Line.set (Lineleft, Linetop, Lineright, Linebottom); Linecorners = (int) ((linebottom-linetop) * 0.45f); } @Override protected void OnDraw (canvas canvas) {super.ondraw (canvas); Paint.setstyle (Paint.Style.FILL); Paint.setcolor (0XFFD7D7D7); Canvas.drawroundrect (line, linecorners, linecorners, paint); }}
It is obvious that the Seekbarradius is designed to be the radius of the Seekbar button, with a value of Rangeseekbar half its height. So that the Seekbar button center of the default state can be pressed to the start and end of the background bar
The starting and ending points of the background bar are, of course, shifted from one radius to the inside relative to the width of each.
Drag the stage is ready, Seekbar button radius is also set. Yielded, the next step is to draw the Seekbar.
Seekbar buttons are excellent for owning objects
A rough thought: Buttons have color, size, discoloration, be drawn, collision detection, boundary detection, drag and so on, the most critical is that there are many. So the Seekbar button can be said to be a complex collection, it is time to send the object.
Private class SeekBar {int widthsize;int left, right, top, bottom; Bitmap bmp;/** * When the Rangeseekbar size changes, the Seekbar button size varies * * @param the relative position of the X center of the CenterX seekbar button in Rangeseekbar * @param cen The relative position of the Y center of the Tery seekbar button in Rangeseekbar * @param heightsize Rangeseekbar expects Seekbar to have a height */void onsizechanged (int CenterX , int centery, int heightsize) {/** * property left Right Top bottom describes the location of the Seekbar button <br> * widthsize = heightsize * 0.8f can be See button The actual area is a rectangle instead of a square * Circle button Why do you occupy a rectangular area? Because of the button shadow effect. Don't you have a shadow? I'm not * so onmeasure twice times the width? I will not */widthsize = (int) (heightsize * 0.8f); left = Centerx-widthsize/2;right = CenterX + widthsize/2;top = centery- Heightsize/2;bottom = centery + heightsize/2;bmp = Bitmap.createbitmap (Widthsize, Heightsize, Bitmap.Config.ARGB_8888 ) int bmpcenterx = Bmp.getwidth ()/2;int bmpcentery = Bmp.getheight ()/2;int Bmpradius = (int) (widthsize * 0.5f); Canvas Defaultcanvas = new canvas (BMP); Paint Defaultpaint = new paint (paint.anti_alias_flag);//Draw Shadowdefaultpaint.setstyle (PaiNt. Style.fill); int barshadowradius = (int) (Bmpradius * 0.95f);d efaultcanvas.save ();d efaultcanvas.translate (0, Bmpradius * 0.25f); Radialgradient shadowgradient = new Radialgradient (Bmpcenterx, Bmpcentery, Barshadowradius, Color.BLACK, Color.transparent, Shader.TileMode.CLAMP);d Efaultpaint.setshader (shadowgradient);d efaultcanvas.drawcircle ( Bmpcenterx, Bmpcentery, Barshadowradius, Defaultpaint);d efaultpaint.setshader (null);d efaultcanvas.restore ();// Draw Bodydefaultpaint.setstyle (Paint.Style.FILL);d Efaultpaint.setcolor (0xFFFFFFFF);d efaultcanvas.drawcircle ( Bmpcenterx, Bmpcentery, Bmpradius, defaultpaint);//Draw Borderdefaultpaint.setstyle (Paint.Style.STROKE); Defaultpaint.setcolor (0XFFD7D7D7);d efaultcanvas.drawcircle (Bmpcenterx, Bmpcentery, Bmpradius, defaultPaint);} void Draw (Canvas canvas) {canvas.drawbitmap (BMP, left, top, null); }}
public class Rangeseekbar extends View { private SeekBar SeekBar = new SeekBar (); Private class SeekBar { ... } @Override protected void onsizechanged (int w, int h, int oldw, int oldh) { super.onsizechanged (W, H, OLDW, OLDH); C7/>int Seekbarradius = H/2; ...//seekBar button size seekbar.onsizechanged (Seekbarradius, Seekbarradius, h) when Rangeseekbar determines size @Override protected void OnDraw (canvas canvas) { super.ondraw (canvas); ...//Draw SeekBar button Seekbar.draw (canvas) when Rangeseekbar is drawn;} }
It's a step closer to success.
Ontouchevent Touch Monitor let Seekbar button move.
@Overridepublic boolean ontouchevent (Motionevent event) {switch (event.getaction ()) {case Motionevent.action_down: Boolean touchresult = false;//to detect whether the finger falls on the current seekbar. That is, when declaring seekbar, use the internal if (Seekbar.collide (event)) {Touchresult = true) of the area described by the left, top, right, bottom property; return touchresult;case MotionEvent.ACTION_MOVE:float percent;float x = Event.getx (); if (x <= lineleft) {percent = 0;} else if (x >= lineright) {percent = 1;} else {percent = (x-lineleft) * 1f/(linewidth);} Seekbar button slide Seekbar.slide (percent) according to the current finger sliding on the drag bar; invalidate (); break;} Return Super.ontouchevent (event);}
Private class SeekBar {int linewidth;//drag bar width to get float currpercent;int left at onsizechanged time, right, top, Bottom;boolean C Ollide (Motionevent event) {float x = Event.getx (); Float y = event.gety (); int offset = (int) (linewidth * currpercent); Retu RN x > left + offset && x < right + offset && y > top && y < bottom;} void slide (float percent) {if (Percent < 0) percent = 0;else if (Percent > 1) percent = 1;currpercent = percent;} void Draw (canvas canvas) {int offset = (int) (linewidth * currpercent); Canvas.save (); canvas.translate (offset, 0); Canvas.drawbitmap (BMP, left, top, null); Canvas.restore ();}}
A better visual experience
At this point, the seekbar is lifeless when pressed, and the next step is to add a strong visual feedback.
Then the lazy way of onsizechanged preset buttons is GG, because the Seekbar UI effect needs to change with the touch state.
First, get this change in ontouchevent.
@Overridepublic boolean ontouchevent (Motionevent event) {switch (event.getaction ()) {case Motionevent.action_move: seekbar.material = seekbar.material >= 1? 1:seekbar.material + 0.1f;...invalidate (); Break;case MotionEvent.ACTION_CANCEL:case motionevent.action_up: Seekbar.materialrestore (); break;} Return Super.ontouchevent (event);}
Then respond to this change in the Seekbar button
Private class SeekBar {float material = 0; Valueanimator anim;final typeevaluator<integer> te = new typeevaluator<integer> () {@Overridepublic Integer Evaluate (float fraction, integer startvalue, integer endvalue) {int alpha = (int) (Color.alpha (startvalue) + fraction * (C Olor.alpha (Endvalue)-Color.alpha (Startvalue)); int red = (int) (Color.Red (startvalue) + fraction * (color.red (EndValue) )-Color.Red (Startvalue)); int green = (int) (Color.green (startvalue) + fraction * (Color.green (Endvalue)-Color.green (s Tartvalue)); int blue = (int) (Color.Blue (startvalue) + fraction * (Color.Blue (Endvalue)-Color.Blue (Startvalue))); Return Color.argb (alpha, red, green, blue);}; void Draw (canvas canvas) {int offset = (int) (linewidth * currpercent); Canvas.save (); Canvas.translate (left, 0); Canvas.translate (offset, 0);d rawdefault (canvas); Canvas.restore ();} private void Drawdefault (canvas canvas) {int CenterX = Widthsize/2;int CenterY = heightsize/2;int radius = (int) (Widt Hsize * 0.5f);Draw Shadowdefaultpaint.setstyle (Paint.Style.FILL); Canvas.save () canvas.translate (0, RADIUS * 0.25f); Canvas.scale (1 + (0.1f * material), 1 + (0.1f * material), CenterX, CenterY);d Efaultpaint.setshader (shadowgradient); Canva S.drawcircle (CenterX, CenterY, radius, defaultpaint);d efaultpaint.setshader (null); Canvas.restore ();//Draw Bodydefaultpaint.setstyle (Paint.Style.FILL);d Efaultpaint.setcolor (te.evaluate (material, 0xFFFFFFFF, 0xffe7e7e7)) ; Canvas.drawcircle (CenterX, CenterY, radius, defaultpaint);//Draw Borderdefaultpaint.setstyle (Paint.Style.STROKE); Defaultpaint.setcolor (0XFFD7D7D7); Canvas.drawcircle (CenterX, CenterY, radius, defaultpaint);} private void Materialrestore () {if (anim! = null) Anim.cancel (); anim = valueanimator.offloat (material, 0); Anim.addupdatelistener (New Valueanimator.animatorupdatelistener () {@Overridepublic void Onanimationupdate ( Valueanimator animation) {material = (float) animation.getanimatedvalue (); invalidate ();}); Anim.addlistener (New Animatorlisteneradapter ({@Overridepublic void Onanimationend (Animator animation) {material = 0;invalidate ();}}); Anim.start ();}}
The logic to draw the BMP directly in the draw method is replaced with the Drawdefault
So Drawdefault's internal logic is basically the same as the pre-fabricated BMP, the only two difference is that the shadow shadow do a scale processing, the button body color to do a gradient processing
Materialrestore that is, when the user's finger is lifted and a thread is opened, the state is gradually initialized
Range
Range means scope, but even knowing that doesn't seem to have any eggs _ (: 3"∠) _
So in order to understand the law, the baby hard groping. Finally found
If they all have their own fixed sliding range, the Seekbar button on the right is the left Seekbar button to the right to pan the Seekbar button width.
public class Rangeseekbar extends View {private SeekBar LEFTSB = new SeekBar (); Private SeekBar RIGHTSB = new SeekBar (); /** * is used to record the current user touch the end is which SB */private SeekBar Currtouch; @Override protected void onsizechanged (int w, int h, int oldw, int oldh) {super.onsizechanged (W, H, OLDW, OLDH) ; ...//RIGHTSB just like the analysis, tightly affixed to LEFTSB's right rightsb.left + = leftsb.widthsize; Rightsb.right + = Leftsb.widthsize; } @Override protected void OnDraw (canvas canvas) {super.ondraw (canvas); ... leftsb.draw (canvas); Rightsb.draw (canvas); } @Override public boolean ontouchevent (Motionevent event) {switch (event.getaction ()) {case Mot IonEvent.ACTION_DOWN:boolean Touchresult = false; /** * Why not detect LEFTSB First and detect RIGHTSB first? Why? ('? ') ) */if (Rightsb.collide (event)) {Currtouch = RIGHTSB; Touchresult = true; } else if (Leftsb.collide (event)) {Currtouch = LEFTSB; Touchresult = true; } return Touchresult; Case MotionEvent.ACTION_MOVE:float percent; float x = Event.getx (); if (Currtouch = = LEFTSB) {if (x < lineleft) {percent = 0; } else {percent = (x-lineleft) * 1f/(linewidth-rightsb.widthsize); } if (Percent > rightsb.currpercent) {percent = Rightsb.currpercent; } leftsb.slide (percent); } else if (Currtouch = = RIGHTSB) {if (x > Lineright) {percent = 1; } else {percent = (x-lineleft-leftsb.widthsize) * 1f/(LINEWIDTH-LEFTSB. widthsize); } if (Percent < leftsb.currpercent) {percent = Leftsb.currpercent; } rightsb.slide (percent); } invalidate (); Break } return Super.ontouchevent (event); }}
By touching the values of some properties, drawing the corresponding UI effect through the values of these properties, routines are all routines
Then after the Switchbutton, it is a re-revision of the routine
So what can this baby's Rangeseekbar do?
supports negative numbers
Support reserved (reserved) Range
What is a reserved (reserved) range? Like that, you know. Imaginative achievement here, inexpressible. (? ? ?)?
For example, now 2 buttons are reserved for a distance, of course, you can keep n
Support scale mode
Of course the scale is supported and the reserved range is supported
Support for custom UI button style background color
There seems to be less pressing state changes
How do I use it?
[Poke me to rangeseekbar use tutorial]
If you like this article, you can also make a reward, the amount of unlimited
Copyright NOTICE: Welcome reprint, but please respect the author's labor results, reprint please indicate the source-->http://blog.csdn.net/bfbx5173 QQ Group: 274306954
Android View custom Rangeseekbar range Selector Walk on the View advanced path