1. The View and ViewGroup
The relationship between a View and a ViewGroup?
1. ViewGroup can be understood as a combination of views, which can contain many views and viewgroups.
2. ViewGroup also inherits from View.
2. The coordinate system
Android system has two kinds of coordinate system, respectively Android coordinate system and View coordinate system.
2.1 Android Coordinate System
Take the vertex in the upper left corner of the screen as the origin of the Android coordinate system, with positive X direction to the right and positive Y direction down.
Usage: getRawX and getRawY
2.2 View Coordinate System
2.2.1 View Obtains its own width and height
GetHeigh () and getWidth ()
2.2.2 View’s own coordinates
- GetTop (): Gets the distance between the top edge of the View itself and the top edge of its parent layout.
- GetLeft () : Gets the distance from the left side of the View itself to the top edge of its parent layout.
- GetRight () : Gets the distance from the right side of the View itself to the top edge of its parent layout.
- GetBottom () : Gets the distance from the bottom edge of the View itself to the top edge of its parent layout.
2.2.3 Methods provided by MotionEvent
- GetX (): Gets the distance from the click event to the left of the control, the view coordinates.
- GetY (): Gets the distance between the click event and the top edge of the control, the view coordinates.
- GetRawX (): Gets the distance of the click event to the left of the entire screen, the absolute coordinates.
- GetRawY (): Gets the absolute coordinates of the distance from the click event to the top edge of the screen.
3. View sliding
Principle: When the click event is transmitted to the View, the system records the coordinate of the touch point; when the finger moves, the system records the coordinate of the touch after the move and calculates the offset, and modifies the coordinate of the View by the offset. Here are 6 ways to slide:
3.3.1 layout () method
The onLayout method is called when the View is drawn to set the display position.
Application:
1. Get the coordinates of the touch point in the onTouchEvent method.
2. Calculate the offset in the ACTION_MOVE event and call the Layout method to reset the position of the custom View.
3. Call the Layout method to rearrange the screen every time you move, so as to achieve the effect of moving View.
3.3.2 rainfall distribution on 10-12 offsetLeftAndRight () and offsetTopAndBottom
These two methods are about the same as the Layout method and are used in the same way.
- OffsetLeftAndRight (offsetX) // offsetLeftAndRight
- OffsetTopAndBottom (offsetY) // offsetTopAndBottom
3.3.3 LayoutParams (Changing layout parameters)
LayoutParams stores the layout parameters of a View, so we can change the layout parameters of the View through LayoutParams, thus changing the position of the View.
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.rightMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
Copy the code
If it is the parent RelativeLayout to use RelativeLayout. LayoutParams.
We do the animation
1. View animation:
Use: Create a new anim folder in the res directory and create translate. Finally use loadAnimation(this,R.anim.translate)
Cons: View animations do not change View position parameters.
2. Property animation:
ObjectAnimator. OfFloat (mCustomView, "translationX", 0300). SetDuration (1000). The start ();Copy the code
3.3.5 scrollTo scrollBy
ScrollTo (x,y): indicates moving to a specific coordinate point.
ScrollBy (dx,dy): indicates that the increment of the movement is dx,dy.
3.3.6 Scroller
Scroller itself cannot realize View sliding. It needs to cooperate with View’s computeScroll method to realize elastic sliding effect.
The system calls this method in the Draw method while drawing the View. In this method, we call the scrollTo method of the parent class and use the Scroller to continuously obtain the current slide value. Every time we swipe a small distance, we call the invalidate method and keep redrawing. The redraw calls the computeScroll method again, so it’s coherent.
4. Property animation
4.1 ObjectAnimator
How it works: ObjectAnimator is the most important class for attribute animation. Creating an ObjectAnimator simply returns an ObjectAnimator object directly from its static factory class. Parameters include an object and the name of the object’s property, but the property must have a get method and a set method, which are internally called by Java reflection to modify the value of the object’s property.
4.2 ValueAnimator
ValueAnimator does not provide any animation effects. It is more like a numeric generator that produces regular numbers that allow the caller to control the implementation of the animation.
4.3 Monitor of animation
The complete animation has four processes: start, Repeat, end and Cancel.
- onAnimationStart(Animator animation)
- onAnimationEnd(Animator animation)
- onAnimationCancel(Animator animation)
- onAnimationRepeat(Animator animation)
4.4 Composite Animation – AnimatorSet
An AnimatorSet provides a play method. If we pass an Animator into this method, we will return an instance of AnimatorSet.Builder, the inner class of the Builder class AnimatorSet, in Builder mode. Four methods are provided:
1. After (Animator anim) : Execute after inserting an existing animation into the incoming animation.
2. After (long delay) : The existing animation is delayed for a specified number of milliseconds.
3. Befor (Animator anim): Inserts an existing animation before the incoming animation.
4. With (Animator anim): Executes an existing animation and an incoming animation simultaneously.
4.5 Combination animation – PropertyValueHolder
ObjectAnimator.ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)Copy the code
4.6 Animating properties in XML
Like View animations, property animations can be unloaded directly into XML. Create a new animator file in the RES file and create a new scale.xml file inside it.
AnimatorInflater.loadAnimator(this,R.animator.scale)
Copy the code
5. Analytical Scroller
Scroller principle: Scroller cannot directly realize View sliding, it needs to cooperate with View computeScroll() method. In computeScroll(), the View is constantly redrawn. Each redrawing will calculate the duration of the slide, and the position of the View can be calculated according to the duration of the slide. We call scrollTo () method to slide according to the position of each slide, so that the above process is repeated continuously to form elastic sliding.
6. Event distribution mechanism of View
6.1 Source code parsing Activity composition
Summary: An Activity contains a Window object, which is implemented by PhoneWindw. PhoneWindow uses a DecorView as the root View of the entire application window, and this DecorView divides the screen into two areas, a TitleView and a ContentView, where the layout of the application is displayed.
6.2 Source parsing View event distribution mechanism
There are three important ways to click on events:
-
DispatchTouchEvent (MotionEvent EV) – used for event distribution;
-
OnInterceptTouchEvent (MotionEvent EV) – Used to intercept events, called from dispatchTouchEvent (). Note that the View does not provide this method;
-
OnTouchEvent (MotionEvent EV) – Used to handle click events, called in the dispatchTouchEvent () method.
6.2.1 Event distribution mechanism of View
When the click event is generated, the event is first passed to the current Activity, which calls the Activity’s dispatchTouchEvent() method. Of course, the specific event handling is handled by the PhoneWindow in the Activity. The PhoneWindow then hands the event handling to the DecorView, which in turn hands the event handling to the root ViewGroup.
A complete sequence of events begins with DOWN and ends with UP. If the current ViewGrou intercepts the event, onInterceptTouchEvent(ev) will be called, and the subsequent sequence of events will be handled by the ViewGroup instead of onInterceptTouchEvent(EV). And set intercepted = true.
Therefore, the onInterceptTouchEvent () method is not called for every event and returns false by default.
If the current ViewGroup does not intercept the event, the series of events is passed to the child View.
FLAG_DISALLOW_INTERCEPT flags: it is mainly prohibited ViewGroup intercept in addition to the DOWN event, generally through the son requestDisallowInterceptTouchEvent to set the View.
The View of dispatchTouchEvent
If onTouchListener is not null and the onTouch method returns true, the event is consumed and onTouchEvent(EV) is not executed. Otherwise, onTouchEvent(ev) is executed. In onTouchEvent, onTouchEvent() returns true to consume the event whenever either the View’s Clickable or LONG_CLICKABLE is true.
The performClick method is then called in ACTION_UP, and the onClick method is called if the click event is set.
The onTouchListener interface has a higher priority than onTouchEvent. If the onTouch method in onTouchListener returns true, indicating that the event has been consumed, the onTouchEvent will not receive the message.
If I set an onTouchListener to a Button and override the onTouch method that returns true, will the Button’s click event still be handled?
The answer is: yes. Since Button’s performClick is implemented using onTouchEvent, Button’s Click event cannot respond if onTouchEvent is not called.
Conclusion:
1. The onTouch method of onTouchListener has a higher priority than onTouchEvent and is triggered first.
2. If onTouch returns false then onTouchEvent will be raised, otherwise onTouchEvent will not be called.
3. Built-in implementations such as click events are based on onTouchEvents, which will not be triggered if onTouch returns true.
(Go back and try it, or it will be difficult to understand)
Events are a series of events, and if DOWN is consumed where subsequent events are consumed.
6.2.2 Delivery rules for click event distribution
From top to bottom:
When the click event is generated, it is handled by the Activity, passed to the PhoneWindow, passed to the DecorView, and finally passed to the top-level ViewGroup. Generally, only the onInterceptTouchEvent method of the ViewGroup is considered in event passing, because we don’t normally override the dispatchTouchEvent method. For the root ViewGroup, the click event is first passed to its dispatchTouchEvent method. If the onInterceptTouchEvent method of the ViewGroup returns true, it will intercept the event, which will be handled by its onTouchEvent method: If the onInterceptTouchEvent method returns false, it does not intercept the event, which is handled by its child’s dispatchTouchEvent, and so on. If it’s passed to an underlying View that has no children, then the View’s dispatchTouchEvent method is called. You’ll usually end up calling the View’s onTouchEvent method.
From the bottom up:
When a click event is passed to the underlying View, the event is consumed and processed by the underlying View if its onTouchEvent method returns true: If the onTouchEvent method returns false, the View is unprocessed and passed to the parent View’s onTouchEvent method: If the parent View’s onTouchEvent method still returns false, it continues to be passed to the parent View, and so on.
7. Workflow of View
Measure: used to measure the width and height of the View;
Layout: Used to determine the position of the View;
Draw: used to draw a View;
7.1 Workflow entry of View
(1) How is the DecorView loaded into the Window?
7.2 understand MeasureSpec
MeasureSpec:
A MeasureSpec class encapsulates the dimensions of a View, including the width and height of the View. A MeasureSpec class converts a View’s LayoutParams into a MeasureSpec class according to the rules imposed by the parent. The width and height of the View are then determined by MeasureSpec in the onMeasure method.
Measurement mode specMode:
- UNSPECIFIED: The mode is UNSPECIFIED, the View is UNSPECIFIED, and the parent container is UNSPECIFIED.
- AT_MOST: Maximum mode, corresponding to the wrap_content property, in which the final size of the child View is the specSize value specified by the parent View and the child View cannot be larger than this value.
- EXACTLY: EXACTLY mode, corresponding to the match_parent property and specific value, the parent container measures the required size of the View, i.e., the value of specSize.
For each View, a MeasureSpec is held, and the MeasrSpec holds the dimensions of the View. In the View measurement process, makeMeasureSpec is used to store width and height information. Use getMode or getSize to get the mode and width and height. MeasureSpec is influenced by both its own LayoutParams and the parent container’s MeasureSpect.
For a DecorView, its MeasureSpec is determined by its own LayoutParams and the size of the window, unlike a normal View.
7.3 Measure Process of the View
Measure is used to measure the width and height of a View, and its process is respectively the measure process of the View and the measure of the ViewGroup, but the measure process of the ViewGroup not only needs to complete its own measurement, but also calls the measure() method of its child elements iteratively.
(1) Measure process of View
Measurement process:
onMeasure()->setMeasuredDimension()->setMesuredDimensionRaw()
GetDefaultSize role:
Public static int resultSize (int size, int measureSpec) {//size = measureSpec; Int specMode = measureSpec. GetMode (measureSpec); Int specSize = measureSpec. GetSize (measureSpec); Switch (specMode) {// If mode is UNSPECIFIED, the View's parent ViewGroup has no UNSPECIFIED dimensions. Case MeasureSpec.UNSPECIFIED: // While the mode is UNSPECIFIED, the View uses its desired size as its calculation result = size. break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: // When mode is AT_MOST or EXACTLY, the View uses the size specified by its parent ViewGroup as its result = specSize; break; } return result; } The result here is obtained by the following function. protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }} mBackground == null if no background is set, then return mMinWidth as specified by android:minWidth, which can be 0; If the View has a background, return the maximum mMinWidth and the minimum background width. GetSuggestedMinimumWidth () returns the measurement width of the View in the case of UNSPECIFIED.Copy the code
Return different result values (SpecSize) based on different SpecMode values. In both AT_MOST and EXACTLY modes, SpecSize is returned, meaning that the measurement width and height of the View in both modes are directly dependent on SpecSize. That is, for a custom View that inherits directly from the View, its wrap_content and match_parent properties have the same effect.
So if you want to implement the wrAP_content of a custom View, you need to override the onMeasure method and handle the wrAP_content property of the custom View.
(2) Measure process of ViewGroup
The ViewGroup does not define the onMeasure() method, but does define the measureChildren() method.
EXACTLY:match_parent AT_MOST:wrap_content
Copy the code
The measureChild method is called, the Child.getLayoutParams () method is called to obtain the child element’s LayoutParams attribute, and the MeasureSpec method is called to measure the child element.
What does getChildMeasureSpec() say?
/** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec The requirements for this view * @param padding The padding of this view for the current dimension and * margins, if applicable * @param childDimension How big the child wants to be in the current * dimension * @return a MeasureSpec integer for the child */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }Copy the code
Obviously, this is a child’s MeasureSpec attribute derived from the parent container’s MeasureSpec schema combined with the child element’s LayoutParams attribute. One thing to note is that if the parent’s MeasureSpec is AT_MOST and the child’s LayoutParams is WRAP_CONTENT, then according to the code above, We’ll see that the child element also has the attribute AT_MOST, and its SpecSize value is the parent container’s SpecSize minus the Padding value. In other words, this has the same effect as setting the LayoutParams attribute to MATCH_PARENT for the child element. To solve this problem, specify the default width and height when LayoutParams is WRAP_CONTENT.
7.4 View layout process
The Layout method is used to determine the position of elements. The Layout method in the ViewGroup is used to determine the position of child elements, and the Layout method in the View is used to determine the position of itself.
The layout parameters l,t,r, and b are the distances from the View to its parent container from left, top, right, and bottom.
Layout ->onLayout, the onLayout method is empty, similar to the onMeasure method. The onLayout method is not implemented in View and ViewGroup.
Take onLayout in a LinearLayout as an example:
LayoutVertical () iterates over the child elements and calls the setChildFrame method.
Call the layout method of the child element in the setChildFrame method to determine its position.
7.5 View’s DRAW process
1. Draw the background if necessary.
2. Save the canvas layer.
3. Draw the View contents.
4. Draw subviews.
5. If desired, draw the faded edges of the View, similar to a shadow effect.
6. Paint decorations like. The scroll bar.
Call the View’s drawBackground(Canvas Canvas).
Draw the contents of the View by calling the View onDraw(Canvas canvas) method.
The dispatchDraw**(Canvas Canvas)** method is called
The ViewGroup overrides this method by traversing the subclass View in dispatchDraw and calling drawChild, which in turn calls the View’s draw method. This will determine if there is a cache, if there is no cache, it will display normally, if there is cache.
Use the onDrawForeground method, which is obviously used to draw the ScrollBar and other decorations at the top of the view content.
8. Customize the View
8.1 Inherit custom View of system control
Inherit system View, rewrite onDraw method can be.
8.2 Inheriting a custom View from a View
8.2.1 Simple implementation of a custom View inherited from the View
Unlike the custom View inherited from the system control above, the custom View inherited from the View is slightly more complex to implement. Not only does it implement the onDraw () method, but it also takes into account the wrap_content and padding properties; To make it easy to configure your own custom View, you will also provide custom properties. Also, if you want to change the touch logic, you have to rewrite touch event methods like onTouchEvent ()
8.2.1 Handle the padding property
Use getPaddingLeft(),getPaddingRight() to get the padding.
8.2.3 Processing the WRAP_content attribute
Set the default width and height for the custom View:
**8.2.4 Customizing attributes **
8.3 User-defined Composite Controls
1. Inherit relative components from RelativeLayout
2. You can use findViewById
3. You can also customize attributes
4. Custom attributes need to be added
Schemas: XMLNS: app = “HTTP: / / schemas.android.com/apk/res-auto” namespace
8.4 Customizing a ViewGroup
1. Rewrite onLayout()
2. Process the wrAP_content attribute
3. Reset the width and height according to whether there are child elements
4. Rewrite the onLayout method.
If the child element is not GONE, then the layout method of the child element is called to place it in the appropriate position.
8.4.4 Handling sliding conflicts
This custom ViewGroup slides horizontally, and if there is a ListView inside, the ListView slides vertically, which causes a slide conflict. The solution is, if we detect a horizontal slide direction, let the parent View intercept, make sure the parent View is used for the View slide switch.
8.4.5 Elastic slide to other pages
In the onTouchEvent method, you need to swipe between pages, and you need to use Scroller.
Get the coordinates of the click event as soon as you enter the onTouchEvent method. Use the scrollBy method in MotionEvent.ACTION_MOVE to handle the effect of the HorizontalView control swiping. If the width is greater than 1/2, call Scroller to slide.
8.4.6 Quickly Swipe to another page
Use the VelocityTracker to test the sliding speed.
Usage:
- tracker = VelocityTracker.obtain();
- tracker.computeCurrentVelocity(1000); // How many seconds
- tracker.getXVelocity();
- tracker.clear()
8.4.7 Touching the screen again prevents the page from continuing to slide
Call scroll.isfinished () to determine whether the Scroller is sliding. If so, call scroll.abortAnimation to terminate the sliding.
For your own learning use only.