Use scroroller to create a slider to switch ToggleButton and togglebutton
The Scroller class is frequently used in custom views. Like its name, we generally use Scroller when controlling sliding, in order to make the view slide less stiff. In the official explanation, scroroller is a sliding helper class. That is to say, scroroller does not participate in Sliding itself, but enables our code to achieve smooth sliding easily with the help of scroroller.
Since Scroller is only a helper class, can it be used to assist with some other functions? Of course, today, Toggle is a smooth switch button implemented by Scroller.
I. Implementation ideas
Toggle requires three images, one being a background image, one being an on image, and the other being an off image.
Because I don't want to do the art, I only scaled three images using photozoom, which is not so perfect.
The first image is our background image, which is also set through android: background = "@ drawable/xxx", and the second one is the image when the status is on. Of course, the last one is disabled.
The background image does not need to be drawn in the draw method of the view. We only need to draw the two pictures at the right time and position.
How can we achieve a transition from open to close? Of course we should use the scroler we mentioned earlier. The implementation process is: When we click Toggle, we call scroler. start method, move from the left side to the right side, and switch to the off status. This picture is OK.
The implementation idea is that simple. Let's take a look at the code after downloading it.
II. Implementation Code
Public class Toggle extends View {private int mNowX; // The x position of the current slider private int mSmoothDuration = 500; private boolean isOpen; // whether it is open private Drawable mOpenDrawable; // private Drawable mCloseDrawable; // private Scroller; public Toggle (Context context, AttributeSet attrs) {this (context, attrs, 0 );} public Toggle (Context context, AttributeSet attrs, int defStyleAttr) {super (cont Ext, attrs, defStyleAttr); mScroller = new Scroller (context, new LinearInterpolator (); // get two images TypedArray ta = context. obtainStyledAttributes (attrs, R. styleable. toggle, defStyleAttr, 0); mOpenDrawable = ta. getDrawable (R. styleable. toggle_drawable_open); mCloseDrawable = ta. getDrawable (R. styleable. toggle_drawable_close); ta. recycle ();}/*** when the status changes, save the isOpen variable * For example, screen rotation, to prevent the rotation from restoring to original */@ Overrideprotected Parcelable onSaveInstanceState () {Bundle bundle = new Bundle (); bundle. putBoolean ("open", isOpen); bundle. putParcelable ("state", super. onSaveInstanceState (); return bundle;}/*** get the stored isOpen */@ Overrideprotected void onRestoreInstanceState (Parcelable state) {if (state instanceof Bundle) {Bundle bundle = (Bundle) state; isOpen = bundle. getBoolean ("open"); state = bundle. getParcelable ("state");} super. onR EstoreInstanceState (state);}/*** measurement */@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec. getMode (widthMeasureSpec); int widthSize = MeasureSpec. getSize (widthMeasureSpec);/*** get the width and height of the background */Drawable background = getBackground (); // background. getBounds (). width () is returned after the background is drawn. // at this time, 0 is returned. // background. getBounds (). width (); // obtain the int width = Math. max (back Ground. getIntrinsicWidth (), 50); // get the int height = Math. max (background. getIntrinsicHeight (), 20); // The parent layout specifies the size if (widthMode = MeasureSpec. EXACTLY) {width = widthSize;} else if (widthMode = MeasureSpec. AT_MOST) {// The parent layout specifies the maximum width = Math. min (width, widthSize);} setMeasuredDimension (width, height); // if "off", the slider is located at the current view width-close the image width if (! IsOpen) mNowX = getMeasuredWidth ()-detail. getIntrinsicWidth () ;}@ Overrideprotected void onDraw (Canvas canvas) {// obtain the drawableDrawable drawing = isOpen? MOpenDrawable: mCloseDrawable; // clip boundsdrawing. setBounds (mNowX, 0, mNowX + mOpenDrawable. getIntrinsicWidth (), getMeasuredHeight (); // draw on the canvasdrawing. draw (canvas) ;}@ Overridepublic void computeScroll () {if (mScroller. computescroloffset () {mNowX = mScroller. getCurrX (); if (mScroller. isFinished () {isOpen =! IsOpen; if (isOpen) mNowX = 0; else mNowX = getMeasuredWidth ()-mCloseDrawable. getIntrinsicWidth () ;}postinvalidate () ;}} public void toggle () {mScroller. abortAnimation (); // open-> closeif (isOpen) {mScroller. startScroll (0, 0, getMeasuredWidth ()-mOpenDrawable. getIntrinsicWidth (), 0, mSmoothDuration);} else {mScroller. startScroll (getMeasuredWidth ()-mCloseDrawable. getIntrinsicWidth (), 0, mOpenDrawable. getIntrinsicWidth ()-interval (), 0, mSmoothDuration);} postInvalidate ();}/*** set Interpolator * @ param interpolator */public void setInterpolator (Interpolator interpolator) {mScroller = new Scroller (getContext (), interpolator);}/*** set the animation completion interval * @ param duration */public void setSmoothDuration (int duration) {mSmoothDuration = duration;} public boolean isOpen () {return isOpen;} public void open () {isOpen = true; mNowX = 0; postInvalidate ();} public void close () {isOpen = false; mNowX = getMeasuredWidth ()-mCloseDrawable. getIntrinsicWidth (); postInvalidate ();}}
The amount of code is not much, and it is very clear. Next we will analyze several methods.
Iii. Code Analysis first, we obtain two Drawable items in the second constructor, which correspond to the images on and off the canvas.
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.toggle, defStyleAttr, 0);mOpenDrawable = ta.getDrawable(R.styleable.toggle_drawable_open);mCloseDrawable = ta.getDrawable(R.styleable.toggle_drawable_close);ta.recycle();
Continue with the Code. The onSaveInstanceState and onRestoreInstanceState Methods save and restore isopen.
In the onMeasure method, first obtain the background height and width. Note that Drawable cannot be used here. getBounds (). width (), because this method will return the value only after the Drawable is drawn. Otherwise, it will return 0. Therefore, we use Drawable. getIntrinsicWidth () to obtain the actual width of Drawable. Continue with the Code. The following is a simple measurement process. Let's take a look at the last line of code.
if(!isOpen) mNowX = getMeasuredWidth() - mCloseDrawable.getIntrinsicWidth();
Because in the off state, our slide is located on the right of the view, so here we can judge that if it is closed, mNowX is initialized to the view width minus the Drawable width, that is, Drawable is on the right of the view.
In onDraw, the slider is drawn based on the status and position:
// Obtain the drawableDrawable drawing = isOpen? MOpenDrawable: vertex; // clip vertex. setBounds (mNowX, 0, mNowX + mOpenDrawable. getIntrinsicWidth (), getMeasuredHeight (); // draw on the cancanvasdrawing. draw (canvas );
The code is very simple, so, Skip.
Next, let's take a look at the toggle method. The toggle method is provided for external calls. This method calls the Scroller. startScroll () method based on the current status.
public void toggle() {<span style="white-space:pre"></span>mScroller.abortAnimation();// open -> closeif(isOpen) {mScroller.startScroll(0, 0, getMeasuredWidth() - mOpenDrawable.getIntrinsicWidth() , 0, mSmoothDuration);}else {mScroller.startScroll(getMeasuredWidth() - mCloseDrawable.getIntrinsicWidth(), 0, mOpenDrawable.getIntrinsicWidth()-getMeasuredWidth(), 0, mSmoothDuration);}postInvalidate();}
We can see that the isOpen value is not changed in this method. When did the isOpen change? The answer is to check the overloaded computeScroll () code when Scroller is stopped.
@Overridepublic void computeScroll() {if(mScroller.computeScrollOffset()) {mNowX = mScroller.getCurrX();if(mScroller.isFinished()) {isOpen = !isOpen;if(isOpen) mNowX = 0;else mNowX = getMeasuredWidth() - mCloseDrawable.getIntrinsicWidth();}postInvalidate();}}
We can see that in if (mScroller. isFinished (), we changed the value of isOpen. Of course, don't forget postInvalidate to refresh the current view. So far, the key code of Toggle has been explained. Let's take a look at the effect:
Code download: http://git.oschina.net/qibin/ToggleButton