Back to project page Android-FoldingLayout.
The source code is released under:
Copyright (c) <YEAR>, Elements Interactive Holding B.V., the Netherlands All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided ...
If you think the Android project Android-FoldingLayout listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright 2014 Element Interactive/*from w w w. jav a 2 s . c o m*/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package nl.elements.flipanimation; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Camera; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; /** * An extension of FrameLayout that allows changing the visible child using Folding animations from * page to page */ public class FoldingLayout extends FrameLayout { public static final int TOP_FRONT = 0; public static final int TOP_BACK = 1; public static final int BOTTOM_FRONT = 2; public static final int BOTTOM_BACK = 3; public static final int FOLD_DOWN=-1; public static final int FOLD_UP=1; private View[] views; private int currentView = 0; private Bitmap[] rectangles = new Bitmap[4]; private boolean folding; private long startTime; private float interpolatedTime; private Interpolator interpolator; private long duration = 500; private float deltaTime; private int nextView; private int foldingDirection=FOLD_DOWN; private int halfHeight; private int halfWidth; Paint p=new Paint(); private OnFoldListener onFoldListener; public FoldingLayout(Context context) { super(context); init(); } public FoldingLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FoldingLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { setWillNotDraw(false); invalidate(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (!folding) { views = new View[getChildCount()]; for (int i = 0; i < getChildCount(); i++) { views[i] = getChildAt(i); } showSingleChild(0); } } /** * Sets all views gone except one * * @param i The index of the view to show */ private void showSingleChild(int i) { hideAllViews(); views[i].setVisibility(VISIBLE); } /** * Sets all views as Invisible */ private void hideAllViews() { for (View view : views) { view.setVisibility(INVISIBLE); } } /** * Starts a fold */ public void fold() { if (folding) { return; } if (interpolator==null) { interpolator=new LinearInterpolator(); } nextView=(currentView+foldingDirection); if (nextView<0) { nextView=views.length-1; } if (nextView>views.length-1) { nextView=0; } if (foldingDirection==FOLD_UP) { generateRectangles(views[currentView], views[nextView]); }else { generateRectangles( views[nextView],views[currentView]); } hideAllViews(); invalidate(); folding = true; startTime = System.currentTimeMillis(); invalidate(); } /** * Reverses direction and folds */ public void foldReverse() { reverseDirection(); fold(); } /** * Folds into a determinate direction. The direction remains settled after fold * @param direction the new direction */ public void foldDirection(int direction) { setDirection(direction); fold(); } /** * Sets the direction * @param direction */ public void setDirection(int direction) { foldingDirection=direction; } /** * change the current direction, no animation is done */ public void reverseDirection() { foldingDirection=-foldingDirection; } @Override protected void onDraw(Canvas canvas) { if (folding) { halfHeight=getMeasuredHeight()/2; halfWidth=getMeasuredWidth()/2; deltaTime = System.currentTimeMillis() - startTime; interpolatedTime = interpolator.getInterpolation ((float) deltaTime / duration); if (foldingDirection==FOLD_DOWN) { interpolatedTime=1-interpolatedTime; } if (deltaTime >= duration) { stopAnimation(); }else { //Clear the canvas and draw the two static halves canvas.drawColor(0x00000000); canvas.drawBitmap(rectangles[TOP_BACK], 0, 0, null); canvas.drawBitmap(rectangles[BOTTOM_FRONT], 0, halfHeight, null); if (interpolatedTime < 0.5) { //First half of fold Matrix matrix = new Matrix(); Camera camera = new Camera(); camera.save(); camera.rotateX(-interpolatedTime * 180); camera.getMatrix(matrix); //Bitmap rotation center should be moved to 0,0,0 position before rotation matrix.preTranslate(-halfWidth, -halfHeight ); //And then put it back matrix.postTranslate(halfWidth, halfHeight ); //Apply light p.setColorFilter(applyLightness((int) (-interpolatedTime * 100))); canvas.drawBitmap(rectangles[TOP_FRONT], matrix, p); camera.restore(); } else { //Second half of fold Matrix matrix = new Matrix(); Camera camera = new Camera(); camera.save(); camera.rotateX(((1f - interpolatedTime) * 180)); camera.getMatrix(matrix); //Bitmap rotation center should be moved to 0,0,0 position before rotation matrix.preTranslate(-halfWidth, 0); //And then put it back and move it down matrix.postTranslate(halfWidth, halfHeight ); //Apply light p.setColorFilter(applyLightness((int) (interpolatedTime * 100 - 100))); canvas.drawBitmap(rectangles[BOTTOM_BACK], matrix, p); camera.restore(); } //Invoke next onDraw invalidate(); } }else{ super.onDraw(canvas); } } /** * Setup and releases all things to stop the animation */ private void stopAnimation() { folding = false; currentView=nextView; showSingleChild(currentView); invalidate(); if (onFoldListener != null) { onFoldListener.onFoldFinished(currentView, views[currentView], foldingDirection); } } /** * Generates a PorterDuff that will light or darken the applied paint * @param progress The amount of light. -100 for dark, 100 for light, 0 for normal * @return The PorterDuffColorFilter with the requested light ready to apply */ public static PorterDuffColorFilter applyLightness(int progress) { if(progress>0) { int value = (int) progress*255/100; return new PorterDuffColorFilter(Color.argb(value, 255, 255, 255), PorterDuff.Mode.SRC_OVER); } else { int value = (int) (progress*-1)*255/100; return new PorterDuffColorFilter(Color.argb(value, 0, 0, 0), PorterDuff.Mode.SRC_ATOP); } } /** * Generates the rectangles arrays with the top and bottom half of each view * * @param frontView The frontview for the animation * @param backView The backView for the animation */ private void generateRectangles(View frontView, View backView) { rectangles[TOP_FRONT] = clipBitmap(frontView, 0, frontView.getHeight() / 2); rectangles[BOTTOM_FRONT] = clipBitmap(frontView, frontView.getHeight() / 2, frontView.getHeight() ); rectangles[TOP_BACK] = clipBitmap(backView, 0, backView.getHeight() / 2); rectangles[BOTTOM_BACK] = clipBitmap(backView, backView.getHeight() / 2, backView.getHeight()); } /** * returns a new Bitmap with a clipped vertical area of the original view cache * * @param view The view to clip * @param top Top Y coordinate where to start to clip * @param bottom Lower Y coordinate where to end the clip * @return A new Bitmap with the clipped area */ private Bitmap clipBitmap(View view, int top, int bottom) { view.buildDrawingCache(false); Bitmap fullImage = view.getDrawingCache(); Bitmap clipView = Bitmap.createBitmap(fullImage.getWidth(), fullImage.getHeight() / 2, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(clipView); Rect source = new Rect(0, top, fullImage.getWidth(), bottom); Rect target = new Rect(0, 0, clipView.getWidth(), clipView.getHeight()); c.drawBitmap(fullImage, source, target, null); view.destroyDrawingCache(); return clipView; } public interface OnFoldListener{ public void onFoldFinished(int position, View view, int direction); } /** * How long this animation should last * @return the duration in milliseconds of the animation */ public long getDuration() { return duration; } /** * How long this animation should last. The duration cannot be negative. * @param duration Duration in milliseconds */ public void setDuration(long duration) { this.duration = duration; } /** * Sets the acceleration curve for this animation. Defaults to a linear interpolation. * @param interpolator The interpolator which defines the acceleration curve */ public void setInterpolator(Interpolator interpolator) { this.interpolator = interpolator; } /** * Gets the acceleration curve type for this animation. * @return the Interpolator associated to this animation */ public Interpolator getInterpolator() { return interpolator; } /** * The listener of the fold process ending * @return The current OnFoldListener object assigned */ public OnFoldListener getOnFoldListener() { return onFoldListener; } /** * Who will be notified once the folding is finished * @param onFoldListener Any object implementing OnFoldListener interface */ public void setOnFoldListener(OnFoldListener onFoldListener) { this.onFoldListener = onFoldListener; } }