Android scroller Complete Parsing _android

Source: Internet
Author: User
Tags abs touch xdiff

In Android, any control can be scrolled because there are scrollto () and Scrollby () methods in the view class, as shown in the following illustration:

The primary role of the two methods is to move the view/viewgroup to the specified coordinates and save the offset. Other than that:

Mscrollx represents the offset coordinates of the x-axis direction
mscrolly represents the offset coordinates of the Y axis direction

Both of these methods are used to scroll through the view, so what's the difference between them? To put it simply, the Scrollby () method is to have the view scroll a certain distance relative to the current position, while the Scrollto () method scrolls the view away from its original position.

About the offset of the settings we can refer to the source code:

public class View { 
  .... 
  protected int mscrollx; The view content corresponds to the offset of the starting coordinate of the view, the x-axis direction   
  protected int mscrolly;//The view content corresponds to the offset of the starting coordinate of the view, the y-axis direction 
  //return value public 
  final int Getscrollx () {return 
    mscrollx; 
  } 
  Public final int getscrolly () {return 
    mscrolly; 
  } 
  The public void Scrollto (int x, int y) { 
    //offset position has changed 
    if (mscrollx!= x | | | mscrolly!= y) { 
      int oldx = MSCROLLX; 
      int oldy = mscrolly; 
      MSCROLLX = x; Assign a new value, save the current cheap amount 
      mscrolly = y; 
      Callback Onscrollchanged Method 
      onscrollchanged (MSCROLLX, mscrolly, OLDX, oldy); 
      if (!awakenscrollbars ()) { 
        invalidate ();///generally cause redrawing 
      }} 
    / 
  / 
  See the difference.  Mscrollx and mscrolly represent the position of our current offset, and continue to offset at the current position (x, y) units public 
  void Scrollby (int x, int y) { 
    Scrollto (mscrollx + x, mscrolly + y); 
  } 
  //... 
} 

So, at any moment, we can get the offset of the view/viewgroup, that is, call the Getscrollx () method and the Getscrolly () method.

Let's take a look at the difference between them:

<?xml version= "1.0" encoding= "Utf-8"?> <linearlayout xmlns:android=
"http://schemas.android.com/apk/" Res/android "
  xmlns:tools=" Http://schemas.android.com/tools "
  android:id=" @+id/layout "
  android: Layout_width= "Match_parent"
  android:layout_height= "match_parent"
  android:orientation= "vertical" >

  <button
    android:id= "@+id/scroll_to_btn"
    android:layout_width= "Wrap_content"
    android: layout_height= "Wrap_content"
    android:text= "Scrollto"/> <button android:id=
    "@+id/scroll_by" _btn "
    android:layout_width=" wrap_content "
    android:layout_height=" wrap_content "
    android:layout_" margintop= "10DP"
    android:text= "Scrollby"/>
</LinearLayout>

The outer layer uses a linearlayout that contains two buttons, one for triggering scrollto logic and one for triggering scrollby logic.

public class Mainactivity extends Appcompatactivity {private linearlayout layout;
  Private Button scrolltobtn;

  Private Button scrollbybtn;
    @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);
    Setcontentview (R.layout.activity_main);
    Layout = (linearlayout) Findviewbyid (r.id.layout);
    SCROLLTOBTN = (Button) Findviewbyid (R.ID.SCROLL_TO_BTN);
    SCROLLBYBTN = (Button) Findviewbyid (R.ID.SCROLL_BY_BTN); Scrolltobtn.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) {LA Yout.scrollto (Getresources (). Getdimensionpixeloffset (R.dimen.horizontal_scroll), Getresources (). getDimensionPi
      Xeloffset (R.dimen.horizontal_scroll));
    }
    }); Scrollbybtn.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) {LA Yout.scrollby (Getresources (). Getdimensionpixeloffset (r.dimen.horizontal_scroll), GETresources (). Getdimensionpixeloffset (R.dimen.horizontal_scroll));
  }
    }); } <resources> <dimen name= "Horizontal_scroll" >-20dp</dimen> <dimen name= "Vertical_scroll"

 ;-30dp</dimen> </resources>

When the Scrollto button is clicked, we call the LinearLayout Scrollto () method, and when the Scrollby button is clicked, the LinearLayout () method is invoked. Some friends may ask, why are all the scroll methods in the linearlayout of the call? It is important to note here that either the Scrollto () or the Scrollby () method, scrolling is the content inside the view, and LinearLayout is the content of our two button, if you directly call the button's scroll method, The result must not be what you want to see.

Another point to note is that the arguments passed in in the two scroll methods, the first parameter x represents the distance that is moved horizontally relative to the current position, the positive value moves to the left, and the negative value moves to the right. The second argument y represents the distance that is moved vertically relative to the current position, the positive value moves upward, and the negative value moves downward.
Run the program:

When we click on the Scrollto button, two buttons will scroll down to the right, then click the Scrollto button will have no effect, the interface will not continue to scroll, only click on the Scrollby button interface will continue to scroll, And constantly click the Scrollby button interface will scroll down together.

Scroller class

From the above example, the results show that the Scrollto ()/scrollby () method is used to offset a view to the specified coordinates (X,Y), the whole process is a direct jump, without any control of the migration process, the user is not very friendly. Thus, based on this migration control, the Scroller class is designed, and the main function of the class is to develop a certain control flow for the migration process, thus making the offset smoother and more perfect.
We analyze the source code to see the relevant methods of the Scroller class, the source (part) is as follows: The path is located in the \frameworks\base\core\java\android\widget\scroller.java

public class Scroller {private int mstartx;  Starting coordinate point, X axis direction private int mstarty;   Starting coordinate point, Y axis direction private int mcurrx;   The current coordinate point x axis, that is, the call Startscroll function, after a certain amount of time to achieve the value of private int mcurry; The current coordinate point Y axis, that is, call the Startscroll function, after a certain amount of time to achieve the value of private float mdeltax; Should continue to slide the distance, the x axis direction private float Mdeltay; Should continue to slide the distance, Y axis direction private Boolean mfinished; 
  Whether this slide operation has been completed, or true//constructor public scroller (context, NULL) if completed; 
  Public Final Boolean isfinished () {return mfinished; 
  //force End of this slide screen operation public final void Forcefinished (Boolean finished) {mfinished = finished; 
  Public final int Getcurrx () {return mcurrx; }/* Call this when you are want to know the new location. If it returns True, * The animation is not yet finished. Loc would be altered to provide the * new location. Draw control, return directly to FAlse return false; 
    int timepassed = (int) (Animationutils.currentanimationtimemillis ()-mstarttime); if (timepassed < mduration) {switch (mmode) {case Scroll_mode:float x = (float) timepassed * m 
        durationreciprocal; 
        ... mcurrx = Mstartx + math.round (x * mdeltax); 
        Mcurry = Mstarty + math.round (x * mdeltay); 
      Break 
    ... 
    } 
      else {Mcurrx = Mfinalx; 
      Mcurry = Mfinaly; 
    Mfinished = true; 
  return true; //Start an animation control, by (StartX, Starty) in duration time forward (Dx,dy) units, that is, the arrival coordinates (STARTX+DX, starty+dy) out of public void startscroll (int s 
    TARTX, int starty, int dx, int dy, int duration) {mfinished = false; 
    mduration = Duration; 
    Mstarttime = Animationutils.currentanimationtimemillis ();    Mstartx = StartX; 
    Mstarty = Starty; MFINALX = startx + dx; 
    Mfinaly = Starty + dy;      Mdeltax = DX; 
    Mdeltay = dy; 
... 
  } 

 }

Among the more important two methods are:

public boolean Computescrolloffset ()
Function Description: Calculates the current coordinate point according to the current elapsed time, and holds it in the Mcurrx and Mcurry values.

public void Startscroll (int startx, int starty, int dx, int dy, int duration)
Function Description: Start an animation control, by (StartX, Starty) in the duration time forward (Dx,dy) units, to reach the coordinates (STARTX+DX, starty+dy) place.

Computescroll () Method Introduction:
for easy control of sliding screen control, the Android framework provides a computescroll () method to control this process. When you draw a view, the method is called in the Draw () procedure. So, in conjunction with the Scroller instance, we can get the current offset coordinates and manually offset the view/viewgroup to that location.
The Computescroll () method is based on the following method, which is located in the Viewgroup.java class

/** 
   * Called by a parent to request this a child update its values for MSCROLLX and mscrolly if necessary. This would typically be done if the animating a scroll using a {@link android.widget.Scroller scroller} 
   * Obje Ct. 
   * is invoked by the parent view to request a child view to mscrollx,mscrolly the offset value
  /public void Computescroll () {//null method, custom ViewGroup must implement the method body     
  } 

In order to achieve the offset control, the general custom View/viewgroup needs to overload the method. Its calling procedure is in the view drawing process draw (), as follows:

@Override 
protected void Dispatchdraw (Canvas Canvas) { 
  ... 

  for (int i = 0; i < count; i++) { 
    final View child = Children[getchilddrawingorder (count, i)]; 
    if ((Child.mviewflags & visibility_mask) = = VISIBLE | | child.getanimation ()!= null) {More 
      |= drawchild (canvas, C Hild, drawingtime); 
    } 
  } 
Protected Boolean drawchild (Canvas Canvas, View Child, Long drawingtime) { 
  ... 
  Child.computescroll (); 
  ... 
} 

Example Demo

Viewpager believe that everyone is familiar with, so it is too often, we can use the Viewpager to easily complete the sliding between the page switch effect, but if asked how it is achieved, I feel that most people are still relatively unfamiliar. In fact, when it comes to viewpager the basic principle of implementation is mainly two parts, one is the distribution of events, one is scroller. For event distribution, you can refer to my blog about the distribution, interception and execution of Android events.
Next I will implement a simple version of Viewpager in conjunction with event distribution and scroller. First customize a viewgroup, do not understand can refer to the Android custom ViewGroup (i) customgridlayout this article. The main practices for smoothing offsets are as follows:

First, invoke the scroller instance to produce an offset control (corresponding to the Startscroll () method)
Second, manually call the invalid () method to repaint, and the rest is to get the coordinates that should be offset (calculated by Computescrolloffset () corresponding to the Scroller instance) based on the current elapsed time in Computescroll ().
Third, the current should offset the coordinates, call the Scrollby () method to slowly move to the coordinates.

Create a new scrollerlayout and let it inherit from ViewGroup as our simple viewpager layout, the code looks like this:

public class Scrollerlayout extends ViewGroup {private scroller mscroller;//To complete an instance of a scrolling operation private Velocitytracker MV Elocitytracker = null; Rate of handling touch public static int snap_velocity = 600;      Minimum sliding rate private int mtouchslop = 0;  Minimum sliding distance, more than, just think start sliding private float Mlastionmotionx = 0; Screen coordinates for the last time the Action_move event was triggered private int curscreen = 0;  Current screen private int leftborder; The interface can be scrolled to the left edge of the private int rightborder; Interface scrollable right boundary//two states: whether in a sliding state private static final int touch_state_rest = 0; I didn't do anything. State private static final int touch_state_scrolling = 1; Start the state of the slide screen private int mtouchstate = Touch_state_rest;
    The default is nothing to do state public scrollerlayout (context, AttributeSet attrs) {Super (context, attrs);
    Create an instance of scroller Mscroller = new Scroller (context);
  Initializes a minimum sliding distance of Mtouchslop = viewconfiguration.get (context). Getscaledtouchslop (); @Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (wIdthmeasurespec, Heightmeasurespec);
    int childcount = Getchildcount ();
      for (int i = 0; i < ChildCount i++) {View Childview = Getchildat (i);
    Measure the size of each child control in the Scrollerlayout measurechild (Childview, Widthmeasurespec, Heightmeasurespec); } @Override protected void OnLayout (Boolean changed, int l, int t, int r, int b) {if (changed) {int C
      Hildcount = Getchildcount ();
        for (int i = 0; i < ChildCount i++) {View Childview = Getchildat (i); Layout horizontally for each child control in the Scrollerlayout childview.layout (i * childview.getmeasuredwidth (), 0, (i + 1) * Childview.get
      Measuredwidth (), Childview.getmeasuredheight ());
    }///Initialize the left and right boundary value Leftborder = Getchildat (0). GetLeft ();
  Rightborder = Getchildat (Getchildcount ()-1). GetRight ();
    @Override public boolean onintercepttouchevent (Motionevent ev) {final int action = Ev.getaction ();
    The expression has started to slide and does not need to go to the Action_move method (which may be invoked the first time). This method is mainly used for users to quickly release their handsrefers to the act of quickly pressing.
    This is considered to be in a sliding state.
    if (action = = motionevent.action_move) && (mtouchstate!= touch_state_rest)) {return true;
    Final float x = Ev.getx ();
        Switch (action) {case MotionEvent.ACTION_MOVE:final int xdiff = (int) math.abs (mlastionmotionx-x);
        When the minimum sliding distance is exceeded, it is assumed that the Xdiff > mtouchslop {mtouchstate = touch_state_scrolling is started to slide;
      } break;
        Case MotionEvent.ACTION_DOWN:mLastionMotionX = x; Mtouchstate = mscroller.isfinished ()?
        touch_state_rest:touch_state_scrolling;
      Break
        Case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:mTouchState = touch_state_rest;
    Break
  return mtouchstate!= touch_state_rest;
    public boolean ontouchevent (Motionevent event) {super.ontouchevent (event);
    Gets the Velocitytracker object and adds a sliding object if (Mvelocitytracker = = null) {Mvelocitytracker = Velocitytracker.obtain (); MVElocitytracker.addmovement (event);
    Touch Point Float x = Event.getx (); 
        Switch (event.getaction ()) {case Motionevent.action_down://If the screen animation is not finished, you press, we end the last animation, that is, to start the new Action_down animation
          if (Mscroller!= null) {if (!mscroller.isfinished ()) {mscroller.abortanimation (); } Mlastionmotionx = x;
      Remember the start of the screen Point break; Case MotionEvent.ACTION_MOVE:int detax = (int) (MLASTIONMOTIONX-X); 
          Each time you slide the screen, the screen should move the distance if (GETSCROLLX () + Detax < Leftborder) {//Prevent the user from pulling out of the boundary there is also a special border protection, which calls the Scrollto () method to return to the boundary position when the boundary is dragged
          Scrollto (leftborder, 0);
        return true;
          else if (GETSCROLLX () + getwidth () + detax > Rightborder) {scrollto (Rightborder-getwidth (), 0);
        return true; Scrollby (detax, 0);//start slowly sliding the screen slightly.
        Detax > 0 slide to the right, Detax < 0 to the Left Mlastionmotionx = x;
      break; Case MotionEvent.ACTION_UP:final Velocitytracker veLocitytracker = Mvelocitytracker;
        Velocitytracker.computecurrentvelocity (1000);
        COMPUTE rate int Velocityx = (int) velocitytracker.getxvelocity (); The sliding rate reached a standard (quickly slide to the right, return to the previous screen) and immediately cut screen processing if (Velocityx > snap_velocity && curscreen > 0) {/
        /fling enough to move left Snaptoscreen (curScreen-1);
          //Fast to Zoshi, return next screen else if (Velocityx <-snap_velocity && Curscreen < (Getchildcount ()-1)) {
        Snaptoscreen (Curscreen + 1);
        }//above for fast moving, Force toggle screen else{//We are moving slowly, so first decide whether to keep on this screen or to the next screen snaptodestination ();
          ///Recycle Velocitytracker object if (mvelocitytracker!= null) {mvelocitytracker.recycle ();
        Mvelocitytracker = null;
        }//correction mtouchstate value mtouchstate = touch_state_rest;
      Break
        Case MotionEvent.ACTION_CANCEL:mTouchState = touch_state_rest;
    Break
 }   return true; //We are moving slowly, so we need to determine which private void snaptodestination () The target screen is based on the offset value () {//To determine whether to exceed the middle position of the next screen, if reached to the next screen, otherwise remain in the original screen//formula means
    : Assume that the current slide offset value is Scrollcurx plus half the width of each screen, divided by the width of each screen is the location of our target screen.
    int destscreen = (GETSCROLLX () + getwidth ()/2)/getwidth ();
  Snaptoscreen (Destscreen); //True implementation of the method of the jump screen private void snaptoscreen (int whichscreen) {//simple move to the target screen, may be the current screen or the next screen, directly jump past, not very friendly, for the sake of friendliness, we are adding an animation
    Effect Curscreen = Whichscreen;
    Prevents the screen from crossing over, that is, exceeding the screen number if (Curscreen > Getchildcount ()-1) Curscreen = Getchildcount ()-1;
    In order to reach the next screen or the current screen, we need to continue to slide the distance. Depending on the DX value, it is possible to slide to the left or to the right by sliding int dx = Curscreen * getwidth ()-GETSCROLLX ();
    Mscroller.startscroll (GETSCROLLX (), 0, DX, 0, Math.Abs (DX) * 2);
  Because the touch event does not redraw view, it is not effective to manually refresh the view at this time invalidate (); @Override the public void Computescroll () {//rewrite the Computescroll () method and complete the logical if smooth scrolling within it (Mscroller.computescrolloff
      Set ()) {Scrollto (Mscroller.getcurrx (), Mscroller.getcurry ()); InvalIdate ();

 }
  }
}

The code is relatively long, but the idea is clearer.
(1) First of all, we create an instance of Scroller in the Scrollerlayout constructor, because the scroller instance only needs to be created once, so we put it into the constructor to execute. Also in the constructor we initialize the value of the Touchslop, which is used later to determine whether the current user's operation is dragging.
(2) then rewrite the Onmeasure () method and the OnLayout () method to measure the size of each child control in Scrollerlayout in the Onmeasure () method, in OnLayout () Each child control in the scrollerlayout is laid horizontally in the method, and the layout is similar to the linearlayout in the direction of horizontal.
(3) then rewrite the Onintercepttouchevent () method, in which we record the x-coordinate position of the user's finger when it is pressed, and the x-coordinate of the user's finger as it drags on the screen, when the distance between the two is greater than the Touchslop value, It is assumed that the user is dragging the layout, the state is touch_state_scrolling, when the user's finger is lifted, the reset state is touch_state_rest. This returns True when the status value is Touch_state_scrolling, intercepts the event here, and prevents the event from being passed into the child control.
(4) Then when we intercept the event, we will give the event to the Scrollerlayout ontouchevent () method to handle it.
If the current event is action_move, which means that the user is dragging the layout, then we should scroll the layout content to affect the drag event by using the Scrollby () method We have just learned, and how much scrollby the user drags here. In addition to preventing the user from dragging out the boundary, there is also a special border protection, which is called Scrollto () method to return to the boundary position when dragging out the boundary.
If the current event is action_up, the user's finger is lifted, but there is a good chance that the user will simply drag the layout to the middle, and we won't be able to let the layout stay in the middle, so we need to use the scroller to do the following scrolling. First, the rolling rate is calculated to determine whether the current action is scroll or fling. If it is fling, then jump to the previous page or the next page according to the direction of fling and call the function Snaptoscreen. If it is scroll, call function Snaptodestination, which first calculates how the layout should continue to scroll to which page, and scroll to which page to call Snaptoscreen, based on the current scrolling position. Look at the Snaptoscreen, in fact, call the Startscroll () method to scroll the data, followed by the call Invalidate () method to refresh the interface.
(5) rewrite the Computescroll () method and complete the logic of smooth scrolling within it. The Computescroll () method is always invoked throughout the subsequent smooth scrolling process, so we need to constantly invoke the Scroller Computescrolloffset () method to determine if the scrolling operation is complete, and if it is not done, Then continue to invoke the Scrollto () method and pass in the scroller CurX and Cury coordinates, then refresh the interface to complete the smooth scrolling operation.

Now that the scrollerlayout is ready, we'll modify the contents of the Activity_main.xml layout as follows:

<?xml version= "1.0" encoding= "Utf-8"?> <com.hx.scroller.scrollerlayout xmlns:android=
"http://" Schemas.android.com/apk/res/android "
  android:layout_width=" match_parent "
  android:layout_height=" Match_ Parent ">

  <imageview
    android:layout_width=" match_parent "
    android:layout_height=" 200DP
    " android:background= "@drawable/crazy_1"/>

  <imageview
    android:layout_width= "Match_parent
    " android:layout_height= "200DP"
    android:background= "@drawable/crazy_2"/>

  <imageview
    android : layout_width= "match_parent"
    android:layout_height= "200DP"
    android:background= "@drawable/crazy_3" >

</com.hx.scroller.ScrollerLayout>

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

Related Article

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.