当前位置:网站首页>View touch analysis
View touch analysis
2022-06-26 14:50:00 【jxq1994】
Android User message
User messages refer to the conversion of hardware physical messages into messages through the message processing front end Framework Internally defined unified format messages . These messages are currently divided into three categories , They are key messages (KeyEvent)、 Point message (Pointer) Or touch message 、 trackball
( Trackball) news . Focus on the first two categories .
Push button messages
The implementation class of key message is android.view.KeyEvent, This class defines the parameters contained in the message , And how to get these parameters
API Interface .
getAction(): This function returns the key action ,DOWN perhaps UPgetKeyCode(): This function returns the key code , These codes are Android Internally defined , The original message must be forwarded
Only with this code can it be Framework Handle . Such as digital 0〜9, word operator A〜Z etc. . Of course , If you need the original message key value of the hardware , CallablegetScanCode()get .getRepeat(): This function returns the number of times it has been repeated since it was pressed . Simple speak , If in DOS Window , Press and hold the character “A ”
Key not put , that Well getRepeat() The number of times is equal to that displayed on the screen A Number of minus 1.
Two points should be paid attention to in the above process .
The first point ,loop Times from 1 To 2, This process often takes more time , and from 2 To 3、3 To 4、4 To 5 The waiting time is the same , And less than from 1 To 2.
Second point ,View The messages available within the system have been processed by the front end , This process includes the delay of the first key press The reason is , and stay View Long press monitoring is also carried out internally , therefore , Actually from DOWN Press... When the message arrives , The message processing callback time consists of two parts , One is the first delay specified by the message processing front end , The other is View An internally defined long press time limit .
Touch the message
The implementation class of touch message is in android.view.MotionEvent in , This class defines message parameters related to touch , And provides
A group of API The interface lets the user get these parameters .
getAction(): Get message action , The definition of touch message action is much larger than that of key message action , The reason is that most current touch screens support multi touch , therefore , The touch message must contain which point is pressed or released .getEventTime() and getDownTime(): The former obtains the occurrence time of this message , The latter obtains DOWN When the message occurred , If this news is DOWN news , Both values are the same , If this time is not DOWN news , Then it will be the last time before DOWN When the message occurred .getPressure: Get the user's click power , Its value can be greater than 1getX(int index) and getY(int index): Returns the coordinates corresponding to the specified touch point , For multi touch , Parameters index Which point does it represent , from 0 Start .
The overall distribution process of key messages
First , stay ViewRoot A is defined in InputHandler Yes like , When the bottom layer gets the key message , Will call back to the InputHandler Object's handleKey() function , This function calls ViewRoot Medium dispatchKey() function , This function internally sends an asynchronous DISPATC_KEY news , The message processing function is deliverKeyEvent(), The inner part of the function performs the following three steps .
- transfer use
mView.dispatchKeyEventPreIme(), there Prelme It means “ stay Ime” Before , Before the input method . Because for View In terms of system , If an input method window exists , The key message will be sent to the input method window first , Only when the input method window does not process the message , Will continue to send messages to the real view . therefore , For the case that the input method window is displayed , If the application programmer wants to process the message before the input method intercepts it , Can be overloadeddispatchKeyEventPreIme(), So as to process some specific key messages . After executiondispatchKeyEventPreIme()after , If the function returns true, You can directly return to , But before returning, if WmS Request to return a processing receipt , Call firstfinishlnputEvent()Report to WmS This message has been processed , Thus making WmS You can continue to send the next message . - Next, you need to send the message to the input method window . Of course , The IME window must exist at this time , If it doesn't exist , It is directly distributed to the real view .
- transfer use
deliverKeyEventToViewHierarchy(), Send the message to the real view . The function can be executed in four steps .
(1). call
checkForLeavingTouchModeAndConsume()Determine whether this message will cause you to leave touch mode , And will
Consume this message , In general, this function always returns false.
(2) transfer usemView.dispatchKeyEvent()Dispatch messages to the root view . For the application window , The root view isPhoneWindowOfDecorViewobject ; For non application windows ,mView It's anything ViewGroup An implementation of , For example, the status bar window , It's just a FrameLayout View . In this function, the key messages are actually processed , And call back what the programmer has implemented Message processing code .
(3) If the message is not processed in the application , By default, it will determine whether the message will cause a change in the view focus , If it will , Then switch the focus . such as , There are many icons on the desktop , When in the direction “ Up and down ” Key time , Will focus on the previous or next Icon , The specific process is as follows .
1. Determine the direction according to the key value , Of course , If it's not the arrow keys , Will return directly to .
2. transfer use mView.findFocusO, Find the current view mView Subviews with focus in . Generally, you will find the phase Should be a child view , Unless mVeiw All child views in are non Focus Of . If you find it , Is assigned to a temporary variable focused And go on with it .
3. transfer use focused.focusSearch(dir) Look for look for focused The next view of the view . ginseng Count dir It's for direction , Which direction to look for the next focus view . If you find it , And the next view is not the current focus view , Assign the next focus view to the temporary variable V .
4. Now that there is the next focus view , Next, you need to let the view get the focus . Because the next focus window needs to know the upper
The location of a focus area , So as to determine where the next focus window should be focused , So we need to calculate the coordinates of the last focus area first .
Touch the message distribution process
- The touch message is directly sent to the application by the message acquisition module
- Touch the message when processing , It is necessary to calculate which message should be sent according to the touch coordinates View/ViewGroup
- No similar to “ System buttons ” Of “ System touch key ”, The application has complete control over the touch behavior
- The child view takes precedence over the parent view to process messages , That is, the subview processes the message first , Only when the child view consumes the message , Father sees
Only by drawing can we have a chance to deal with , This is the exact opposite of the key message processing .
Touch the overall message distribution process
When the message acquisition module passes pipe Deliver the message to the client ,InputQueue in Of next() Function internal call nativePollOnce() The message will be read in the function . If there's news , Then callback ViewRoot Inside mlnputHandler Object's dispatchMotion() function , This function simply initiates a DISPATCH_POINTER Asynchronous messaging , The message processing function is deliverPointerEvent(). After executing the function , transfer use finishInputEvent() Send a receipt to the message acquisition module , So that it can send the next message , The code that really completes the receipt is native C++ Compiling . Let's introduce deliverPointerEvent() The specific process of :
- Convert physical pixels to logical pixels . In general , The pixels of the physical screen are equal to the screen defined in the operating system
Pixels , There's no need to switch , Only when the two are different do you need to convert . For example, for 800X480 Pixel resolution screen , The operating system defines it as 480X320 Pixels , Touch the physical screen corresponding to the message book , Therefore, it is necessary to convert to the system logical coordinates . - If it is DOWN news , transfer use
ensureTbuchMode(true)The function enters the touch mode , The opposite is true “ Non contact
Touch mode ”, Key mode . This function causes the state of the related view to change . - Convert screen coordinates to view coordinates . The coordinate position of the touch message itself is relative to the upper left corner of the screen , Yes On 800X480
Pixel screen , Views can be considered borderless , The coordinates needed to process messages internally are relative to the view itself , Pictured 13-5 Shown . The method of conversion is very simple , change The amountmCurScrollYThe... Of the view in the screen coordinates is recorded Y Axis roll , Here, please pay attention to , For the root view , No, X The rolling of the shaft , Because the width of the root view has been set to the width of the screen itself . - transfer use
mView.dispatchTouchEvent()Dispatch messages to the root view , This function will then send the message to the whole View Trees . There are two cases of root view , Yes On Activity Included windows , The root view isPhoneWindowMedium DecorView; For non application windows , The root view is just a normal ViewGroup. - If the above root view and all its child views do not consume this message , Finally, the screen boundary offset . The screen boundary is offset at
The program is in English edge slop Express , It is used when the user just touches the screen boundary , The system automatically offsets the original message , Then find whether there is a matching view in the new offset position , If so, send the message to the view . Why Yes “ Screen offset ” Well ? Because for the touch screen , Especially capacitive touch screen , Human fingertips have a certain size , When the boundary is touched , The force will be automatically absorbed to the screen boundary , therefore , Here, we will make a comparison according to the original position of the boundary object messages from the top, bottom, left and right Fixed offset .
Root view internal message dispatch process
First of all to see mView.dispatchTouchEvent() Distribution process of . The function is in ViewRoot In the ,mView The type of
There may be two situations , For the application window ,mView It's a PhoneWindow Medium DecorView type ; For non applications Window ,mView It's average ViewGroup type .
stay DecorView in , First, judge whether there is Callback object , It is the same as when the key message is sent Callback object ,
Just yes Activity class . Such as fruit no Yes Callback object , be straight Pick up transfer use DeeprView The base class ViewGroup Medium dispatchTouchEvent() function .
stay Activity in ,dispatchTouchEvent() The process is as follows .
- If it is ACTION_DOWN news , Call
onUserInteraction(). This is the same as the key message , Give the application a
A chance , In order to do something before message processing , This function does nothing by default . - The call contains Window Object's
superDispatchTouchEvent(). - Such as fruit Window Class does not consume the message , Call
onTouchEvent(), This function does nothing by default , only
Is to give the application a chance to process messages .
Let's see next Window Class superDispatchTouchEvent(). this when Window The implementation of a class is PhoneWindow
class , This function then calls mDecor Of superDispatchTouchEvent(), and stay DecorView In this function of the super.disptchTouchEvent(), namely ViewGroup Of dispatchTouchEvent function . Notice the calling procedure here , General elimination The information processing flow is to execute the next processing logic only when no messages are consumed in the previous step , In the root view DecorView in , When there is no Callback Call when ViewGroup Message processing logic , Not when Callback Call only when no message is consumed ViewGroup Message processing logic , The reason is that Callback Itself will call ViewGroup Message processing logic .
ViewGroup Internal message distribution process
ViewGroup The internal processing logic is also recursive , But it is different from the recursion of key processing . In touch message processing, first
Will send the message to View The last child view in the tree , If the subview does not consume the message , To recursively send to its parent view , and During key message processing , The process of recursion is just the opposite .
- Convert layout coordinates to view coordinates . The concept of these two coordinates is shown in the figure 13-6 Shown .
View coordinates , The size of the view depends on how much content the view itself contains , Not limited by physical screen size . The layout coordinates have Limited , It refers to the layout assigned by the parent view to a child view (layout) size , Areas larger than this size will not be displayed to the parent view In the area of the graph . The method of transformation is also very simple , only Need to be want send use getX()/getY( ) Get layout coordinates , And then add
mScrollX/mScrollYthat will do . For the first call of this function in a recursive call ,getX() The value of is actually on the screen X Axis coordinates ,getY() The value of is on the screen Y Subtract the height of the status bar from the value of the axis .
Why do you want to switch ? Because next, it is necessary to judge that the coordinate point falls into the ViewGroup Which child view in the , The position of the subview is relative to the ViewGroup View coordinates of .
- Handle DOWN news , Its function is to judge which sub view the view coordinates fall into .
( 1 ) First of all, judge the ViewGroup Whether it is forbidden to acquire TOUCH news , If there is no prohibition , And the callback function
onlnterceptTouchEvent()The message is not consumed in , This means that the message can be passed to the child view . If the subview consumes this DOWN news , Then return directly true.
(2) Start looking for subviews . transfer use child.getHitRect(frame) Function to obtain the layout coordinates of the child view in the parent view , namely
The ViewGroup by The child What is the assigned location , This position is relative to the child It is the layout coordinates , In contrast to the
ViewGroup In terms of view coordinates , ginseng Count frame Is the position output rectangle after execution . After getting the position , You can call frame.contain() Method to determine whether the message location is included in the child in , If you include , and And The child Also a ViewGroup, You are ready to call this... Recursively child OfdispatchToiichEvent(), Before calling , First, you need to convert the coordinates again To child In the coordinate system of .
The above process sounds a bit convoluted , For clarity , This process can be represented as shown in Figure 13-7 Shown . The figure illustrates with an example
This coordinate conversion process , The blank area in the figure does not count , The black border is a ViewGroup, It contains five child, Every time Every square is a child, Wide for 60, The bureau is 20, The black dot is the touch position , In the corresponding position child Also a ViewGroup, Contains two white boxes inside View.
( 3 ) In the previous step, the coordinate conversion before recursive operation is completed , Next, judge whether to child Whether it is ViewGroup class . If so, call recursively to ViewGroup Of dispatchToucheEvent(), Start again from the first step ; Such as fruit child No ViewGroup, It is a View, Means the end of the recursive call .
- If it is UP perhaps CANCEL news , Then remove mGroupFlags Medium FLAG_DISALLOW_INTERCEPT mark knowledge , This is allowed ViewGroup Intercept the message . let me put it another way , A common situation is when the user releases the finger , The next time you press , The ViewGroup It can intercept messages again , While pressing and not releasing ,ViewGroup It is not allowed to intercept messages .
- sentence break target Is the variable empty . Empty means that all child windows do not consume the message , So the ViewGroup Itself needs
To process this message . In the second step , If it matches a child , And it's time to child After consuming messages , Will be the child Assign a value to In the parent viewmMotionTargetVariable . In this step , First, restore the original location of the message , Because in the second step , To determine whether the subview contains the position in the message , The position is converted from layout coordinates to view coordinates , In this case, you need to convert the view coordinates to the layout coordinates , Because the next call issuper.dispatchTouchEvent(), namely View Class .View Class , Layout coordinates are required , See the following sections of this chapter for details . After the conversion , Call directlysuper.dispatchTouchEvent(), And return its execution result , This function is just a callback insideonTouchEvent(), Judge before callingmPrivateFlagsInclude inCANCEL_NEXT_UP_EVENTidentification , This identifier will not exist under normal circumstances , If there is , The message will be action Change the type toACTION_CANCEL. - It's about The reason is target There is , And variable disallowlntercept by false, That is, interception is allowed , By default ViewGroup
Are allowed to intercept messages , Only when it's time to ViewGroup The child view of calls the parent viewrequestDisallowdlnterceptTouchEvent()Function time , To prevent the parent view from intercepting messages again , But every time UP News or CANCEL After message , The ViewGroup Again Intercept the message again . Be careful , In this step , If message interception is not allowed , Then... Will not be calledonlnterceptTouchEvent()Letter Count , If allowed , alsoonIntereptToucheEvent()Consumed the message , To perform this step . If this happens , The... Of the message in the code action Change the type to CANCEL, Gen P “ Cancel ”, And then calltarget.dispatchTouchEvent(), Thus, the message tracking that may exist before can be cancelled in the target view , For example, to monitor the long press 、 Specific gestures, etc , After execution Return to true. - In most cases, you will go to this step , namely target There is , also ViewGmup It is not allowed to intercept messages or it is allowed to intercept but does not consume messages , So call
target.dispatchTouchEvent()Continue to send the message to the target view for processing . You need to check before calling this function target Whether it is stated in the to cancel the subsequent message , namelymPrivateFlagsContained in theCANCEL_NEXT_UP_EVENT, If it is , Then send the message action Value is modified to CANCEL, emptymMotionTargetVariable , because target I don't want to deal with the next message , Then it can be said that there is no target 了 .
That's all Touch eliminate Rest stay ViewGroup Internal recursion and dispatch , When analyzing the above processes, pay attention to distinguish onInterceptTouchEvent() and onTouchEvent() .
onInterceptTouchEvent() yes stay ViewGroup As defined in , That is, only ViewGroup Subclasses of can overload this method . and onTouchEvent() Function has two definitions , One is in View Class , all View Subclasses of class can overload the method , Include ViewGmup, The other is in Activity As defined in , use Household Activity This function can be overloaded .View Message processing of the system In mechanism , It will first execute the onTouchEvent, If not dealt with , Will call Activity Medium onTouchEvent(). in addition , Yes On ViewGroup for , In general, you will call onInterceptTouchEvent(), Only if the function does not Consume messages , And the child views it contains do not consume the message , To execute the ViewGroup Of onTouchEvent(). and Yes On View for , no Yes onInterceptTouchEvent() Called . But not all message processing procedures call first onInterceptTouchEvent(), Only the following two cases can be called to onlnterceptTcmchEvent().
- That is to say, on the 2 In the step , when DOWN news , also ViewGroup When message interception is allowed .
- That is to say, on the 5 In the step , When ViewGroup in target object , And allow intercepting messages .
Basic implementation methods of various message monitoring
This section should have introduced messages from ViewGroup Finally distributed to View after ,View Internal process , however , stay View A tracking and monitoring mechanism is used in the internal processing , Its function is to generate long press and other different messages , therefore , This section first introduces the implementation method of this kind of monitoring , Then in the next section, we will introduce View Internal message distribution process .
In touch messages , Three types of monitoring have been implemented , One is called pre-pressed, The other is pressed, And the last one is
Long press , The three are divided by time , Such as chart 13-8 Shown .
real present prison measuring Of The basic principle is to use Handler Send an asynchronous delay message , stay Such as chart 13-8 As shown in , Happen when ACTION_DOWN When the news , First send a delay of t0 Asynchronous messages for , If in t0 Within time , The user released the screen , namely ACTION_UP Message in t0 In the time period , Then this touch process corresponds to pre-pressed Processing code , Its meaning is “ user light touch (tap) For a moment ”. otherwise , The system will start long press monitoring . If the user is t 1 Release the screen within the time period , Then the system thinks that this operation is a “press” operation , Or more than t0 Releasing the screen after time is considered as a long press message , exceed t0 After time , The system is no longer monitored .
The above monitoring process is in View class Of onTouchEvent() Realized , If the application overloads the function , And there is no call super.onTouchEvent(), Then the callback of the above three messages will not be executed . let me put it another way , If overloaded onTouchEvent( ),
Then the View Object's onLongClick() The callback will not be executed .
View Default message dispatch process in
This section analyzes touch messages in View Class .
- call
onFilterTouchEventForSecurity()Process messages when the window is in a blurred state . The so-called fuzzy display means , The application can set the current window to a fuzzy state , At this point, all views inside the window will be displayed as blur effect . This is done to hide the contents of the window , For each of these views , You can set theHLTER_TOUCHES_WHEN_OBSCUREDidentification , If this identification exists , Means that the user wants to not process the message . - Callback view listener's onTouchO function , If the listener consumes the message , Then return directly .
- call onTouchEvent(), Applications can overload this function , But without overloading , This function has a default execution mode . The default execution process is as follows .
(1) Judge whether the view is disable state , If it is , Do nothing , return true, This message is consumed .
(2) Process message broker TouchDelegate. The so-called message broker refers to , You can give someone View Specify a message processing agent , When View On receipt of the message , First, send the message to its agent for processing . If the message is consumed internally by the agent , be View No further processing is required ; If the agent does not handle , be View Continue with the default logic . The comments of this class in the source code say , The purpose of this class is to expand the click area , It's very simple , An example is shown in the figure 13-9 Shown .
The black solid wireframe represents the size of the view itself on the screen , In general , Only when the user clicks on this area , The View The object can handle Touch news . However, sometimes it is hoped that the actual click area can be larger than View Its own area , As shown in the dotted line area , This situation usually occurs in the View When there are no other views around itself , In order to improve the accuracy of clicking, it is deliberately set . The idea is good , But the source code does not really achieve this goal , Because to achieve this goal , The size of the proxy view must be used in the decision to match the message to the corresponding window , Instead of using the size of the view itself , otherwise , This view will be ignored because the clicked position does not fall into the view area . However, the source code has not been judged , the With TouchDelegate It's like nothing .
( 3 ) Determine whether the view is clickable , If not clickable , Then return directly false, That is, the message will not be processed . otherwise , Actually start to execute the default processing logic of the touch message , The logic deals with ACTION4DOWN、MOVE and UP news , The specific process is as follows .
① stay ACTION_DOWN In the news , to mPrivateFlags change The amount add Add PRESSED identification , And change the mHasPerformLongPress Set up by false, however after Qi dynamic ta p monitoring , That is, send an asynchronous delay message , The delay time is ViewConfigration.getTapTimeout().
② For touch messages , The message itself does not repeat Properties of , This is different from the key message , There is only one message at a time DOWN news , And then there's continuity MOVE news , And finally UP End of message . therefore , In this step, it is necessary to MOVE Message processing . The specific logic includes , Determine whether it has moved outside the view area , If it is , shellfish _ except tap or person longPress Monitoring of , And get rid of mPrivateFlags Medium PRESSED identification , And then call refreshDrawableState() Refresh the state of the view , This will cause the background to be redrawn according to the State .
There is something wrong with this processing logic . In a normal GUI In the system , such as HTC magic mobile phone , Or apple system , or person Windows System , When the user presses a button , If you move the cursor outside the button , At this point, the button will return to the normal state from the highlighted state , But if the user moves the cursor back to the button , The button will be highlighted again . However, in the present Android In the system , The design of the code is , When the user moves the cursor back to the view area again , The view does not regain focus , The same is true of the actual test results .
③ Handle ACTION_UP news , The code determines the UP In which monitoring period did the message occur , And according to this different treatment .
a . Check to see if it happened in pre-pressed Within the time frame , If it is , Then give the variable prepressed The assignment is true.
b . Whether it happens in pre-pressed It still happens in press District , Should let this view get the focus , Provided that the view is in Touch You can have focus in mode .
c . If it happens in press after , Long press , Then do nothing , Because the long press message has been processed in the long press asynchronous message processing . If it happens before long press , be change The amount mHasPerformedLongPress by false, Call at this time removeLongPressCallbackO Remove the long press monitoring message that has not been executed .
d . Judgment variable focusTaken Whether the local variable of is false, In general , This variable is false, Because generally, the view defaults to Touch You can't get focus in mode , therefore requestFocus() Returns the false. Of course , Only when you return false Under the circumstances , The UP Messages can cause callbacks performClick() function , otherwise , When focusTaken by true when , The user clicks on a view , This view is just getting the focus , You must click again to execute performClick() function . Because when you click again , The view has gained focus , So... Will not be called requestFocus(), So the variable focusTaken For the initial value false. This step explains tap and press The same and different actions , The same thing is that they both cause the view to focus or execute performClick(), The difference lies in tap Does not change the state of the view , and press Will change the state of the view to PRESSED. From a functional point of view , There is no essential difference between the two , Just a reflection UI The effect is just a little different .
④ It's about The reason is ACTION—CANCEL news , All you need to do here is clear PRESSED identification , Refresh view state , Then turn it off
tap Monitoring is enough .
( 4) Deal with... Separately tap and press action . If it is press action , Then remove PRESSED identification , And change the view state ;
If it is tap, Just send one UnSetPressedState Asynchronous messaging .
(5) call removeTapCallback(), close tap monitoring .
thus ,Touch One processing of the message is over .
Lead to View Time for tree traversal
All over calendar View The tree means the whole View You need to resize and redraw the child views it contains . In general , Lead to
There are three main reasons for re traversal .
- Changes in the internal state of the view itself cause redrawing ; For example, the display property is defined by GONE To VISIBLE;
- View Added or removed from the tree View;
- View Its size and visibility change ; such as TextView The text content in becomes less and less changeable ;
These three cases will eventually call directly or indirectly to View Three functions in :
- requestLayout()
Results in a call to requestLayout() There are two cases of functions :
- setVisibility(), When the application changes the view display properties , Because showing or not showing will affect the position of other sibling views .
- The application directly or indirectly calls this function , Indirect calls are when an application calls View Other functions of class , So as to introduce the call to requestLayout().
- requestFocus()
Generally, it is called directly by the program , Introduction call refers to when the user presses ” On / Next ”、” Left / Right ” Key time , The related processing logic will indirectly call this function .
- invalidate()
Can cause calls to invalidate() Function includes three cases :
- call setVisibility(): When the application changes the view display properties .
- setSelected(): When changing the view Selected In the state of .
- setEnable(): When changing the view Enable In the state of .
Because it is View Tree traversal , Therefore, it will be executed in the top-level parent view ViewRootImpl.scheduleTraversals(); In this method , The system will initiate an asynchronous message ( In the old version, you can directly use Handler Hair , The new version 4.1 Introduced in Choreographer, And right VSync And level three Buffer Support , Make the page display and operation more smooth , For details, please refer to 《Android Project Butter analysis 》), It is then invoked during the execution of asynchronous messages performTraversals() Complete specific View Tree traversal ; See the figure below :

Classification of States
stay View A variety of states related to interface effects are defined in the view . Different states usually display different interface effects , There are several actions that can cause these states to change .Android The application in is executed according to the message mechanism , One message at a time , If the message causes a state change , Then only some status identifiers are made in the code , Then send an asynchronous message , Instead of immediately redrawing . Then in the next message processing , According to the saved status data , Draw different interface effects .
selected and focused The difference between
- Only one view in a window can get focus , When the user presses the key , The view that gets the focus will become highlighted , A window can have multiple views in selected state .
- The key message will eventually be delivered to focused In the view , instead of selected In the view .
- When a view is in pressed In the state of , If you take it selected Status set as false, So the pressed The status will be cleared .
- focused The state is usually caused by key operation ,pressed The status is caused by a touch message ,selected It is completely called by the application setSlected() Control .
- When the view is redrawn , Different background images will be selected according to different current states (selector To define the background ).
View.refreshDrawableState()
This function is used to identify , Assign different values to the view Drawable object .
public void refreshDrawableState() {
// The mPrivateFlags add to PFLAG_DRAWABLE_STATE_DIRTY identification , This identifier is only called later drawableStateChanged Function to determine whether a state change has occurred .
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
// call drawableStateChanged(), This function is a protected type , Heavy duty .ViewGroup This function has been overloaded in just to match FLAG_ADD_STATES_FROM_CHILDREN identification .
drawableStateChanged();
// If the view has a parent view , The parent view is called childdrawableStateChanged. The parent view is either a ViewGroup class , Or a ViewRoot class .
ViewParent parent = mParent;
if (parent != null) {
parent.childDrawableStateChanged(this);
}
}
// about ViewGroup, If it's time to ViewGroup Medium mGroupFlags Contained in the FLAG_ADD_STATES_FROM_CHILDREN identification , It means that ViewGroup Background map
// It can also change with the changes of its child views , So call refreshDrawableState() Refresh the ViewGroup Its own background picture .
// The application can call ViewGroup Of setAddStatesFromChildre() The function will ViewGroup The background of is set to be synchronized with the background image of the subview , When ViewGroup Object tone
// use refreshDrawableState Function time , Will call its own onCreateDrawableState(). In this function, you will first determine whether..., is set
// FLAG_ADD_STATES_FROM_CHILDREN. If there is no such identification , be ViewGroup Just call directly View Class refreshDrawableState() Method , If this identifier exists , Then identify all sub views (pressd、forcus、…) Merge into one , And as the ViewGroup The background of int[] Type of the array .
// The purpose of this design is to make ViewGroup The background state of is synchronized with the background state of the subview .
protected void drawableStateChanged() {
Drawable d = mBackground;
if (d != null && d.isStateful()) {
//d The variable is the background image of the view .setState The interior will be based on int[] by d Find the real Drawable object .
d.setState(getDrawableState());
}
}
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY) == 0)) {
return mDrawableState;
} else {
// call onCreateDrawableState() Convert these states into a int[], The internal format of this array is predefined ,DrawableStateList Class can recognize this int[].
mDrawableState = onCreateDrawableState(0);
mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY;
return mDrawableState;
}
}
View.onFocusedChanged()
This function is used to handle some logic of other state changes caused by focus changes .
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect){
// Judge whether you have gained focus or lost focus .
if (gainFocus) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
InputMethodManager imm = InputMethodManager.peekInstance();
if (!gainFocus) {
if (isPressed()) {
// If you do not get focus but are currently in PRESSED state , Must be cleared PRESSED state .
setPressed(false);
}
if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
// If you are interacting with an input method , Call imm.focusOut(), The input method window is hidden inside this function , And disconnect the input method service from the current window . What we should pay attention to here is , In this step, the current window is interacting with the input method , Because the code is a query variable mAttachInfo.mHasWindowFocus.mAttachInfo yes View The inner class of .mHasWindowFocus Indicates whether the current window is " Front desk window ".
imm.focusOut(this);
}
// call onFocusLost(), See below :
onFocusLost();
} else if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
// If the view is getting focus , call imm.focusIn(). In this function , If the view is editable , Will start the input method , And display the input method window .
imm.focusIn(this);
}
// call invalidate(), Because in general, after the focus changes , Will cause changes in the view background .
invalidate();
// Callback mOnFocusChangeListener().onFocusChange(), The application can implement the callback interface , For other operations .
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(this, gainFocus);
}
if (mAttachInfo != null) {
mAttachInfo.mKeyDispatchState.reset(this);
}
}
protected void onFocusLost() {
resetPressedState();
}
private void resetPressedState() {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return;
}
if (isPressed()) {
// eliminate Pressed state
setPressed(false);
// Delete long press monitoring
if (!mHasPerformedLongPress) {
removeLongPressCallback();
}
}
}
ViewRoot.ensureTouchMode(boolean inTouchMode)
Its function is to process the focus state of the view when the touch mode and the key mode are switched .
boolean ensureTouchMode(boolean inTouchMode) {
if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
+ "touch mode is " + mAttachInfo.mInTouchMode);
// Judge whether the parameter is passed in inTouchMode And current Touch Whether the mode is the same , If it is the same, return directly to false, Don't do anything? . The current value is stored in View.mAttachInfo.mInTouchMode in .
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
// tell the window manager
try {
// The following are the changes that need to be made , First report WindowManagerService Current Touch The pattern changes , because WindowManagerService During customer window layout , According to the customer window Touch Patterns are handled differently .
sWindowSession.setInTouchMode(inTouchMode);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
// handle the change
// call ensureTouchModeLocally() Function to report
return ensureTouchModeLocally(inTouchMode);
}
private boolean ensureTouchModeLocally(boolean inTouchMode) {
if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
+ "touch mode is " + mAttachInfo.mInTouchMode);
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
// First of all mInTouchMode Reassign , Because the current state has changed .
mAttachInfo.mInTouchMode = inTouchMode;
// Every View All of them contain a mAttachInfo object , The object comes from ViewRoot Medium mAttachInfo, There is another... Inside the object mTreeObserver Variable , It's a ViewTreeObserver object , Calling the dispatchOnTouchModeChanged().
mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
// If the function is to enter Touch state , Call enterTouchMode(), Otherwise, call leaveTouchMode().
return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
}
private boolean enterTouchMode() {
if (mView != null) {
// Determine if you have focus , The method is View Class ,ViewGroup This function is overloaded in .View.getFocusChild(), Indicates that the current View(Group) A subview that contains focus , This function returns the current View Direct sub view .
if (mView.hasFocus()) {
// note: not relying on mFocusedView here because this could
// be when the window is first being added, and mFocused isn't
// set yet.
// Find the one who really has the focus View perhaps ViewGroup, there mView Is the root view .
final View focused = mView.findFocus();
// If there is a focus , And the view is not in Touch Ability to get focus in mode , Go straight back to false, In this case, there are few . The application can call setFocusableInTouchMode() Change this attribute , By default , View in Touch You cannot have focus in mode .
if (focused != null && !focused.isFocusableInTouchMode()) {
//【1】
final ViewGroup ancestorToTakeFocus =
findAncestorToTakeFocusInTouchMode(focused);
// If possible , Directly call the... Of the parent view .requestFocus(), And return the result directly . I just can't ask my dad if he has this Touch The ability to gain focus .
if (ancestorToTakeFocus != null) {
// there is an ancestor that wants focus after its descendants that
// is focusable in touch mode.. give it focus
return ancestorToTakeFocus.requestFocus();
} else {
// General procedures will go to this step , It indicates that you can normally enter Touch And need to clear its focus ( I don't have , But it is possible that the parent view has ).
// nothing appropriate to have focus in touch mode, clear it out
// This function will clear the focus in all child views from the root .
mView.unFocus();
// call ViewTreeObserver Of dispatchOnFocusChanged(), So that the application can perform relevant operations on this , Be careful ~ Here is Focus The callback , It's on it Touch The callback .
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
// empty mFocusedView
mFocusedView = null;
// Represents a change .
return true;
}
}
}
}
// No change , I don't have this ability .
return false;
}
【1】 Most of the time ,Touch Cannot have focus in mode , So you need to clear the focus . however , In this step, first judge whether the parent view of the view can be in Touch Have focus in mode , If you can , The parent view is called requestFocus(), And return the result , If you can't, just start to really clear the focus .
private boolean leaveTouchMode() {
if (mView != null) {
// If it's time to View There is focus in the system
if (mView.hasFocus()) {
// i learned the hard way to not trust mFocusedView :)
// Find the one who really has the focus View.
mFocusedView = mView.findFocus();
if (!(mFocusedView instanceof ViewGroup)) {
// If it is View object , There is no need to refocus the view , Go straight back to false.
// some view has focus, let it keep it
return false;
} else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
ViewGroup.FOCUS_AFTER_DESCENDANTS) {
// If the view is a ViewGroup object , And it's time to ViewGroup Object prevents its child views from gaining focus , Then return directly false.(ListView You can get the focus ViewGroup, also ListView It does not directly transfer the focus to the item, Instead, use special logic to make item Focus of attention .)
// some view group has focus, and doesn't prefer its children
// over itself for focus, so let them keep it.
return false;
}
}
// If you don't find a view with focus , Call ViewRoot.focusSearch() Give the view focus .【2】
// find the best view to give focus to in this brave new non-touch-mode
// world
final View focused = focusSearch(null, View.FOCUS_DOWN);
if (focused != null) {
return focused.requestFocus(View.FOCUS_DOWN);
}
}
return false;
}
【2】 Parameters null Represents the view that currently has focus , That is, no view currently has focus .DOWN The representative continues to look down , This is why when the user switches from key mode to Touch Pattern , Then enter the key mode , The previous view with focus cannot get focus again , It's why the first view gets the focus . about ListView for , Last view that got focus , When you switch to the key mode again, you can still get the focus , because ListView Special logic is used for the included item Give focus .
View How to manage the attribute variables of Zhongchao ?
In the vast View Class will involve a lot of status codes , For example, whether it is available 、 Whether it is pressed 、 Whether the location needs to be reassigned 、 Whether redrawing is required, etc ;View The tree will perform corresponding operations according to different variable values when traversing and redrawing , So View Introduced in bit Flag bit to manage these status values , Use them separately mViewFlags and mPrivateFlags Variables to manage ( As the status code increases , In the new version 4.2 There is also mPrivateFlags2/mPrivateFlags3 Variable ), They are all int Type of , That is, theoretically, each variable can be used to indicate 32 Status values , The bitwise operator is used when modifying the status values &| To complete ;
among
mViewFlagsVariables are mainly used to store values related to view state , For example, whether you can click 、 Can I double-click 、 Is it available 、 Whether it has focus, etc ;mPrivateFlagsVariables are mainly used to store attributes related to internal logic , For example, whether the location needs to be reassigned 、 Do you need to redraw 、 Refresh or not View Cache, etc ;
Be careful : There is a close relationship between these two variables , It is often necessary for two variables to set certain status values at the same time , You can see setFlags(…) The specific content of the method ;
requestLayout()
The execution of this method is very simple , Because when View When the tree is rearranged , Always re layout all views , Unlike redrawing, you can specify that only a small area be drawn ;
From the code level, he is just for mPrivateFlags Variable addition FORCE_LAYOUT The logo is just ; Then request layer by layer mParent.requestLayout(); As shown in the figure below :
invalidate()
The purpose of this method is to request View Trees redraw ; Views and their parent views are displayed hierarchically on the interface , The parent view is under the child view , In the process of drawing , First draw the root view at the bottom , Then draw the child views it contains , If the subview is ViewGroup, Then continue to draw its child views , So iterate until there is no child view ;
In the specific redrawing process , Generally, all views are not redrawn , But only those “ Need to draw ” The view of , How to find out “ Need to draw ” What about your area ? This is it. invalidate Method to complete the function !
The general idea is : When View If you need to redraw, you will be given mPrivateFlags Variable addition DRAWN identification , Then determine the rectangular block to be redrawn finally according to all the view boundaries with the mark , This will involve the conversion between different coordinate systems , See the figure below :
The specific execution process of the code is :
- View.invalidate() After setting the necessary status bit ID in , Will perform to the mParent.invalidateChild(…); there mParent There are two situations , One is to have a parent view ViewGroup, The other is that we have reached the top floor ViewRootImpl;
- if ViewGroup, It will be done invalidateChildInParent(…) Then continue to call mParent.invalidateChildInParent(…);
- Finally call to ViewRootImpl.invalidateChildInParent(…), And then perform scheduleTraversals();
Be careful : It will be judged in advance mWillDrawSoon Local variable value , If it is already being implemented performTraversals() Traversal redraws , Then you won't call scheduleTraversals(), It will not initiate redrawing asynchronous messages , but View The various status values set in are still valid , It will only take effect at the next redrawing ;
scheduleTraversals()
This method will be called in multiple places , such as requestLayout()/invalidate() in , And we will often see the case that these two methods are called consecutively , Would that not be twice View Tree traversal redraw request ? In fact, it will not , Because in scheduleTraversals() Method to set a local variable mTraversalScheduled, If you execute requestLayout(), At this time mTraversalScheduled by false, Initiate an asynchronous message request to redraw , And will mTraversalScheduled The variable value is set to true, This then calls invalidate() Temporal judgment mTraversalScheduled The variable value is no longer false 了 , This ensures that only one asynchronous redraw request is initiated ; See the figure below :
performTraversals()
This method is carried out in the system View The core method of tree traversal and page redrawing , The internal logic is still very complicated , about 800 Line code ; To be honest, I haven't fully understood the details yet , There are too many related variables involved ; But the main process is clear , It is based on the previously set status values , Determine whether you need to recalculate the view size (Measure)、 Whether you need to reassign the location of the view ( Also called layout Layout)、 And whether you need to redraw the view (Draw), See the following figure for the framework process , The specific process of each item is detailed in the following specific description :
边栏推荐
猜你喜欢

Attention meets geometry: geometry guided spatiotemporal attention consistency self supervised monocular depth estimation

Sword finger offer 15.65.56 I 56Ⅱ. Bit operation (simple - medium)

MySQL master-slave replication and read-write separation

Question bank and answers of the latest Guizhou construction eight (Mechanics) simulated examination in 2022

Intellij IDEA--格式化SQL文件的方法

'教练,我想打篮球!' —— 给做系统的同学们准备的 AI 学习系列小册

Electron

Matplotlib common operations

Sword finger offer 45.61 Sort (simple)

STM32F1和GD32F1有什么区别?
随机推荐
[solo π] ADB connects multiple mobile phones
Flex & bison start
Error when redis is started: could not create server TCP listening socket *: 6379: bind: address already in use - solution
View触摸分析
vmware部分设置
Question bank and answers of the latest Guizhou construction eight (Mechanics) simulated examination in 2022
秒懂JSONArray和JSONObject的区别和使用
Correlation of XOR / and
Electron
'教练,我想打篮球!' —— 给做系统的同学们准备的 AI 学习系列小册
Deploy the flask environment using the pagoda panel
通俗语言说BM3D
Common evaluation indexes of classification model -- confusion matrix and ROC curve
From Celsius to the three arrows: encrypting the domino of the ten billion giants, and drying up the epic liquidity
权威发布 | 延安大学2022年教师岗位招聘公告
fileinput.js php,fileinput
Atcoder bit operation & Conclusion + formula derivation
Sword finger offer 15.65.56 I 56Ⅱ. Bit operation (simple - medium)
Setup instance of layout manager login interface
Declaration and assignment of go variables