[Quoted from MrXI's blog] 1. Generation of sliding effect Sliding a View is essentially the same as moving a View. Changing the coordinates of the current View is similar to animation in that the coordinates are constantly changed. To achieve the sliding of a View, you must listen to the sliding event and dynamically and constantly change the coordinates of the View according to the coordinates passed in by the event, so that the View can slide along with the user's touch. (1) Android coordinate system In Android, the vertex at the upper left corner of the screen is used as the origin of the Android coordinate system. From this point to the right is the positive direction of the X axis, and from this point downward is the positive direction of the Y axis, as shown in the following figure: The system provides methods such as getLocationOnScreen(int location[]) to obtain the position of the center point in the Android coordinate system, that is, the coordinates of the upper left corner of the view in the Android coordinate system. The coordinates obtained by using the getRawX() and getRawY() methods in touch events are also coordinates in the Android coordinate system. (2) View coordinate system In addition to the coordinate system mentioned above, Android also has a view coordinate system, which describes the positional relationship of the child view in the parent view. These two coordinate systems are neither contradictory nor complicated, and their functions complement each other. Similar to the Android coordinate system, the view coordinate system also takes the origin to the right as the positive direction of the X axis and the origin downward as the positive direction of the Y axis, but in the view coordinate system, the origin is no longer the upper left corner of the screen in the Android coordinate system, but the upper left corner of the parent view as the coordinate origin, as shown in the following figure: In a touch event, the coordinate system obtained by getX() and getY() is the coordinate system in the view coordinate system. (3) Touch event - MotionEvent The touch event MotionEvent plays an important role in user interaction. First, let's look at some common event constants encapsulated by MotionEvent, which define different types of touch events.
Usually, the type of touch event is obtained through the event.getAction() method in the onTouchEvent(MotionEvent event) method, and the switch-case method is used to filter it. The pattern of this code is basically fixed:
When multi-point operation is not involved, the above code can usually be used to complete the monitoring of touch events. In Android, the system provides a lot of methods to obtain coordinate values, relative distances, etc. It is good to have a variety of methods. The following is a summary of the coordinate system API, as shown below: These methods can be divided into two categories:
2. Seven ways to achieve sliding After understanding the Android coordinate system and touch events, let's take a look at how to use the API provided by the system to dynamically modify a View coordinate, that is, a real-time sliding effect. Regardless of which method is used, the idea of implementation is basically the same. When you touch the View, the system records the coordinates of the current touch point. When the finger moves, the system records the coordinates of the touched point after the move, thereby obtaining the offset relative to the previous coordinate point, and modifying the coordinates of the View by the offset. This is repeated continuously to achieve the sliding process. Let's take a look at an example to see how to implement the sliding effect in Android. Define a View in LinearLayout to implement a simple layout:
Our goal is to make this custom View slide as the finger slides on the screen. Display effect during initialization: (1) Layout method When the View is drawn, the onLayout() method is called to set the display position. Similarly, the coordinates of the View can be controlled by modifying the left, top, right, and bottom properties of the View. As with the template code provided above, each time the onTouchEvent is called back, we get the coordinates of the touch point. The code is as follows:
Next, record the coordinates of the touch point in the Action_DOWN event, as follows:
***, you can calculate the offset in the Action_MOVE event and apply the offset to the Layout method. Based on the current Layout's left, top, right, and bottom, add the calculated offset. The code is as follows:
In this way, after moving, View will call the Layout method to rearrange itself, thereby achieving the effect of moving the View. In the above code, getX() and getY() methods are used to obtain coordinate values, that is, to obtain the offset through view coordinates. Of course, you can also use getRawX() and getRawY() to obtain coordinates and use absolute coordinates to calculate the offset. The code is as follows:
When using the absolute coordinate system, there is one thing that needs to be paid attention to. After each execution of the ACTION_MOVE logic, the initialization coordinates must be reset to accurately obtain the offset. (2), offsetLeftAndRight() and offsetTopAndBottom() This method is equivalent to a system-provided encapsulation of the left-right, up-and-down movement API. After calculating the offset, you only need to use the following code to complete the View's re-layout, and the effect is the same as using the Layout method. The code is as follows:
The offsetX and offsetY here are the same as the offset calculation method in the layout method. (3) LayoutParams LayoutParams stores the layout parameters of a View. Therefore, you can dynamically modify the position parameters of a layout by changing LayoutParams in the program, thereby changing the position of the View. We can easily use getLayoutParams() in the program to get the LayoutParams of a View. Of course, the method of calculating the offset is the same as calculating the offset in the Layout method. After obtaining the offset, you can change its LayoutParams through setLayoutParams. The code is as follows:
When getLayoutParams() gets LayoutParams, you need to set different types according to the type of the parent layout of the View. For example, if the View is placed in LinearLayout, you can use LinearLayout.LayoutParams. If it is in RelativeLayout, you must use RelativeLayout.LayoutParams. The premise of all this is that you must have a parent layout, otherwise the system cannot get LayoutParams. When changing the position of a View by changing LayoutParams, the Margin property of the View is usually changed. Therefore, in addition to using the layoutParams of the layout, you can also use ViewGroup.MarginLayoutParams to implement this function. The code is as follows:
We can find that it is more convenient to use ViewGroup.MarginLayoutParams. There is no need to consider the type of parent layout. Of course, their essence is the same. (4) scrollTo and scrollBy In a View, the system provides two methods, scrollTo and scrollBy, to change the position of a View. The difference between these two methods is very easy to understand, similar to the difference between To and By in English. scrollTo(x,y) means moving to a specific coordinate point (x,y), while scrollBy(dx,dy) means moving by increments of dx, dy. Similar to the previous methods, scrollBy is used to move the View after obtaining the offset. The code is as follows:
However, when we drag the View, you will find that the View does not move. In fact, the method is correct. The View does move, but what moves is not what we want. The scrollTo and scrollBy methods move the content of the View. If the scrollTo and scrollBy methods are used in ViewGroup, all sub-Views will be moved. If they are used in View, the content of the View will be moved. For example, the content of a TextView is its text, and the content of an ImageView is its drawable object. Through the above analysis, we now know why we can no longer use these two methods to drag the View. Then we can use the scrollBy method in the ViewGroup where the View is located to move its child View. The code is as follows:
But when you drag the View again, you will find that although the View has moved, it is moving around randomly, and it is not moving with the touch point as we want. Let's take a look at the view movement here. Imagine that the mobile phone screen is a hollow cover, and under the cover is a huge canvas, which is the view we want to display. When this cover is placed on a certain part of the canvas, we can see the view displayed on the mobile phone screen through the empty rectangle in the middle, while the views in other places on the canvas are covered by the cover and cannot be seen. Our view is very similar to this example. Just because we don't see the view does not mean that it does not exist. It may just be outside the screen. When the scrollBy method is called, you can imagine that the cover outside is moving. This is a bit abstract. The rectangle in the middle of the first figure below is equivalent to the screen and the visible area. The content behind it is equivalent to the canvas, representing the view. It can be seen that only the middle part of the view is currently visible, and the rest is invisible. In the visible area, we set a Button with coordinates (20,10). Next, use the scrollBy method to translate the cover (screen, visible area) horizontally by 20 degrees in the positive direction of the X axis (to the right) and vertically by 10 degrees in the positive direction of the Y axis (downward). The visible area after translation is as shown in Figure 2. Figure 1 Figure 2: Visible area after moving We found that although scrollBy(20,10) is set, the offset is a positive number in the positive direction of the X-axis and Y-axis, but in the visible area of the screen, the Button moves in the negative direction of the X-axis and Y-axis. This is because different reference systems are selected, resulting in different effects. From the above analysis, we can find that if the parameters dx and dy in scrollBy are set to positive numbers, the content will move in the negative direction of the coordinate axis. If the parameters dx and dy in scrollBy are set to negative numbers, the content will move in the positive direction of the coordinate axis. Therefore, returning to the previous example, to achieve the effect of sliding with the movement of the finger, the offset must be changed to a negative value. The code is as follows:
Now run it once and you will find that the effect is the same as the previous methods. Similarly, when using absolute coordinates, you can also achieve this effect by using the scrollTo method. (5) Scroller Having mentioned the scrollBy and scrollTo methods, we have to talk about the Scroller class. The Scroller class is very similar to the scrollBy and scrollTo methods. What's the difference? Let's look at an example first. If you want to achieve such an effect; by clicking a button, let a sub-View of a ViewGroup move 100 pixels to the right. The problem seems simple. Just use the previous scrollBy method to set the offset in the button click event? Indeed, this can make a sub-View in a sub-ViewGroup translate, but no matter whether you use the scrollBy or scrollTo method, the translation of the sub-view occurs instantly. The translation is completed when the event is executed. This effect will make people feel very sudden. Google recommends using natural transition animation to achieve the moving effect. Therefore, the Scroller class was born. The Scroller class can achieve a smooth moving effect instead of completing the movement instantly. The implementation principle of the Scroller class is actually similar to the scrollTo and scrollBy methods used above to implement the child View following the finger movement. Although the scrollBy method allows the child View to move from one point to another instantly, the tiny offset of the finger movement is continuously obtained in the ACTION_MOVE event, so a distance is divided into N very small offsets. Although each offset is moved instantly through the scrollBy method, a smooth movement effect can be obtained as a whole. This principle is basically similar to the implementation principle of animation, and they both use the visual persistence characteristics of the human eye. Next, we use the Scroller class to achieve smooth movement. In this example, the sub-View also slides with the finger, but when the finger leaves the screen, the sub-View moves smoothly to the initialization position, that is, the upper left corner of the screen. Using the Scroller class requires the following three steps:
First, create a Scroller object through its construction method. The code is as follows:
Next, we need to rewrite the computerScroller() method, which is the core of the Scroller class. The system will call this method in the draw() method when drawing the View. This method actually uses the scrollTo method. Combined with the Scroller object, it helps to get the current scroll value. We can achieve an overall smooth movement effect by continuously moving a small distance in an instant. The code is as follows:
The Scroller class provides the computeScrollOffset() method to determine whether the entire slide is completed, and also provides the getCurrX() and getCurrY() methods to obtain the current slide coordinates. In the above code, the only thing to note is the invalidate() method, because the scrollX and scrollY coordinates in the simulation process can only be obtained in the computeScroller() method. However, the computeScroll() method will not be called automatically. The compuetScroll() method can only be called indirectly through invalidate()->draw()->computeScroll(), so it is necessary to call the invalidate() method in the compuetScroll() method to achieve the purpose of looping to obtain scrollX and scrollY. When the simulation process is over, the scroller.compuetScrollOffset() method will return false, interrupting the loop and completing the smooth movement process.
In the event that smooth movement is required, we use the startScroll() method of the Scroller class to start the smooth movement process. The startScroll() method has two overloaded methods.
You can see that the difference between them is that one has a specified supported duration, while the other does not. It is easy to understand, and it is the same as setting the duration in the animation and using the default display duration. The other four coordinates have the same meaning as their names, which are the starting coordinates and the offset. When obtaining the coordinates, you can usually use the getScrollX() and getScrollY() methods to obtain the coordinates of the point in the parent view where the content slides. It should be noted that the positive and negative values are the same as those explained in scrollBy and scrollTo. According to the above three steps, you can use the Scroller class to achieve smooth movement. Initialize the Scroller object in the construction method, rewrite the computerScroll() method of View, and finally listen for the event of the finger leaving the screen, and call the startScroll() method in the event to complete the smooth movement. To listen for the event of the finger leaving the screen, you only need to add an ACTION_UP listening option in onTouchEvent. The code is as follows:
In the startScroll() method, we get the distance that the child View moves - getScrollX(), getScrollY(), and set the offset to its opposite number, so as to slide the child View to its original position. The invalidate() method here is used to notify the View to redraw and call the simulated process of computeScroll(). Of course, you can also add a duration parameter to the startScroll() method to set the duration of the slide. (6) Attribute animation For attribute animation, please refer to my other article: Android full set of animation usage skills (7) ViewDragHelper Google provides us with two layouts, DrawerLayout and SlidingPaneLayout, in its support library to help developers achieve the effect of sidebar sliding. These two new layouts make it easy for us to create our own sliding layout interface. Behind these two powerful layouts is a powerful class - ViewDragHelper. Through ViewDragHelper, basically all kinds of sliding and dragging requirements can be achieved, so this method is also the ultimate trick among various sliding solutions. The following demonstrates a layout that uses ViewDragHelper to create a QQ sidebar sliding layout, as shown in the figure: Figure 3 Figure 4
First, you need to initialize ViewDragHelper. ViewDragHelper is usually defined inside a ViewGroup and initialized through a static factory method. The code is as follows:
The View that the first parameter monitors usually requires a ViewGroup, i.e. parentView; the second parameter is a Callback callback, which is the logical core of the entire ViewDragHelper.
Rewrite the interception event and pass the event to ViewDragHelper for processing;
Using ViewDragHelper also requires overriding the computeScroll() method, because ViewDragHelper also uses Scroller to achieve smooth movement.
Create a ViewDragHelper.Callback
as automatically rewrites the tryCaptureView() method, which can be used to specify which child View in the parameter parentView can be moved when creating ViewDragHelper. For example, in this example, we customize a ViewGroup, which defines two child Views - Menu View and MainView, as shown in the following code:
The specific vertical sliding method is clampViewPositionVertical(), and the horizontal sliding method is clampViewPositionHorizontal(). These two methods must be written to implement sliding. The default return value is 0, that is, no sliding occurs. Of course, if only one of clampViewPositionVertical() or clampViewPositionHorizontal() is rewritten, then only the sliding effect in that direction will be achieved.
The parameter top in clampViewPositionVertical(View child, int top, int dy) represents the distance the child moves in the vertical direction, and dy represents the increment compared to the previous one. clampViewPositionHorizontal(View child, int left, int dx) has a similar meaning. Usually, only top and left need to be returned. However, when more precise calculation of properties such as padding is required, left needs to be processed and a value of the appropriate size needs to be returned. By rewriting the above three methods, you can achieve a basic sliding effect. When you drag MainView with your hand, it will slide along with your finger. The code is as follows:
In the previous Scroller, we implemented an effect: after the finger leaves the screen, the View slides back to the initial position. Now we use ViewDragHelper to implement it. In ViewDragHelper.Callback, the system provides such a method: onViewReleased(). By overriding this method, we can easily implement the operation when the finger leaves the screen. This method is implemented by the Scroller class, which is why we overrode the computeScroll() method.
When the left margin of MainView is less than 500 pixels after it moves, the smoothSlideViewTo() method is used to restore MainView to its initial state, that is, the coordinate (0,0). If the left margin is greater than 500, MainView is moved to the coordinate (300,0), that is, MainView is displayed.
When sliding, in the onFinishInflate() method of the custom ViewGroup, define the child Views as MenuView and MainView in order, and get the width of the View in the onSizeChanged method. If you need to process the effect after sliding according to the width of the View, you can use this value to judge.
***, the entire code for implementing the QQ side-sliding function through ViewDragHelper:
In addition, many powerful features of ViewDragHelper have not been demonstrated. In ViewDragHelper.Callback, the system defines a large number of listening events to help us handle various events, as follows:
STATE_IDLE: The View is not being dragged or animated, but is just sitting there quietly. STATE_DRAGGING: The View is currently being dragged, and the View position is changing due to user input or simulated user input. STATE_SETTLING: View is currently being settled to a specified location, triggered by a fling gesture or a predefined non-interactive action
3. Open Source Code Library *** I will share another code library that I have accumulated for a long time. There is nothing you can't think of and nothing you can't use. Welcome to star https://github.com/xijiufu Since the GitHub server is in the United States, sometimes the access is slow. We also provide an open source Chinese address library. The codes of the two repositories are updated synchronously: http://git.oschina.net/xijiufu |
<<: Android full set of animation usage tips
>>: How to effectively collect user feedback in mobile apps
Produced by: Science Popularization China Author:...
The development of the "lazy economy" a...
I wonder if you have noticed that in the Internet...
With the arrival of spring, nature begins to revi...
From "UCC Coffee" to "Sad Tea"...
The screen size of ASUS ZenFone 6 is 6.0 inches, w...
Modern oil exploration began in the mid-19th cent...
On Weibo, there is a classic question with a play...
Bidding advertising products: product strategies ...
So far, we still don't know the battery speci...
Previously, the news that "bank facial recog...
When it comes to the concept of seed users , I be...
Well-known football commentators Zhan Jun and Yan...
On March 11, Meituan Waimai released the "Ca...
“Words are finite, but their meanings are infinit...