In Android apps, sliding is standard for almost every app. For sliding, we can use Scroller, property animations, or LayoutParams that dynamically change the view. Today, we will mainly analyze the use of Scroller, and apply Scroller to our development by implementing a simple sideslip menu.

Preparation: Coordinate system

Before analyzing the use of Scroller, we need to clarify some problems about the coordinate system. Here, we mainly analyze the X axis and the Y axis. First, let’s take a look at getScrollX() :




getScrollX.png

Simply put, this method returns the left X-axis of the view minus the value of the left X-axis of the view’s content. So what exactly is sliding view content? Let’s take a look at two methods and illustrate them with examples:




scrollto.png

Both methods slide the view content, but the difference is that scrollBy slides incrementally relative to the current position of the view content, whereas scrollTo slides relative to the absolute coordinates of the view. Having said so much, what exactly is sliding on content and relative sliding, absolute sliding, everyone may be a bit vague, I will illustrate the following by example. First, let’s look at the layout and core code:



       

       
   

   

   Copy the code
Copy the code
mScrollBy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMainArea.scrollBy(100, 0); }}); mScrollTo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMainArea.scrollTo(0, 0); }});Copy the code

Then, take a look at the rendering:




scroll.gif

The layout file contains two TextViews in a relative layout that are evenly spaced and full in height. In the scroolBy button, the scrollBy(100,0) method of the RelativieLayout is called. As mentioned earlier, scrollBy slides based on the relative position of the view’s contents, and we see that the contents of this RelativeLayout are shifted 100px to the left with every click. The scrollTo button is the scrollTo(0,0) method called, and we see that it returns the two textviews directly to the point (0,0), which is the view’s content offset relative to its absolute coordinates. ScrollBy (100,0); scrollBy(100,0); scrollBy(100,0); scrollBy(100,0); The positive x axis is on the right, right? This is exactly what confused me most when I first started learning Scroller. Again, it is the contents of the view that are being swiveled, not the view itself, and as we can see from the above example, the caller’s RelativeLayout is unchanged no matter how we call it. GetScrollX () returns the slide distance of the left edge of the view minus the X-axis of the left edge of its parent view.




scroolX.png

The first call to scrollBy(100,0) will give the view’s contents a distance of 100px from the left edge of the view. The test x-coordinate should be -100. Subtract the view content from the left X-axis of the view: 0- (-100) equals 100, which is exactly the relative distance of our slide. If we call it the second and third time, the X-axis of the view content will be -200, -300, and we should understand. And scrollTo is sliding to the absolute position of the view, so I’m passing the value 0,0, so it’s sliding to its starting position in the view.

About the principle of Scroller to achieve elastic sliding effect

The Scroller class, however, provides the following method:




startscroll.png

This method can provide us with the elastic sliding effect of sliding a certain distance in a certain period of time. The following is a typical use of it:

private void smoothScroll(int dx,int dy) { int deltaX = dx-getScrollX(); int deltaY = dx-getScrollX(); mScrooler.startScroll(getScrollX(),getScrollY(),deltaX,deltaY,1000); invalidate(); } @Override public void computeScroll() { if (mScrooler.computeScrollOffset()) { scrollTo(mScrooler.getCurrX(), mScrooler.getCurrY()); postInvalidate(); }}Copy the code

In particular, dx and dy represent not the terminal coordinates but the distance we slide, which we can easily conclude from the source code:

public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; Mdurationframe = 1.0f/(float) mDuration; }Copy the code

In the above smoothScroll method we finally called invalidate. Why is that? Since calling invalidate causes the view to be redrawn in the View’s draw method, we can see that it calls the computeScroll method that we have copied, which in turn calls the PostInvalidate method to redraw the view’s elastic sliding. But how does it slide a little bit according to our time? Let’s look at the computeScrollOffset() method:

Public Boolean computeScrollOffset() {if (mFinished) {return false; if (mFinished) {return false; } / / timerPassed is based on the current time minus the starting time to calculate the time value of the int timePassed = (int) (AnimationUtils. CurrentAnimationTimeMillis () - mStartTime);  If (timePassed < mDuration) {switch (mMode) {case SCROLL_MODE: final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal); mCurrX = mStartX + Math.round(x * mDeltaX); mCurrY = mStartY + Math.round(x * mDeltaY); break; case FLING_MODE: final float t = (float) timePassed / mDuration; final int index = (int) (NB_SAMPLES * t); float distanceCoef = 1.f; float velocityCoef = 0.f; if (index < NB_SAMPLES) { final float t_inf = (float) index / NB_SAMPLES; final float t_sup = (float) (index + 1) / NB_SAMPLES; final float d_inf = SPLINE_POSITION[index]; final float d_sup = SPLINE_POSITION[index + 1]; velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); distanceCoef = d_inf + (t - t_inf) * velocityCoef; } mCurrVelocity = velocityCoef * mDistance/mDuration * 1000.0f; mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX)); // Pin to mMinX <= mCurrX <= mMaxX mCurrX = Math.min(mCurrX, mMaxX); mCurrX = Math.max(mCurrX, mMinX); mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY)); // Pin to mMinY <= mCurrY <= mMaxY mCurrY = Math.min(mCurrY, mMaxY); mCurrY = Math.max(mCurrY, mMinY); if (mCurrX == mFinalX && mCurrY == mFinalY) { mFinished = true; } break; } } else { mCurrX = mFinalX; mCurrY = mFinalY; mFinished = true; } return true; }Copy the code

Through the analysis of the above code, we can find that Google is very clever in the design of Scroller, not using timer, but also achieved the effect of sliding within a certain period of time.

Use Scroller to achieve a slide menu

Through the above analysis of the use and principle of Scroller sliding, we must have a good understanding of the use of this class. Next, I will use this class to achieve a simple side slide menu, in order to achieve the purpose of application: the core code is as follows:

Public class SlideMenu extends ViewGroup {private int mLastx; Private Scroller mScrooler; Private Boolean mIsMain = true; private Boolean mIsMain = true; public SlideMenu(Context context) { this(context, null); } public SlideMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScrooler = new Scroller(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); View left = this.getChildAt(0); left.measure(left.getLayoutParams().width, heightMeasureSpec); View main = this.getChildAt(1); main.measure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { View left = this.getChildAt(0); left.layout(-left.getLayoutParams().width, 0, 0, b); View main = this.getChildAt(1); main.layout(l, t, r, b); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN : // getX returns the horizontal distance relative to the left edge of the current view,getRawx returns the distance relative to the left edge of the screen, MLastx = (int) event.getx (); mLastx = (int) event.getx (); break; case MotionEvent.ACTION_MOVE : int x = (int) event.getX(); Int deltaX = mlastx-x; int deltaX = mlastx-x; Int scrollX = getScrollX() + deltaX; If (scrollX > 0) {// If (scrollX > 0) {// scrollTo(0,0); } else if (scrollX < -getChildat (0).getwidth ()) {if (scrollX < -getChildat (0).getwidth ()) scrollTo(-this.getChildAt(0).getWidth(), 0); } else {// The menu is not fully hidden or fully realized. ScrollBy (deltaX, 0); } mLastx = x; break; ACTION_UP: int upX = getScrollX(); ACTION_UP: int upX = getScrollX(); If (upX > -this.getChildAt(0).getwidth () / 2) {mIsMain = true; } else {mIsMain = false; } switchScreen(); break; } return true; } private void switchScreen() { int startX = getScrollX(); // The initial x-offset is int deltaX; If (mIsMain) {// If (mIsMain) {// If (mIsMain) {deltaX = 0 - startX; } else {deltaX = -getChildAT (0).getLayOutParams ().width -startx; } mScrooler.startScroll(startX, 0, deltaX, 0, Math.abs(deltaX) * 3); invalidate(); } @Override public void computeScroll() { if (mScrooler.computeScrollOffset()) { scrollTo(mScrooler.getCurrX(), mScrooler.getCurrY()); postInvalidate(); } } public void switchMenu() { mIsMain = ! mIsMain; switchScreen(); }}Copy the code

There are only more than one hundred lines of code with comments. Through the above analysis and code comments, I believe it is easy to understand the implementation principle of the entire sideslip menu.

This article sample code address :github.com/tracyxia716…