Android custom ViewPager (1) -- custom Scroller to simulate the animation process

Source: Internet
Author: User

Android custom ViewPager (1) -- custom Scroller to simulate the animation process

 

I believe that the ViewPager component provided by the Android SDK is quite familiar to everyone, but ViewPager exists in support. in the v4 package, ViewPager does not exist in earlier android versions. How can we use sliding effects similar to ViewPager in earlier android versions? Here, we will continue to explore the custom components of andrid, and this blog post only explores some knowledge about android, rather than deliberately building a custom ViewPager for use, this is not necessary. please focus on the knowledge points to achieve this effect, so that you can "put it apart" in the future ".

Now, let's take a brief look at ViewPager. ViewPager can be viewed as a "Container". In this "container", various View types can be placed. For example, TextView and ImageView can be placed on each page of ViewPager, listView, GridView, and so on. In fact, the placement of these views on ViewPager can be seen as Layout various views on ViewGroup (in fact, this implementation is relatively complicated, so we can abstract it as that ViewPager is equivalent to ViewGroup and Layout various views on this ViewGroup. So in the following code, we mainly need a custom ViewGroup to achieve this effect. In addition, you also need to add a sliding effect to the View on each page on this ViewGroup to simulate the dynamic effect on ViewPager.

We need to carefully discuss the structure of custom ViewGroup. Some concepts are worth further understanding. For the convenience of understanding, please refer to the "sketch" below ":

From the above sketch, we can see that the red border represents the device screen, that is, the place we can see with the naked eye. The entire gray border represents the entire effect, which is called a "View ", each View is divided into three views. Three or more views form a large View. We need to figure out the relationship between the three, the display area of the device screen, that is, the range we can see on the device. View represents a single component, one or more views can be displayed on one screen, but views are the most confusing. A View is theoretically a large area, it not only includes a part of the device's screen that can be seen by the naked eye, but also contains a part that is invisible to the naked eye outside the device's screen, as shown in. Child View2 and child View3 are also part of the view, but outside the device screen, it is an area invisible to the naked eye. A View can store a lot of views, which are used to manage the Display Effect of the View. In addition, the view can be freely active. By controlling the view activity and controlling the display range of the view on the device screen, you can switch between different pages.

 

So what we will do next is how to customize a View and display different views on the device screen, you can use a custom ViewGroup to display multiple views on Android and enable onLayout to typeset the View. during initialization, I added 6 subviews TO THE ViewGroup, as shown in, the width and height of each View are the same as those of the parent View (ViewGroup). The first View is displayed on the screen, the other five sub-views are added in at the same time. The layout is hidden in the right area of the device screen in multiples of N of the width of the parent View. The following is the implementation code of custom ViewGroup:

 

Package com. example. myviewpager; import android. content. context; import android. util. attributeSet; import android. view. gestureDetector; import android. view. motionEvent; import android. view. view; import android. view. viewGroup; public class MyViewPager extends ViewGroup {/** gesture reader */private GestureDetector detector;/** Context */private Context ctx; /** coordinates of the X axis pressed for the first time */private int firstDownX;/** record the id of the current View */priv Ate int currId = 0;/** animation simulation tool */private MyScroller myScroller; public MyViewPager (Context context, AttributeSet attrs) {super (context, attrs); this. ctx = context; init ();} private void init () {myScroller = new MyScroller (ctx); detector = new GestureDetector (ctx, new GestureDetector. onGestureListener () {@ Overridepublic boolean onSingleTapUp (MotionEvent e) {return false ;}@ Overridepublic void onShowPress (M OtionEvent e) {}@ Overridepublic boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {// slide scrollBy (int) distanceX, 0 ); return false ;}@ Overridepublic void onLongPress (MotionEvent e) {}@ Overridepublic boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return false ;} @ Overridepublic boolean onDown (MotionEvent e) {return false ;}}) ;}/ *** subpair View layout. If the position of the sub-View is changed to true, * the layout is changed. l the position of the current View in the parent View */@ Overrideprotected void onLayout (boolean changed, int l, int t, int r, int B) {for (int I = 0; I <getChildCount (); I ++) {View view = getChildAt (I ); // It refers to the position of the stator View, left, top, right, and bottom. It refers to the position view in the ViewGroup coordinate system. layout (0 + I * getWidth (), 0, getWidth () + I * getWidth (), getHeight () ;}@ Overridepublic boolean onTouchEvent (MotionEvent event) {detec Tor. onTouchEvent (event); // specify the gesture identifier to process the sliding event. // you have to handle some logic switches by yourself. getAction () {case MotionEvent. ACTION_DOWN: // press firstDownX = (int) event. getX (); break; case MotionEvent. ACTION_MOVE: // move break; case MotionEvent. ACTION_UP: // lift int nextId = 0; // record the idif (event. getX ()-firstDownX> getWidth ()/2) {// the X axis coordinate of the finger exit point-firstDownX> half of the screen width, shifted left nextId = (currId-1) <= 0? 0: currId-1;} else if (firstDownX-event. getX ()> getWidth ()/2) {// the X axis coordinate of the finger exit point-firstDownX 

1. The above is all the source code of the custom ViewGroup. Next we will analyze the implementation process slowly. The first is to initialize the layout of each sub-View. As mentioned above, the main code is onLayout () method has been reflected, relatively simple.

2. Implement the gesture sliding effect. As we all know, ViewPager can change different pages as its fingers slide on the screen. To achieve the same effect, I have rewritten the onTouchEvent (MotionEvent) method of the parent class in the Custom ViewGroup, this method is used to process the logic of a sliding event. For the sake of convenience, I used the GestureDetector of the gesture recognition tool to handle the effect of moving the view along with the finger when the finger moves on the screen. It is simple in GestureDetector's onScroll () in the method, pass the moving distance to ScrollBy (int) as the parameter.

3. Switch the view when the fingers are pressed to the very complex lifting mode. This is a specific analysis process. The following is a sketch involved in this process:

Here, we use the View2 sub-View as an example to analyze three situations:

(1), the finger leaves the X axis coordinate of the point-the X axis coordinate of the Point pressed by the finger> half of the screen width, move left, the screen shows the next View

(2) The finger leaves the X axis coordinate of the point-the X axis coordinate of the Point pressed by the finger

(3) If neither of the preceding conditions is met, the system will stay on the current View without switching between the frontend and backend views.

4. Through the (3) process, we will know which View the current View is moving to and get the id of the next View to be displayed, set this id to the id of the current View, and then pass the id * View width of the next View to be displayed to ScrollTo (int, 0) as the parameter, to control the movement of views.

5. After performing the preceding steps, the switch of the View is completed. However, the ScrollTo (int, int) method is used for switching between the left and right sides of the View, this method moves the View directly to the specified position, but the entire process of moving is too fast, and the View switching is completed in an instant, so the experience is very poor, so how can we improve the experience? By the way, the View is switched to a slow process, so that the View switching process is slow or at a constant speed. In this way, the experience will be improved, so how can we add a uniform switching effect during the switching process? Let's take the following small example for your convenience:

For example, if A person wants to walk A 100-meter path, he can walk slowly. He can also run A lot of times, and the time is very short, but what should he do if he wants to finish the path at a constant speed? At this time, he found an engineer, Mr. B, and asked the engineer, Mr. B, to help him calculate the distance. Prior to moving forward, Mr. A asked the engineer, Mr. B. How many meters will I walk in the next five seconds? Engineer B started to calculate the result and told Mr. A that you should move forward for 10 meters first. When Mr. A finished the 10-meter journey, Mr. A asked Mr. B again, how many meters will I move forward in the next five seconds? Small B calculates and tells small A that it is better to move forward 20 meters, so small A continues to move forward 20 meters, stops and then asks Small B ...... after repeating this process, I know that John has finished the 100-meter path.

The above example is not hard to understand! Therefore, during the View switching process, we also need such an "engineer" to calculate the displacement during each certain interval and pass it to the View, immediately move to the corresponding position, and request the "engineer" to calculate the shift forward in the next interval, and so on. Below is a customized tool for calculating the displacement source code:

 

Package com. example. myviewpager; import android. content. context; import android. OS. systemClock;/*** tool class for calculating the view offset ** @ author Administrator **/public class MyScroller {/** X coordinate at the beginning */private int startX; /** Y coordinate at the start */private int startY;/** the distance from X to be moved */private int distanceX; /** the distance to be moved in the Y direction */private int distanceY;/** start time */private long startTime;/** whether to stop moving */private boolean isFinish; /** coordinates of the current X axis */private long currX;/** coordinates of the current Y axis */private long currY; /** default interval */private int duration = 500; public MyScroller (Context ctx) {}/*** start to move ** @ param startX * start X coordinate * @ param startY * Start Y coordinate * @ param distanceX * to move distance from * @ param distanceY * Y to be moved */public void startScroll (int startX, int startY, int distanceX, int distanceY) {this. startX = startX; this. startY = startY; this. distanceX = distanceX; this. distanceY = distanceY; this. startTime = SystemClock. uptimeMillis (); this. isFinish = false;}/*** determine the current running status ** @ return */public boolean computeOffset () {if (isFinish) {return false ;} // obtain the time used long passTime = SystemClock. uptimeMillis ()-startTime; System. out. println (passTime: + passTime); // if the time is still within the permitted range if (passTime <duration) {currX = startX + distanceX * passTime/duration; currY = startY + distanceY * passTime/duration;} else {currX = startX + distanceX; currY = startY + distanceY; isFinish = true;} return true ;} /*** get the current X value ** @ return */public long getCurrX () {return currX;} public void setCurrX (long currX) {this. currX = currX;}/*** get the current Y value ** @ return */public long getCurrY () {return currY;} public void setCurrY (long currY) {this. currY = currY ;}}
Analyze the process.

 

When we calculate the switch to the next View id, we can get the switch distance. formula: the distance to be moved = the final location-the current location; after obtaining the moving distance, get the distance and initial location and tell the "engineer"-tool class MyScroller. At this time, the calculation can begin. The initialization code is as follows:

 

// Distance to be moved = final location-current location int distanceX = currId * getWidth ()-getScrollX (); // set the runtime myScroller. startScroll (getScrollX (), 0, distanceX, 0); // refresh the invalidate () view ();
After initializing the computing tool class, you need to refresh the current view and call the invalidate () method. This method will undergo a series of chain reactions. In fact, refreshing the view is a very complicated process, I will not explain it here until the computeScroll () method is triggered. At this time, we need to rewrite the computeScroll () method of the parent class to complete some operations in this method:

 

 

/*** Invalidate (); will cause execution of this method */@ Overridepublic void computeScroll () {if (myScroller. computeOffset () {int newX = (int) myScroller. getCurrX (); System. out. println (newX: + newX); scrollTo (newX, 0); invalidate ();}}

In this method, call the computeOffset () method of the tool class to calculate the displacement. This method first checks whether the view movement is completed. If the view is completed, false is returned. If the view is not completed, first, obtain the time interval of the motion. If the time interval of the current motion is within the total time interval duration, the actual position of the view to be moved after the time interval is calculated through the time interval. The formula is:Start position + total distance/total time * Moving interval of the current segmentIf the time interval of the current motion exceeds the total time interval, calculate the last position directly. formula:Start position + moving distance. GetCurrX is used to get the distance of the current displacement, that is, the newest displacement distance. The scrollTo (int, int) method is called to move the view to the new position. Finally, call invalidate () recursively to refresh the current view, trigger the computeScroll () method, and continue the above steps until the specified interval is exceeded. If false is returned, the view's displacement process ends.

In addition, in this example, I customized a myscroler tool class to calculate the displacement, which is time-consuming and laborious. It is feasible as a learning principle, but in actual development, you can use Android to provide us with a similar and extremely simple Helper class. You can use this Helper class to calculate the displacement. This class is

 

android.widget.Scroller; 

 

The following are related methods of the scroroller class:

Mscroroller. getCurrX ()// Obtain the current horizontal scroll position of mScroller
Mscroroller. getCurrY ()// Obtain the current vertical scroll position of mScroller
MScroller. getFinalX ()// Obtain the horizontal position of the final stop of mScroller
Mscroroller. getFinalY ()// Obtain the vertical position of the final stop of mScroller
Mscroroller. setFinalX (int newX)// Set the horizontal position of the mscroeller to stay at. If there is no animation effect, Jump directly to the target position.
Mscroroller. setFinalY (int newY)// Set the vertical position where the mScroller stays. If there is no animation effect, Jump directly to the target position.
Mscroroller. startScroll (int startX, int startY, int dx, int dy)// Scroll. startX and startY are the starting position of the scroll, and dx and dy are the offset of the scroll.
Mscroroller. startScroll (int startX, int startY, int dx, int dy, int duration)// Scroll. startX and startY are the positions where the rolling starts, dx and dy are the offset of the rolling, and duration is the time when the rolling is completed.
Mscroroller. computescroloffset ()// The return value is boolean. true indicates that the rolling is not completed. false indicates that the rolling is completed. This is a very important method. It is usually placed in View. computeScroll () to determine whether the rolling process ends.

The specific usage of Scroller has been used in my previous blog. Please go to the Android custom control-slide menu to view the relevant source code.

 

 

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.