What are the main coordinate parameters? What are the key points to pay attention to?
Answer: Left, Right, top, Bottom Note that these 4 values are actually the relative coordinate values of view and its parent control. It is not the absolute value from the upper left corner of the screen, so you should pay attention to this point.
In addition, X and Y are actually coordinate values relative to the parent control. TranslationX and TranslationY are both 0 by default, which is the offset from the upper left corner of the parent control.
Conversion relationship:
x=left+tranX,y=top+tranY.
Many people don’t understand why this is the case. In fact, if the View moves, such as panning, you should pay attention to the values of top and left will not change.
No matter how you drag the view, the values of x,y,tranX,tranY change with the drag and pan. Just figure this out.
When is it better to use GestureDetector?
Answer: When only sliding needs are required, use the former, and when there is double-clicking and other behaviors, use the latter.
What problem is used to solve?
Answer: The scrollTo and scrollBy of the view are too poor and are completed instantly. The scroller can cooperate with the view's computeScroll to complete the gradient sliding effect. The experience is better.
What should I pay attention to with ScrollBy?
Answer: The former is absolute sliding, while the latter is relative sliding. What is sliding is the content of the view rather than the view itself. This is very important. For example, textview calls these two methods and slides the content of the displayed word.
Generally speaking, we will use scrollBy more. If you pass the value, actually just remember a few rules. Right-left x is positive Otherwise x is negative Up-lower y is negative, otherwise y is positive.
You can take a look at the source codes of these 2:
public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } } public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
I saw that there are 2 variables mScrollX and mScrollY. The values of these 2 units are pixels. The former represents the distance between the left edge of the view and the left edge of the view content. The latter represents the distance between the upper edge of the view and the upper edge of the view content.
5. Use animation to achieve view sliding. What are the consequences?
Answer: In fact, view animation is a movement on the surface of the view, that is, the visual effect presented to the user. The animation itself cannot move the real position of the view. Except for attribute animations. After the animation is played, the view will eventually return to its own position. Of course, you can set the fillafter property to make the view appearance stay at the position after the animation playback is over. So this will have a serious consequence. For example, your button is on the left side of the screen, you now use an animation and set the fillafter property to let it go to the right side. You will find that clicking on the button on the right does not trigger the click event, but clicking on the left can trigger the button on the right is just a look of the view, and the real button has not been moved on the left. If you must do this, you can place a new button in advance at the position after the button on the right, and after the animation is completed, just let the enable on the right and the left side to go.
Doing so can avoid the above problems.
6. How many ways do you want to slide the view? What should you pay attention to? Which scenarios are all suitable for?
Answer: There are three types in total:
a: scrollto, scrollby. This is the simplest, but can only slide the content of the view. The view itself cannot be swiped.
b: Animation. Animation can slide the view content, but pay attention to non-attribute animations. Just like what we said in question 5, it will affect the interaction. Pay more attention when using it. However, most complex sliding effects are completed by attribute animations, which are at the level of killer.
c: Change layout parameters. This is best understood, it is nothing more than dynamically modifying the parameters of margin and other views through Java code. But it is used less. I don't use this method very much.
What is it for? What is the principle?
Answer: Scroller is used to make the view have a sliding gradient effect. The usage is as follows:
package ; import ; import ; import ; import ; /** * Created by Administrator on //. */ public class CustomTextView extends TextView{ private Scroller mScroller; public CustomTextView(Context context) { super(context); mScroller=new Scroller(context); } public CustomTextView(Context context, AttributeSet attrs) { super(context, attrs); mScroller=new Scroller(context); } public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScroller=new Scroller(context); } //Call this method to scroll to the target positionpublic void smoothScrollTo(int fx, int fy) { int dx = fx - (); int dy = fy - (); smoothScrollBy(dx, dy); } //Call this method to set the relative offset of scrollingpublic void smoothScrollBy(int dx, int dy) { //Set the scroll offset of mScroller((), (), dx, dy,); invalidate();//Invalidate() must be called here to ensure that computeScroll() will be called, otherwise the interface may not be refreshed and the scrolling effect will not be seen.} //The most important thing is to use scroller not to miss this method@Override public void computeScroll() { if (()) { scrollTo((),()); //Don't forget to call this method.postInvalidate(); } (); } }
In fact, many people should be able to search the above code. Let's mainly talk about his principles here.
//The parameters are easy to understand. The starting point of the front sliding, the distance between the middle sliding, the last one is the gradient time//And we see that the startScroll method is to set the parameters and there is no sliding code in//Return to the previous demo, we can see that we usually call the invalidate() method immediately after we call this method.public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = (); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = .f / (float) mDuration; } //We all know that invalidate will trigger the draw method of view//We follow up and see that the following code will be called in the draw method://In other words, the computeScroll method will be called and the view itself method//It's empty so it will be left to us to realize it ourselvesint sx = ; int sy = ; if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; } // Then go back to our customtextview and you can see that the computeScroll method we implemented is as follows://You see, in this method, we called the scrollTo method to realize sliding, and after the sliding ends, the view's repaint is triggered again to re-draw the view//Then computeScroll will be triggered again to implement a loop.public void computeScroll() { if (()) { scrollTo((),()); //Don't forget to call this method.postInvalidate(); } (); } //Return true means that the slide has not ended yet false is ended.//In fact, this method is like the interpolation in attribute animation. When you use the startScroll method, you will pass the value of an event.//This method is to calculate the values of scrollx and scrolly every time you use the value of this eventpublic boolean computeScrollOffset() { if (mFinished) { return false; } int timePassed = (int)(() - mStartTime); if (timePassed < mDuration) { switch (mMode) { case SCROLL_MODE: final float x = (timePassed * mDurationReciprocal); mCurrX = mStartX + (x * mDeltaX); mCurrY = mStartY + (x * mDeltaY); break; case FLING_MODE: final float t = (float) timePassed / mDuration; final int index = (int) (NB_SAMPLES * t); float distanceCoef = .f; float velocityCoef = .f; if (index < NB_SAMPLES) { final float t_inf = (float) index / NB_SAMPLES; final float t_sup = (float) (index + ) / NB_SAMPLES; final float d_inf = SPLINE_POSITION[index]; final float d_sup = SPLINE_POSITION[index + ]; velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); distanceCoef = d_inf + (t - t_inf) * velocityCoef; } mCurrVelocity = velocityCoef * mDistance / mDuration * .f; mCurrX = mStartX + (distanceCoef * (mFinalX - mStartX)); // Pin to mMinX <= mCurrX <= mMaxX mCurrX = (mCurrX, mMaxX); mCurrX = (mCurrX, mMinX); mCurrY = mStartY + (distanceCoef * (mFinalY - mStartY)); // Pin to mMinY <= mCurrY <= mMaxY mCurrY = (mCurrY, mMaxY); mCurrY = (mCurrY, mMinY); if (mCurrX == mFinalX && mCurrY == mFinalY) { mFinished = true; } break; } } else { mCurrX = mFinalX; mCurrY = mFinalY; mFinished = true; } return true; }
How many ways are there to change the sliding gradient effect?
Answer: Three types, the first type is scroller, which is also the most commonly used. There is an explanation in question 7. Another type is animation. I won’t talk about animation anymore, it does not fall into the scope of this article. The last one we often use is to use handler to update the status of the view every time interval.
It's very simple to not write the code. Experience it yourself.
How to express the event delivery mechanism in pseudo-code?
answer:
/** * For a root viewgroup, if a click event is accepted, its dispatchTouchEvent method will be called first. * If the onInterceptTouchEvent of this viewgroup returns true, it means that the event is to be intercepted. The next event is * The viewgroup handles itself, so the viewgroup's onTouchEvent method will be called. If this viewgroup onInterceptTouchEvent * Returning false means that I do not intercept the event, and then pass this event to my child element, and then the dispatchTouchEvent of the child element * will be called, and it is such a loop until the event is processed. * */ public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume=false; if (onInterceptTouchEvent(ev)) { consume=onTouchEvent(ev); }else { consume=(ev); } return consume; }
What are the onTouch methods of onTouchEvent, OnClickListerner and OnTouchListener? What are the priority of the three?
Answer: onTouchListener has the highest priority. If the onTouch method returns false, then onTouchEvent will be called, and if it returns true, it will not be called. As for onClick, the lowest priority.
11. What is the order of passing click events?
Answer: Activity-Window-View. Pass from top to bottom in sequence. Of course, if your lowest view onTouchEvent returns false, it means that he doesn't want to deal with it. Then throw it up and don't deal with it.
In the end, Activity handled it by itself. For example, pm issues a task to the leader, but the leader does not do it himself, and the architect a, and small a does not do it to the programmer b. If b does it, then the task will be ended.
If b finds that you can't handle it, then look for a to do it. If a can't handle it, you will continue to make requests upwards, and it may be done in the end.
// Activity's dispatchTouchEvent method was handed over to the window for processing from the beginning.//Win's superDispatchTouchEvent returns true, which will directly end this function. Returning false means//No one handled this incident, and in the end, the activity's onTouchEvent handled it myself. The getwindow here is actually phonewindowpublic boolean dispatchTouchEvent(MotionEvent ev) { if (() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); } //Look at this function of phonewindow, directly pass the event to mDecor@Override public boolean superDispatchTouchEvent(MotionEvent event) { return (event); } //devorview is our rootview. It is the framelayout that is passed in our setContentView.//It's the decorview's subview@Override public final View getDecorView() { if (mDecor == null) { installDecor(); } return mDecor; }
12. How many steps are there in the event?
Answer: At the beginning of the down event, at the end of the up event, there may be an uncertain number of move events in the middle.
How to distribute click events?
answer:
The viewgroup is to determine whether the process of intercepting events will be entered in the two situations of actionMasked == MotionEvent.ACTION_DOWN and mFirstTouchTarget != null to determine whether the process of intercepting events will be entered. If it is an ACTION_DOWN event, it must enter the process of intercepting events. If it is not an ACTION_DOWN event, it depends on whether the mFirstTouchTarget != null. This is a bit confusing, but it is easy to understand. In fact, for an event sequence, down is the beginning of the event, so it must enter the process of whether the event is intercepted, that is, in the if brackets.
mFirstTouchTarget is actually a single linked list structure and it points to the child elements that successfully handle events.
That is to say, if a child element successfully handles the event, the value is not NULL. On the other hand, as long as the viewgroup intercepts the event, mFirstTouchTarget is not NULL, so it will not be executed in parentheses, which indirectly explains a conclusion:
Once a view decides to intercept an event, the event sequence to which the event belongs can only be executed by him. And the onInterceptTouchEvent method will not be called
final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != ; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); (action); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; }
14.: What will happen if a view does not consume down events when processing an event?
Answer: If a view returns false when the down event comes, then the event sequence to which the down event belongs, that is, his subsequent move and up, will not be handled by him, and will be handled by his parent view.
15. What will happen if the view does not consume move or up event?
Answer: Then the event sequence to which this event will disappear, and the parent view will not be processed, and eventually it will be processed by the activity.
Is the default intercept event?
Answer: By default, no events are intercepted, and the onInterceptTouchEvent returns false.
17. Once an event is passed to the view, will the view's onTouchEvent definitely be called?
Answer: Yes, because the view itself does not have an onInterceptTouchEvent method, so as long as the event comes to the view, the onTouchEvent method will definitely be used.
And by default, they are all consumed and return true. Unless this view is unclickable, the so-called unclickable means that clickable and longgclikable are both fail
Button's clickable is true but textview is false.
Does it affect the onTouchEvent return value of the view?
Answer: It does not affect. As long as one of the clickable and longClickable is true, then onTouchEvent will return true.
Can I interfere with the parent element's event distribution in a child element? If so, can all be interfered with?
Answer: Sure, but the down event cannot interfere.
Will it be called every time?
A: Yes, onInterceptTouchEvent won't.
21. How to solve the problem of sliding conflict? What is the idea?
answer. To resolve sliding conflicts, the most important thing is to have a core idea. Which view do you want to respond to your swipe in an event sequence? For example, from top to bottom, which view handles this event, from left to right?
After using business needs to figure out the rest, it will be easy to do. The core method is 2 external interception, that is, father interception, and internal interception, that is, child view interception method. Learn these 2. Basically all sliding conflicts are variants of these 2, and the core code ideas are the same.
External interception method: The idea is to rewrite the onInterceptTouchEvent of the parent container. Sub-elements generally do not need to be managed. It can be easily understood because this is exactly the same as Android's own event handling mechanism.
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercepted = false; int x = (int) (); int y = (int) (); switch (()) { //The down event must not be intercepted. If the intercepted, the subsequent one will not be received.case MotionEvent.ACTION_DOWN: intercepted = false; break; case MotionEvent.ACTION_MOVE: if (Your business needs) { //If you are sure to intercept, go to your onTouchEvent to process the operations and effects after interception.intercepted = true; } else { intercepted = false; } break; case MotionEvent.ACTION_UP: //Up event we usually return false. Generally, the parent container will not intercept it. Because up is the last step of the event. It doesn't make sense to return true here//The only meaning is that the parent element up is intercepted. As a result, the child element cannot receive the up event, the child element must not have the onClick event triggered.//Think about the small detailsintercepted = false; break; default: break; } return intercepted; }
Internal interception method: The internal interception method is a little more complicated, that is, when the event arrives, the parent container does not care about it, and let the child elements decide whether to handle it themselves. If consumed, it is best. If it is not consumed, it will naturally be transferred to the parent container for processing.
Sub-element code:
@Override public boolean dispatchTouchEvent(MotionEvent event) { int x = (int) (); int y = (int) (); switch (()) { case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: if (If the parent container requires this click event) { getParent().requestDisallowInterceptTouchEvent(false); }// Otherwise, it will be automatically processed by the onTouchEvent of your own view.break; case MotionEvent.ACTION_UP: break; default: break; } return (event); }
The father's container code also needs to be modified, in fact, it is to ensure that the father does not intercept down:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (() == MotionEvent.ACTION_DOWN) { return false; } return true; }