@(Alu)
1. on the original
The animation effect in Filpboard I saw in Hencoder's draft a few days ago:
First bb sentence: read this article at the same time, if you feel that I write vague read not understand can be directly pulled to the end of the text, with the complete code to see step by step.
2. implementation
Overall thinking:
Take a picture with your mobile phone and watch it frame by frame for a long time. It suddenly becomes clear that it is a piece of paper. After folding one side, let it rotate around the center of the broken line.
Relevant custom View: Camera controls the folding range, and canves controls rotation.
Specific:
Whenever a pair of folded lines rotates, the icon is always folded up on one side, flat on the other, and centrally symmetrical, so it is drawn in two parts as a whole.
Cutting canvas can be easily realized by using canves'ClipRect() method.
We should also follow the idea of animating canves to make the folding line (cutting line) move. There is a problem here. That is, when the folding line is moved, the content that can be drawn will follow it. What we need is that only the splitting line rotates in the middle of the real-time splitting icon, so we have a little skill.
First of all:
canvas.rotate(-degreeZ); canvas.clipRect(0, -centerY, centerX, centerY);
Again:
canvas.rotate(degreeZ); canvas.drawBitmap(bitmap, x, y, paint);
Well, after the code of the rotating Sketchpad is cut, after the cutting, let him rotate backward at exactly the same angle, and then draw the icon, so as to achieve the purpose of rotating the cutting line before the icon does not move.
Post this part of the code first, and write the rest tomorrow:
protected void onDraw(Canvas canvas) { super.onDraw(canvas); int bitmapWidth = bitmap.getWidth(); int bitmapHeight = bitmap.getHeight(); int centerX = getWidth() / 2; int centerY = getHeight() / 2; int x = centerX - bitmapWidth / 2; int y = centerY - bitmapHeight / 2; // Another part of the motionless canvas.save(); canvas.translate(centerX, centerY); canvas.rotate(-degreeZ); canvas.clipRect(-centerX, -centerY, 0, centerY); canvas.rotate(degreeZ); canvas.translate(-centerX, -centerY); canvas.drawBitmap(bitmap, x, y, paint); canvas.restore(); }
The effect should be as follows:
Next, write the other half that is centrally symmetric with it:
Because it is centrally symmetrical and rotates at the same speed, only the cutting part needs to be changed to be symmetrical, so this part of the code only needs to copy the former part of the code and then modify this line:
canvas.clipRect(-centerX, -centerY, 0, centerY);
For:
canvas.clipRect(0,-centerY,centerX,centerY);
The goal can be achieved.
Complete code:
protected void onDraw(Canvas canvas) { super.onDraw(canvas); int bitmapWidth = bitmap.getWidth(); int bitmapHeight = bitmap.getHeight(); int centerX = getWidth() / 2; int centerY = getHeight() / 2; int x = centerX - bitmapWidth / 2; int y = centerY - bitmapHeight / 2; // Another part of the motionless canvas.save(); canvas.translate(centerX, centerY); canvas.rotate(-degreeZ); canvas.clipRect(-centerX, -centerY, 0, centerY); canvas.rotate(degreeZ); canvas.translate(-centerX, -centerY); canvas.drawBitmap(bitmap, x, y, paint); canvas.restore(); // Another part that has been folded up canvas.save(); canvas.translate(centerX,centerY); canvas.rotate(-degreeZ); canvas.clipRect(0,-centerY,centerX,centerY); canvas.rotate(degreeZ); canvas.translate(-centerX,-centerY); canvas.drawBitmap(bitmap,x,y,paint); canvas.restore(); }
At this point, the effect of running code is ineffective. Because the two parts we draw are always centrally symmetrical, and although we know it's moving, it looks like it's just drawing a static image.
Don't worry, you need to add Camera to the folded half and make it "fold up".
Add Camera classic four steps to the code:
camera.save(); camera.rotateY(degreeY); camera.applyToCanvas(canvas); camera.restore();
Of course, it is not possible to add these codes directly, because we are "folded", which means that the folded part should rotate synchronously with the cutting line.
So the code should be inserted like this:
// Another part that has been folded up canvas.save(); camera.save(); canvas.translate(centerX, centerY); canvas.rotate(-degreeZ); camera.rotateY(degreeY); camera.applyToCanvas(canvas); canvas.clipRect(0, -centerY, centerX, centerY); canvas.rotate(degreeZ); canvas.translate(-centerX, -centerY); canvas.drawBitmap(bitmap, x, y, paint); camera.restore(); canvas.restore();
Then run the code:
Hey, 80% of the original animation requirements have been completed. Now let's add the final animation: Half of the previous immovable parts have been folded up. This is a very simple modification of the immovable part of the code:
// Another part of the motionless canvas.save(); camera.save(); canvas.translate(centerX, centerY); canvas.rotate(-degreeZ); canvas.clipRect(-centerX, -centerY, 0, centerY); canvas.rotate(degreeZ); camera.rotateX(degreeY2); camera.applyToCanvas(canvas); canvas.translate(-centerX, -centerY); canvas.drawBitmap(bitmap, x, y, paint); camera.restore(); canvas.restore();
Run the code, ok, complete~
By the way, there's another detail. Careful observation shows that the rotation speed of the middle rotation animation is getting slower and slower, so use Linear OutSlow In Interpolator as an interpolator.
About attribute animation, there are more customization and details optimization behind, such as rewriting onMeasure(), animation attribute extraction is not said, the overall idea and some details of thinking I feel that I have spoken out, I do not know if it is the only one I understand. I'm lazy. I want to sleep. Want to get off work, want to eat chicken.
Finally, recommend hencoder: http://hencoder.com/ Not only free, but also thieves 6~
Although I guess not many people can insist on seeing it here, so sad, covering their faces.
Project Code Cloud Address: https://gitee.com/alucode/AsFilpBoard
Complete code:
public class FlipBoardView extends View { private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Bitmap bitmap; private Camera camera = new Camera(); int degreeZ; int degreeY; int degreeY2; //Intermediate Rotation Animation private ObjectAnimator animator = ObjectAnimator.ofInt(this, "degreeZ", 0, 270); //First Fold-up Picture private ObjectAnimator animator1 = ObjectAnimator.ofInt(this, "degreeY", 0, -45); //The Last Fold Picture private ObjectAnimator animator2 = ObjectAnimator.ofInt(this, "degreeY2", 0, -45); AnimatorSet animatorSet; public FlipBoardView(Context context) { super(context); } public FlipBoardView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public FlipBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } { //Make z-axis distance adapter for camera to avoid blurring. DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); float newZ = -displayMetrics.density * 6; camera.setLocation(0, 0, newZ); bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.maps); animator.setDuration(1000); animator.setStartDelay(500); animator.setInterpolator(new LinearOutSlowInInterpolator()); animator1.setDuration(800); animator1.setStartDelay(500); animator1.setInterpolator(new LinearInterpolator()); animator2.setDuration(500); animator2.setStartDelay(500); animator2.setInterpolator(new LinearInterpolator()); animatorSet = new AnimatorSet(); animatorSet.playSequentially(animator1, animator, animator2); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } degreeZ = 0; degreeY = 0; degreeY2 = 0; animatorSet.start(); } }); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); animatorSet.start(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); animatorSet.end(); } @SuppressWarnings("unused") public void setDegreeZ(int degreeZ) { this.degreeZ = degreeZ; invalidate(); } @SuppressWarnings("unused") public void setDegreeY(int degreeY) { this.degreeY = degreeY; invalidate(); } public void setDegreeY2(int degreeY2) { this.degreeY2 = degreeY2; invalidate(); } /** * Principle: Drawing two bitmaps from a broken line. In fact, two bitmaps are drawn one by one and then cut and patch together. * * @param canvas ca */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int bitmapWidth = bitmap.getWidth(); int bitmapHeight = bitmap.getHeight(); int centerX = getWidth() / 2; int centerY = getHeight() / 2; int x = centerX - bitmapWidth / 2; int y = centerY - bitmapHeight / 2; canvas.save(); camera.save(); canvas.translate(centerX, centerY); canvas.rotate(-degreeZ); camera.rotateY(degreeY); camera.applyToCanvas(canvas); canvas.clipRect(0, -centerY, centerX, centerY); canvas.rotate(degreeZ); canvas.translate(-centerX, -centerY); canvas.drawBitmap(bitmap, x, y, paint); camera.restore(); canvas.restore(); // Another part of the motionless canvas.save(); camera.save(); canvas.translate(centerX, centerY); canvas.rotate(-degreeZ); canvas.clipRect(-centerX, -centerY, 0, centerY); canvas.rotate(degreeZ); camera.rotateX(degreeY2); camera.applyToCanvas(canvas); canvas.translate(-centerX, -centerY); canvas.drawBitmap(bitmap, x, y, paint); camera.restore(); canvas.restore(); } /** * onMeasure() Daily rewriting * @param widthMeasureSpec w * @param heightMeasureSpec h */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width; int height; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { width = getPaddingLeft() + bitmap.getWidth() + getPaddingRight(); } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { height = getPaddingTop() + bitmap.getHeight() + getPaddingBottom(); } setMeasuredDimension(width, height); } }