1. Learn brain maps

2. View Foundation

2.1 what isView?

Q1: How do you understand View?

  • ViewIs an abstraction of a control at the interface layer, representing a control.
  • isandroidVisual presentation.
  • Yes All controls are base classes and can be individual controlsViewIt can be a set of controlsViewGroup.

Q2: The importance of View?

View is a very important concept in Android, although View does not belong to the four components, but its role is as important as the four components, in the development, Activity assumes the visual function, Android provides a lot of basic controls, when we are not satisfied with the function of these basic controls, you can use custom controls, The control of the customization needs to have a deep understanding of the View system.

2.2 ViewPosition parameter of

In Android system, there are two kinds of coordinate system, namely Android coordinate system and View coordinate system.

2.2.1 AndroidCoordinate system
  • Take the upper-left corner of the screen as the origin of the coordinates
  • To the right of the origin is the positive X-axis
  • Down from the origin is the positive Y direction

Note: The coordinates obtained using the getRawX() and getRawY() methods are those of the Android coordinate system

2.2.2 ViewCoordinate system

Q1: What determines the position of a View?

Four vertices: top, left, right, and bottom

Note: These coordinates are relative to the parent container, and are relative coordinates

Top = getTop(), Left = getLeft(),Right = getRight(),Bottom=getBottom()

Since android 3.0, x, Y, translationX, translationY parameters have been added.

  • x,y:ViewThe coordinates of the top left corner
  • translationX,translationY: The offset of the upper-left corner relative to the parent container

Note: During the translation of View, top and left represent the position information of the original upper left corner, and the values changed are x, Y, translationX, translationY.

Q2: What is the difference between getX() and getY() and getRawX() and getRawY()?

GetX and getY are view coordinates and are distances relative to the control

GetRawX and getRawY are absolute coordinates, which are distances from the entire screen

Q3: How does a View get its width and height?

width = getRight()getLeft() = getWidth()

height = getBottom()getTop() = getHeight()

2.2.3 ViewThe touch of
2.2.3.1 MotionEvent

The sequence of events that occurs when a finger touches a screen.

  • ACTION_DOWN— Fingers just touching the screen
  • ACTION_MOVE— Moving your finger across the screen
  • ACTION_UP— The moment your finger is released from the screen

Under normal circumstances, the following two situations occur when you touch the screen

  • Tap the screen and release, DOWN -> UP
  • Click the screen to slide and then release, DOWN->MOVE->… ->MOVE->UP
2.2.3.2 TouchSlop

TouchSlop is the minimum distance the system can recognize to be considered a slide, and is a constant.

Q1: How do I get this constant?

ViewConfiguration. Get (getContext ()). GetScaledTouchSlop ().

Q2: What does this constant mean?

When dealing with slides, this constant can be used for filtering, and when two slide events slide less than this constant, they are not considered slides.

2.2.3.3 VelocityTracker

Speed tracking, used to track finger speed in the sliding process, including horizontal and vertical speed.

Q: How do I use VelocityTracker?

1. Track the speed of the current click event in the View’s onTouchEvent method

 VelocityTracker velocityTracker = VelocityTracker.obtain(); 
 velocityTracker.addMovement(event);
Copy the code

2. Obtain the current speed

  velocityTracker.computeCurrentVelocity(1000);
        int xVelocity = (int)velocityTracker.getXVelocity();
        int yVelocity = (int)velocityTracker.getYVelocity();
Copy the code

Note:

  • Speed before need to calculation speed, namely getXVelocity () and getYVelocity () method must first before calling velocityTracker.com puteCurrentVelocity (1000);

  • The speed here refers to the number of pixels the hand slides over a period of time. The velocity can be either positive or negative, because in the Android coordinate system, if you slide your finger against the positive direction of the coordinate, the velocity will be negative, and plus or minus here refers to the direction.

    Speed = (finishing position – starting position)/time period

3. Use clear to reset and reclaim memory

When velocityTracker is not needed, clear is used to reclaim it.

   velocityTracker.clear();
        velocityTracker.recycle();
Copy the code
2.2.3.4 GestureDetector

Gesture detection is used to help detect user behaviors such as clicking, sliding, long pressing, and double clicking.

Q: How to use GestureDetector?

1. Create a GestureDetector object and implement the OnGestureDetector interface

GestureDetector mGestureDetector = new GestureDetector(this,this); / / to solve long. According to the phenomenon of screen can't drag mGestureDetector setIsLongpressEnabled (false);Copy the code

2. Add in the View onTouchEvent method

 boolean consume = mGestureDetector.onTouchEvent(event);
        return consume;
Copy the code

3. Optionally implement the OnGestureListener and OnDoubleTapListener methods

Suggestion: If only listening for the sliding operation, it is recommended to implement in onTouchEvent; If you want to listen for double-clicking, use a GestureDetector.

2.2.3.5 Scroller

Elastic sliding object, used to realize the elastic sliding of View

Q: How do I use Scroller? Typical code is fixed as follows.

Scroller scroller = new Scroller(mContext); Private void smoothScrollTo(int destX,int destY){int scrollX = getScrollX(); int delta = destX -scrollX; Scroll. startScroll(scrollX,0,delta,0,1000); invalidate; } @Override Public void computeScroll(){ if(mScroller.computeScrollOffset()){ ScrollTo(mScroll.getCurrX(),mScroller.getCurrY()); postInvalidate(); }}Copy the code

3. View sliding

Slide in Android development has an important role, master the method of slide is the basis of the implementation of custom controls.

Basic idea of View sliding:

When the touch event is transmitted to the View, the system writes down the coordinate of the touch point. When the finger moves, the system writes down the coordinate of the touch after moving and calculates the offset, and modifies the coordinate of the View by the offset.

3.1 7 ways to slide View

3.2.1 layout()

The onLayout() method is used to set the position of the view, so you can control the coordinates of the view by modifying the left, top, right, and bottom attributes of the view.

  • Use:
Public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {int x = (int) event.getx (); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; ACTION_MOVE: // Calculate the distance moved int offsetX = x-lastx; int offsetY = y - lastY; Layout (getLeft()+offsetX, getTop()+offsetY, getRight()+offsetX, getBottom()+offsetY); / / layout () method to break; } return true; }Copy the code

3.2.2 offsetLeftAndRight()withoffsetTopAndBottom()

The layout() method is the same as the layout() method, but offsetLeftAndRight() and offsetTopAndBottom() set the left, right, and up deviation values.

Use:

Public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {int x = (int) event.getx (); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; ACTION_MOVE: // Calculate the distance moved int offsetX = x-lastx; int offsetY = y - lastY; // offsetLeftAndRight offsetLeftAndRight(offsetX); // offsetTopAndBottom offsetTopAndBottom(offsetY); break; } return true; }Copy the code
3.2.3 LayoutParams(Change layout parameters)

LayoutParams saves the layout parameters of a View. You can change the layout parameters of a View by LayoutParams.

  • Use:
Public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {int x = (int) event.getx (); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; ACTION_MOVE: // Calculate the distance moved int offsetX = x-lastx; int offsetY = y - lastY; LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams); break; } return true; }Copy the code

If the parent LinearLayout is according to the code, as shown on the parent controls if RelativeLayout, is to use RelativeLayout. LayoutParams, besides using layout LayoutParams, Can also use ViewGroup. MarginLayoutParams to implement

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
Copy the code

3.2.4 animation

Idea: by animation allows a View of translation, and translation is a kind of sliding, the main operation is to View the translationX and translationY attributes.

There are two types of animation available on Android: View animation and property animation.

  • View animation uses:

    1. Create a new anim folder in the res directory and create translate.

  LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
                layoutParams.leftMargin = getLeft() + offsetX;
                layoutParams.topMargin = getTop() + offsetY;
                setLayoutParams(layoutParams);
Copy the code

2. Reference in Java code:

 mCustomView.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));
Copy the code

Note: View animations do not change the View’s position parameters.

If we animate a Button as shown above, clicking on the Button will not trigger a click event when the Button is shifted 300 pixels away from its current position, but clicking on the Button’s original position will trigger a click event. That’s the difference between tween animation and property animation

  • Property animation uses:

    CustomView moves 300 pixels right along the X-axis in 1000 ms:

ObjectAnimator. OfFloat (mCustomView, "translationX", 0300). SetDuration (1000). The start ();Copy the code

3.2.5 scrollTo/scrollBy

ScollTo (x,y) means to move to a specific coordinate point, while scollBy(dx,dy) means to move in increments of dx,dy. ScollBy will eventually call scollTo. ScollTo and scollBy move the contents of a View, or all of its child views if used in a ViewGroup.

  • Use:
Public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {public Boolean onTouchEvent(MotionEvent) {int x = (int) event.getx (); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = x; lastY = y; break; ACTION_MOVE: // Calculate the distance moved int offsetX = x-lastx; int offsetY = y - lastY; ((View)getParent()).scrollBy(-offsetX,-offsetY); break; } return true; }Copy the code

3.2.6 Scroller

When using scollTo/scollBy, this process is done instantaneously. Using Scroller to achieve the excessive effect of sliding, this process is not done instantaneously, but in a certain time interval. Scroller itself cannot realize View sliding. It needs to cooperate with View computeScroll() method to achieve elastic sliding effect.

  • Use:
@Override public void computeScroll() { super.computeScroll(); if(mScroller.computeScrollOffset()){ ((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); // Repeat computeScroll calls PostInvalidate(); Public void smoothScrollTo(int destX,int destY){int scrollX=getScrollX(); int delta=destX-scrollX; DestX mscroll. startScroll(scrollX,0,delta,0,2000); invalidate(); }Copy the code

Called in the View class

/ / using insensitive to move smoothly mCustomView smoothScrollTo (400, 0).Copy the code
  • Source code analysis

    When we construct a Scroller object and call its startScroll method, startScroll holds several parameters passed

    /** * @param dx horizontal sliding distance * @param dy vertical sliding distance * @param duration sliding time */ 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

    Q1: In the startScroll method, there is no internal sliding, so how does startScroll make a View slide?

    A: Use the invalidate method. The invalidate method will cause the View to be redrawn. During the redrawing process, the View’s Draw method will call the computeScroll method. The original computeScroll method is an empty implementation in the View. Slide through the scrollTo method, then redraw a second time using the PostInvalidat method, and so on until the slide process is complete.

    The computeScroll method uses the computeScrollOffset() method

    public boolean computeScrollOffset() { if (mFinished) { return false; } 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; .Copy the code

    This method computes the current scrollX and scrollY values based on the elapsed time. Return true to indicate that the slide is not over, false to indicate that the slide is over.

3.2.7 Delay Policy

Core idea: achieve a progressive effect by sending a series of delayed messages.

Use: Handle/View postDelayed/ thread sleep.

Note: Timing cannot be precise because system message scheduling also takes time.

4. Event distribution mechanism

Event distribution mechanism is the core point of learning in View system. It is the theoretical basis of solving sliding conflicts. Therefore, it is very important to learn event distribution mechanism well.

This part is mainly my summary of knowledge, after reading or feel fuzzy to the event distribution mechanism of readers, recommend a detailed event distribution article to learn View event distribution, like outsiders on the black car! .

Q1: What is event distribution for click events?

When a click event MotionEvent is generated, the system sends the event to a specific View, which is called event distribution.

4.1 Main Methods

  • DispatchTouchEvent: Dispatches events. The return value is Boolean and is affected by the current onTouchEvent and the dispatchTouchEvent of the subordinate View

  • OnInterceptTouchEvent: Intercepts events. This method is only available in viewgroups, not views (excluding viewgroups). If intercepted, the onTouchEvent of the ViewGroup is executed, and the event is processed in the ViewGroup, not then distributed to the View, and only called once, so subsequent events are handed over to the ViewGroup.

  • OnTouchEvent: Performs event processing.

Pseudocode for the relationship between the three:

public boolean dispatchTouchEvent(MotionEvent event) { boolean consume = false; // The Boolean value indicates whether to consume the event if(onInterceptTouchEvent(event)){consume = onTouchEvent(event); }else {consume = child.dispatchTouchEvent(event); consume = child.dispatchTouchEvent(event); } return consume;} return consume;} return consume; }Copy the code

4.2 Event Distribution Process

Q2: What is the nature of View event distribution?

The essence of View event distribution is recursion. The process of click event top-down distribution is “recursion”, and the process of consume event bottom-up is “return”.

Q3: What are the two processes of “recursion” and “return”?

When a click event is generated, it is passed in the following order: Activity->Window->ViewGroup->… – > View. This process of top-down transmission is the process of “transmission”.

When passed to a particular View, the View’s onTouchEvent returns false, meaning that it does not consume the event, and the event will be passed up. If all elements do not handle the event, the event will eventually be passed to the Activity. This process of bottom-up transmission is the process of “return”.

Note:

In the process of recursion, the ViewGroup can directly step into the process of recursion by setting the onInterceptTouchEvent method to return true at the current level.

In ViewGroup can intercept events issued at the same time, the child can also through the getParent. RequestDisallowInterceptTouchEvent method, issued to intercept to stop at the next higher level.

Figure from the learning View event distribution, like out-of-towners on the black car!

Summary: Also refer to the link above

4.3 Source Code Analysis

The three processes of event distribution:

  1. ActivityDistribution of events
  2. topViewDistribution of events
  3. ViewHandling of events

The source code is not listed here, just a flow chart. Readers who need to see the source code can check out the corresponding section of The Android Development Art Explorer or check it out in the compiler.

Five, sliding conflict

In the process of using the sliding process, suppose a situation, an interface can slide inside and outside, when you slide it, how does the interface determine whether you slide the inner layer or the outer layer? This is where the slippage conflict occurs, so in this section, let’s solve the slippage conflict together.

5.1 Sliding Conflict Scenarios

  • Scenario A: External sliding and internal sliding inconsistent sliding conflict, common in common inScrollViewandFragmentIn theLisetViewThe use of.
  • Scenario B: Sliding conflicts consistent with external sliding and internal sliding may occur in customizationViewwithListViewIn, the outside can slide up and down, and the inside can also slide up and down.
  • Scenario C: nesting of scenario AB.

5.2 Handling Rules

  • The processing rule for scenario A: Let the external View intercept the click event when the user swipes left and right, and let the internal View intercept the click event when the user swipes up and down.

    Q1: How to determine whether users swipe left or up or down

    The horizontal offset is subtracted from the vertical offset, and the offset is greater than 0 to determine which offset is larger. If offsetx-offsety >0, it can be judged to be sliding horizontally, which can be intercepted externally to handle the click event.

  • The processing rules for scenario B: Need to be handled according to the business logic, and specify when the external View intercepts the event and when the internal View intercepts the event.

  • Rule of scenario C: Also find a service breakthrough

5.3 Solutions

  • External interception

  • Internal interception method

5.3.1 External interception

Click events are first intercepted by the parent container. If the parent container needs to intercept the event, it is not intercepted.

Method: Override the onInterceptTouchEvent method of the parent container and intercept it internally.

Note: Once the parent container starts intercepting any event, subsequent events are handed over to it for processing.

Public Boolean onInterceptTouchEvent (MotionEvent event){Boolean intercepted = false; int x = (int) event.getX(); int y = (int) event.getY(); Switch (event.getAction()) {case motionEvent.action_down :// False must be returned for ACTION_DOWN events, Once intercepted, subsequent events cannot be passed to the child View intercepted = false; break; ACTION_MOVE:// For ACTION_MOVE events, decide whether to intercept if (parent needs the current event) {intercepted = true; } else { intercepted = flase; } break; ACTION_UP:// False must be returned for ACTION_UP events. If the onClick event of the child View is intercepted, it will not trigger intercepted = false; break; default : break; } mLastXIntercept = x; mLastYIntercept = y; return intercepted; }Copy the code

5.3.2 Internal interception

The parent container does not intercept any events. All events are passed to the child element. If the child element needs the event, it consumes it.

Methods: the need to match the requestDisallowInterceptTouchEvent method. Overwrite dispatchTouchEvent() for child View

public boolean dispatchTouchEvent ( MotionEvent event ) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction) { case MotionEvent.ACTION_DOWN: parent.requestDisallowInterceptTouchEvent(true); // True prevents the parent from intercepting break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; int deltaY = y - mLastY; If (the parent container need such click events) {parent. RequestDisallowInterceptTouchEvent (false); // for fasle, the parent is allowed to intercept} break; case MotionEvent.ACTION_UP: break; default : break; } mLastX = x; mLastY = y; return super.dispatchTouchEvent(event); }Copy the code

Note: in addition to the child container needs to be done to deal with, the parent container will default to intercept besides ACTION_DOWN of other events, so that when the container calls the parent. RequestDisallowInterceptTouchEvent (false) approach, the parent can continue to intercept the events.

Therefore, the parent View needs to override onInterceptTouchEvent() :

public boolean onInterceptTouchEvent (MotionEvent event) { int action = event.getAction(); if(action == MotionEvent.ACTION_DOWN) { return false; } else { return true; }}Copy the code

Q1: Why can’t the parent container intercept ACTION_DOWN events?

Because the event is not affected by FLAG_DISALLOW_INTERCEPT (set by requestDisallowInterceptTouchEvent method) marked position control, so once the parent container to intercept the events, all events are not passed to the View, The inside intercept method would have failed.


Since the reference:

  • Exploring the Art of Android Development

  • Android Advanced Light

  • Advanced road | wonderful trip to the View

  • Learn View event distribution, like out-of-towners on the black car!