Summary
When I was making a novel reader, I met a magical pit. Then I searched for a lot of information, read some source code, and just had some guesses. Here's a record. If anyone knows the specific reasons, please don't hesitate to give advice.
phenomenon
First, let's look at the phenomenon that I wrote code for testing.
public class MyView extends View {
int count = 1;
private Scroller scroller;
private boolean flag = true;
public MyView(Context context) {
super(context);
scroller = new Scroller(getContext());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("Debug", "draw");
if (flag) {
flag = false;
scroller.startScroll(0, 0, 100, 100, 600);
invalidate();
}
}
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
Log.d("Debug", "compute " + scroller.isFinished());
invalidate();
}
}
}
The output log is shown in Figure 1.
The first draw is the initialization of onDraw, which is no problem. The addition of pairs of compute and draw is the invocation of the system after invoking invalidate. It can be seen that the system invokes compute first and onDraw later. Finally, there's another draw. What's the matter? You can see that isFinish is true when the last commpute returns, indicating that the slide ends, and the computer will issue the last invalidate. Then the system calls compute and onDraw in turn, but the computer fails, so there is only one draw.
Now let's talk about the situation, a page turning effect that was done at that time, responding to finger manipulation in the onTouchEvent() method. When UP or CANCEL opens the automatic page turning animation, use Scroller.
onTouchEvent
if (event.getAction() == MotionEvent.ACTION_UP) {
if (canDragOver()) {
startAnimation(700);
} else {
// Restore original
mTouch.x = mCornerX +0.1f;
mTouch.y = mCornerY +0.1f;
}
}
private void startAnimation(int delayMillis) {
int dx, dy;
if (mCornerX > 0) {
dx = -(int) (mWidth + mTouch.x);
} else {
dx = (int) (mWidth - mTouch.x + mWidth);
}
if (mCornerY > 0) {
dy = (int) (mHeight - mTouch.y) - 1;
} else {
dy = (int) (1 - mTouch.y); // Prevent mTouch.y from eventually turning to 0
}
mScroller.startScroll((int) mTouch.x, (int) mTouch.y, dx, dy,
delayMillis);
invalidate();
}
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
float x = mScroller.getCurrX();
float y = mScroller.getCurrY();
mTouch.x = x;
mTouch.y = y;
//The onDraw method must be redrawn
postInvalidate();
}
}
Then, of course, it's drawn in onDraw according to the coordinates of mTouch.
Next, when DOWN is used, the animation is interrupted first, and then the last page (assuming it is flipped forward) is judged. If it does not return false, no response is made, and the next operation is not accepted.
onTouchEvent
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Interrupt animation
abortAnimation();
mTouch.x = event.getRawX();
mTouch.y = event.getRawY();
calcCornerXY(mTouch.x, mTouch.y);
// Draw the current page
mBookFactory.draw(mCurrentCanvas);
int state = -1;
//Towards the right
if(DragToRight()) {
state = mBookFactory.prePage();
} else {
state = mBookFactory.nextPage();
}
if (state == BookFactory.STATE_SUCCESS) {
mBookFactory.draw(mNextCanvas);
} else if (state == BookFactory.STATE_NULL) {
if (DragToRight()) {
ToastUtil.showShort("Now it's the first page.");
} else {
ToastUtil.showShort("That's the last page.");
}
// Return false directly, do not invalidate, do not accept the next action
Log.d("Debug", "return false");
return false;
} else {
mNextCanvas.drawBitmap(mBookFactory.getmBackgroundBitmap(), 0, 0, null);
startAnimation(700);
Toast.makeText(getContext(), "Loading", Toast.LENGTH_SHORT).show();
}
}
In theory, if I turn from page two to page one, and then from page one to page one, I will not respond, but the results are unexpected, as shown in the figure.
You can see that when I turn to the first page, I turn half of the page and let go. Then I use Start Animation to turn the page automatically. However, I turn right quickly before I finish the page automatically. At this time, I can turn out a corner and then not move. But I did not return to false when I was on DOWN, which means that I would not invalidate (the onTouchEvent method ended up with an invalidate refresh page), but why would I turn the corner?
Then I typed the log before the return false of DOWN and the log in the onDraw method. The output log is as follows
Let's analyze that after we return false, there is another draw, which can't be invalidate called by computeScroll after we return false, because before return false, abort Animation, scroll finish es is true, and mScroller.computeScrollOffset() in computeScroll returns false. So that's the last validate we mentioned above. Before DOWN, a DRAW event was sent into the message queue (see android's Hadler mechanism for details). Then when we DOWN sent a TOUCH event into the message queue, the question arises again. Why did TOUCH come earlier than DRAW?
Guessing and Verification
Here I can only guess that the priority of touch event is relatively high. When sending this event to the message queue, the message.when sending this event to the message queue, the message queue structure is a linked list structure. When entering the queue, the position is determined by comparing when. Setting to 0 is the head of the queue, so priority is given to processing. I checked the source code with this guess, but I didn't find any evidence to confirm my guess. If the result is updated later...