Advertisement
Guest User

Untitled

a guest
Jul 5th, 2012
276
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 33.56 KB | None | 0 0
  1.  package com.custom.curl;
  2.    
  3.     import java.lang.ref.WeakReference;
  4.     import java.util.ArrayList;
  5.    
  6.    
  7.    
  8.     import android.content.Context;
  9.     import android.content.res.TypedArray;
  10.     import android.graphics.Bitmap;
  11.     import android.graphics.BitmapFactory;
  12.     import android.graphics.Canvas;
  13.     import android.graphics.Color;
  14.     import android.graphics.Paint;
  15.     import android.graphics.Path;
  16.     import android.graphics.Rect;
  17.     import android.graphics.Paint.Style;
  18.     import android.os.Handler;
  19.     import android.os.Message;
  20.     import android.text.TextPaint;
  21.     import android.util.AttributeSet;
  22.     import android.util.Log;
  23.     import android.view.MotionEvent;
  24.     import android.view.View;
  25.    
  26.     /**
  27.      *
  28.      * @author Moritz 'Moss' Wundke ([email protected])
  29.      *
  30.      */
  31.     public class PageCurlView extends View {
  32.        
  33.         /** Our Log tag */
  34.         private final static String TAG = "PageCurlView";
  35.        
  36.         // Debug text paint stuff
  37.         private Paint mTextPaint;
  38.         private TextPaint mTextPaintShadow;
  39.        
  40.         /** Px / Draw call */
  41.         private int mCurlSpeed;
  42.        
  43.         /** Fixed update time used to create a smooth curl animation */
  44.         private int mUpdateRate;
  45.        
  46.         /** The initial offset for x and y axis movements */
  47.         private int mInitialEdgeOffset;
  48.        
  49.         /** The mode we will use */
  50.         private int mCurlMode;
  51.        
  52.         /** Simple curl mode. Curl target will move only in one axis. */
  53.         public static final int CURLMODE_SIMPLE = 0;
  54.        
  55.         /** Dynamic curl mode. Curl target will move on both X and Y axis. */
  56.         public static final int CURLMODE_DYNAMIC = 1;
  57.        
  58.         /** Enable/Disable debug mode */
  59.         private boolean bEnableDebugMode = false;
  60.        
  61.         /** The context which owns us */
  62.         private WeakReference<Context> mContext;
  63.        
  64.         /** Handler used to auto flip time based */
  65.         private FlipAnimationHandler mAnimationHandler;
  66.        
  67.         /** Maximum radius a page can be flipped, by default it's the width of the view */
  68.         private float mFlipRadius;
  69.        
  70.         /** Point used to move */
  71.         private Vector2D mMovement;
  72.        
  73.         /** The finger position */
  74.         private Vector2D mFinger;
  75.        
  76.         /** Movement point form the last frame */
  77.         private Vector2D mOldMovement;
  78.        
  79.         /** Page curl edge */
  80.         private Paint mCurlEdgePaint;
  81.        
  82.         /** Our points used to define the current clipping paths in our draw call */
  83.         private Vector2D mA, mB, mC, mD, mE, mF, mOldF, mOrigin;
  84.        
  85.         /** Left and top offset to be applied when drawing */
  86.         private int mCurrentLeft, mCurrentTop;
  87.        
  88.         /** If false no draw call has been done */
  89.         private boolean bViewDrawn;
  90.        
  91.         /** Defines the flip direction that is currently considered */
  92.         private boolean bFlipRight;
  93.        
  94.         /** If TRUE we are currently auto-flipping */
  95.         private boolean bFlipping;
  96.        
  97.         /** TRUE if the user moves the pages */
  98.         private boolean bUserMoves;
  99.    
  100.         /** Used to control touch input blocking */
  101.         private boolean bBlockTouchInput = false;
  102.        
  103.         /** Enable input after the next draw event */
  104.         private boolean bEnableInputAfterDraw = false;
  105.        
  106.         /** LAGACY The current foreground */
  107.         private Bitmap mForeground;
  108.        
  109.         /** LAGACY The current background */
  110.         private Bitmap mBackground;
  111.        
  112.         /** LAGACY List of pages, this is just temporal */
  113.         private ArrayList<Bitmap> mPages;
  114.        
  115.         /** LAGACY Current selected page */
  116.         private int mIndex = 0;
  117.        
  118.         /**
  119.          * Inner class used to represent a 2D point.
  120.          */
  121.         private class Vector2D
  122.         {
  123.             public float x,y;
  124.             public Vector2D(float x, float y)
  125.             {
  126.                 this.x = x;
  127.                 this.y = y;
  128.             }
  129.            
  130.             @Override
  131.             public String toString() {
  132.                 // TODO Auto-generated method stub
  133.                 return "("+this.x+","+this.y+")";
  134.             }
  135.            
  136.              public float length() {
  137.                  return (float) Math.sqrt(x * x + y * y);
  138.              }
  139.        
  140.              public float lengthSquared() {
  141.                      return (x * x) + (y * y);
  142.              }
  143.            
  144.             public boolean equals(Object o) {
  145.                 if (o instanceof Vector2D) {
  146.                     Vector2D p = (Vector2D) o;
  147.                     return p.x == x && p.y == y;
  148.                 }
  149.                 return false;
  150.             }
  151.            
  152.             public Vector2D reverse() {
  153.                 return new Vector2D(-x,-y);
  154.             }
  155.            
  156.             public Vector2D sum(Vector2D b) {
  157.                 return new Vector2D(x+b.x,y+b.y);
  158.             }
  159.            
  160.             public Vector2D sub(Vector2D b) {
  161.                 return new Vector2D(x-b.x,y-b.y);
  162.             }      
  163.    
  164.             public float dot(Vector2D vec) {
  165.                 return (x * vec.x) + (y * vec.y);
  166.             }
  167.    
  168.             public float cross(Vector2D a, Vector2D b) {
  169.                     return a.cross(b);
  170.             }
  171.        
  172.             public float cross(Vector2D vec) {
  173.                     return x * vec.y - y * vec.x;
  174.             }
  175.            
  176.             public float distanceSquared(Vector2D other) {
  177.                 float dx = other.x - x;
  178.                 float dy = other.y - y;
  179.    
  180.                 return (dx * dx) + (dy * dy);
  181.             }
  182.        
  183.             public float distance(Vector2D other) {
  184.                     return (float) Math.sqrt(distanceSquared(other));
  185.             }
  186.            
  187.             public float dotProduct(Vector2D other) {
  188.                 return other.x * x + other.y * y;
  189.             }
  190.            
  191.             public Vector2D normalize() {
  192.                 float magnitude = (float) Math.sqrt(dotProduct(this));
  193.                 return new Vector2D(x / magnitude, y / magnitude);
  194.             }
  195.            
  196.             public Vector2D mult(float scalar) {
  197.                     return new Vector2D(x*scalar,y*scalar);
  198.             }
  199.         }
  200.    
  201.         /**
  202.          * Inner class used to make a fixed timed animation of the curl effect.
  203.          */
  204.         class FlipAnimationHandler extends Handler {
  205.             @Override
  206.             public void handleMessage(Message msg) {
  207.                 PageCurlView.this.FlipAnimationStep();
  208.             }
  209.    
  210.             public void sleep(long millis) {
  211.                 this.removeMessages(0);
  212.                 sendMessageDelayed(obtainMessage(0), millis);
  213.             }
  214.         }
  215.        
  216.         /**
  217.          * Base
  218.          * @param context
  219.          */
  220.         public PageCurlView(Context context) {
  221.             super(context);
  222.             init(context);
  223.             ResetClipEdge();
  224.         }
  225.        
  226.         /**
  227.          * Construct the object from an XML file. Valid Attributes:
  228.          *
  229.          * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
  230.          */
  231.         public PageCurlView(Context context, AttributeSet attrs) {
  232.             super(context, attrs);
  233.             init(context);
  234.            
  235.             // Get the data from the XML AttributeSet
  236.             {
  237.                 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageCurlView);
  238.        
  239.                 // Get data
  240.                 bEnableDebugMode = a.getBoolean(R.styleable.PageCurlView_enableDebugMode, bEnableDebugMode);
  241.                 mCurlSpeed = a.getInt(R.styleable.PageCurlView_curlSpeed, mCurlSpeed);
  242.                 mUpdateRate = a.getInt(R.styleable.PageCurlView_updateRate, mUpdateRate);
  243.                 mInitialEdgeOffset = a.getInt(R.styleable.PageCurlView_initialEdgeOffset, mInitialEdgeOffset);
  244.                 mCurlMode = a.getInt(R.styleable.PageCurlView_curlMode, mCurlMode);
  245.                
  246.                 Log.i(TAG, "mCurlSpeed: " + mCurlSpeed);
  247.                 Log.i(TAG, "mUpdateRate: " + mUpdateRate);
  248.                 Log.i(TAG, "mInitialEdgeOffset: " + mInitialEdgeOffset);
  249.                 Log.i(TAG, "mCurlMode: " + mCurlMode);
  250.                
  251.                 // recycle object (so it can be used by others)
  252.                 a.recycle();
  253.             }
  254.            
  255.             ResetClipEdge();
  256.         }
  257.        
  258.         /**
  259.          * Initialize the view
  260.          */
  261.         private final void init(Context context) {
  262.             // Foreground text paint
  263.             mTextPaint = new Paint();
  264.             mTextPaint.setAntiAlias(true);
  265.             mTextPaint.setTextSize(16);
  266.             mTextPaint.setColor(0xFF000000);
  267.            
  268.             // The shadow
  269.             mTextPaintShadow = new TextPaint();
  270.             mTextPaintShadow.setAntiAlias(true);
  271.             mTextPaintShadow.setTextSize(16);
  272.             mTextPaintShadow.setColor(0x00000000);
  273.            
  274.             // Cache the context
  275.             mContext = new WeakReference<Context>(context);
  276.            
  277.             // Base padding
  278.             setPadding(3, 3, 3, 3);
  279.            
  280.             // The focus flags are needed
  281.             setFocusable(true);
  282.             setFocusableInTouchMode(true);
  283.            
  284.             mMovement =  new Vector2D(0,0);
  285.             mFinger = new Vector2D(0,0);
  286.             mOldMovement = new Vector2D(0,0);
  287.            
  288.             // Create our curl animation handler
  289.             mAnimationHandler = new FlipAnimationHandler();
  290.            
  291.             // Create our edge paint
  292.             mCurlEdgePaint = new Paint();
  293.             mCurlEdgePaint.setColor(Color.WHITE);
  294.             mCurlEdgePaint.setAntiAlias(true);
  295.             mCurlEdgePaint.setStyle(Paint.Style.FILL);
  296.             mCurlEdgePaint.setShadowLayer(10, -5, 5, 0x99000000);
  297.            
  298.             // Set the default props, those come from an XML :D
  299.             mCurlSpeed = 30;
  300.             mUpdateRate = 33;
  301.             mInitialEdgeOffset = 20;
  302.             mCurlMode = 1;
  303.            
  304.             // LEGACY PAGE HANDLING!
  305.            
  306.             // Create pages
  307.             mPages = new ArrayList<Bitmap>();
  308.             mPages.add(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher));
  309.             mPages.add(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher));
  310.            
  311.             // Create some sample images
  312.             mForeground = mPages.get(0);
  313.             mBackground = mPages.get(1);
  314.         }
  315.        
  316.         /**
  317.          * Reset points to it's initial clip edge state
  318.          */
  319.         public void ResetClipEdge()
  320.         {
  321.             // Set our base movement
  322.             mMovement.x = mInitialEdgeOffset;
  323.             mMovement.y = mInitialEdgeOffset;      
  324.             mOldMovement.x = 0;
  325.             mOldMovement.y = 0;    
  326.            
  327.             // Now set the points
  328.             // TODO: OK, those points MUST come from our measures and
  329.             // the actual bounds of the view!
  330.             mA = new Vector2D(mInitialEdgeOffset, 0);
  331.             mB = new Vector2D(this.getWidth(), this.getHeight());
  332.             mC = new Vector2D(this.getWidth(), 0);
  333.             mD = new Vector2D(0, 0);
  334.             mE = new Vector2D(0, 0);
  335.             mF = new Vector2D(0, 0);       
  336.             mOldF = new Vector2D(0, 0);
  337.            
  338.             // The movement origin point
  339.             mOrigin = new Vector2D(this.getWidth(), 0);
  340.         }
  341.        
  342.         /**
  343.          * Return the context which created use. Can return null if the
  344.          * context has been erased.
  345.          */
  346.         private Context GetContext() {
  347.             return mContext.get();
  348.         }
  349.        
  350.         /**
  351.          * See if the current curl mode is dynamic
  352.          * @return TRUE if the mode is CURLMODE_DYNAMIC, FALSE otherwise
  353.          */
  354.         public boolean IsCurlModeDynamic()
  355.         {
  356.             return mCurlMode == CURLMODE_DYNAMIC;
  357.         }
  358.        
  359.         /**
  360.          * Set the curl speed.
  361.          * @param curlSpeed - New speed in px/frame
  362.          * @throws IllegalArgumentException if curlspeed < 1
  363.          */
  364.         public void SetCurlSpeed(int curlSpeed)
  365.         {
  366.             if ( curlSpeed < 1 )
  367.                 throw new IllegalArgumentException("curlSpeed must be greated than 0");
  368.             mCurlSpeed = curlSpeed;
  369.         }
  370.        
  371.         /**
  372.          * Get the current curl speed
  373.          * @return int - Curl speed in px/frame
  374.          */
  375.         public int GetCurlSpeed()
  376.         {
  377.             return mCurlSpeed;
  378.         }
  379.        
  380.         /**
  381.          * Set the update rate for the curl animation
  382.          * @param updateRate - Fixed animation update rate in fps
  383.          * @throws IllegalArgumentException if updateRate < 1
  384.          */
  385.         public void SetUpdateRate(int updateRate)
  386.         {
  387.             if ( updateRate < 1 )
  388.                 throw new IllegalArgumentException("updateRate must be greated than 0");
  389.             mUpdateRate = updateRate;
  390.         }
  391.        
  392.         /**
  393.          * Get the current animation update rate
  394.          * @return int - Fixed animation update rate in fps
  395.          */
  396.         public int GetUpdateRate()
  397.         {
  398.             return mUpdateRate;
  399.         }
  400.        
  401.         /**
  402.          * Set the initial pixel offset for the curl edge
  403.          * @param initialEdgeOffset - px offset for curl edge
  404.          * @throws IllegalArgumentException if initialEdgeOffset < 0
  405.          */
  406.         public void SetInitialEdgeOffset(int initialEdgeOffset)
  407.         {
  408.             if ( initialEdgeOffset < 0 )
  409.                 throw new IllegalArgumentException("initialEdgeOffset can not negative");
  410.             mInitialEdgeOffset = initialEdgeOffset;
  411.         }
  412.        
  413.         /**
  414.          * Get the initial pixel offset for the curl edge
  415.          * @return int - px
  416.          */
  417.         public int GetInitialEdgeOffset()
  418.         {
  419.             return mInitialEdgeOffset;
  420.         }
  421.        
  422.         /**
  423.          * Set the curl mode.
  424.          * <p>Can be one of the following values:</p>
  425.          * <table>
  426.          * <colgroup align="left" />
  427.          * <colgroup align="left" />
  428.          * <tr><th>Value</th><th>Description</th></tr>
  429.          * <tr><td><code>{@link #CURLMODE_SIMPLE com.dcg.pagecurl:CURLMODE_SIMPLE}</code></td><td>Curl target will move only in one axis.</td></tr>
  430.          * <tr><td><code>{@link #CURLMODE_DYNAMIC com.dcg.pagecurl:CURLMODE_DYNAMIC}</code></td><td>Curl target will move on both X and Y axis.</td></tr>
  431.          * </table>
  432.          * @see #CURLMODE_SIMPLE
  433.          * @see #CURLMODE_DYNAMIC
  434.          * @param curlMode
  435.          * @throws IllegalArgumentException if curlMode is invalid
  436.          */
  437.         public void SetCurlMode(int curlMode)
  438.         {
  439.             if ( curlMode != CURLMODE_SIMPLE &&
  440.                  curlMode != CURLMODE_DYNAMIC )
  441.                 throw new IllegalArgumentException("Invalid curlMode");
  442.             mCurlMode = curlMode;
  443.         }
  444.        
  445.         /**
  446.          * Return an integer that represents the current curl mode.
  447.          * <p>Can be one of the following values:</p>
  448.          * <table>
  449.          * <colgroup align="left" />
  450.          * <colgroup align="left" />
  451.          * <tr><th>Value</th><th>Description</th></tr>
  452.          * <tr><td><code>{@link #CURLMODE_SIMPLE com.dcg.pagecurl:CURLMODE_SIMPLE}</code></td><td>Curl target will move only in one axis.</td></tr>
  453.          * <tr><td><code>{@link #CURLMODE_DYNAMIC com.dcg.pagecurl:CURLMODE_DYNAMIC}</code></td><td>Curl target will move on both X and Y axis.</td></tr>
  454.          * </table>
  455.          * @see #CURLMODE_SIMPLE
  456.          * @see #CURLMODE_DYNAMIC
  457.          * @return int - current curl mode
  458.          */
  459.         public int GetCurlMode()
  460.         {
  461.             return mCurlMode;
  462.         }
  463.        
  464.         /**
  465.          * Enable debug mode. This will draw a lot of data in the view so you can track what is happening
  466.          * @param bFlag - boolean flag
  467.          */
  468.         public void SetEnableDebugMode(boolean bFlag)
  469.         {
  470.             bEnableDebugMode = bFlag;
  471.         }
  472.        
  473.         /**
  474.          * Check if we are currently in debug mode.
  475.          * @return boolean - If TRUE debug mode is on, FALSE otherwise.
  476.          */
  477.         public boolean IsDebugModeEnabled()
  478.         {
  479.             return bEnableDebugMode;
  480.         }
  481.    
  482.         /**
  483.          * @see android.view.View#measure(int, int)
  484.          */
  485.         @Override
  486.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  487.             int finalWidth, finalHeight;
  488.             finalWidth = measureWidth(widthMeasureSpec);
  489.             finalHeight = measureHeight(heightMeasureSpec);
  490.             setMeasuredDimension(finalWidth, finalHeight);
  491.         }
  492.    
  493.         /**
  494.          * Determines the width of this view
  495.          * @param measureSpec A measureSpec packed into an int
  496.          * @return The width of the view, honoring constraints from measureSpec
  497.          */
  498.         private int measureWidth(int measureSpec) {
  499.             int result = 0;
  500.             int specMode = MeasureSpec.getMode(measureSpec);
  501.             int specSize = MeasureSpec.getSize(measureSpec);
  502.            
  503.             if (specMode == MeasureSpec.EXACTLY) {
  504.                 // We were told how big to be
  505.                 result = specSize;
  506.             } else {
  507.                 // Measure the text
  508.                 result = specSize;
  509.             }
  510.            
  511.             return result;
  512.         }
  513.    
  514.         /**
  515.          * Determines the height of this view
  516.          * @param measureSpec A measureSpec packed into an int
  517.          * @return The height of the view, honoring constraints from measureSpec
  518.          */
  519.         private int measureHeight(int measureSpec) {
  520.             int result = 0;
  521.             int specMode = MeasureSpec.getMode(measureSpec);
  522.             int specSize = MeasureSpec.getSize(measureSpec);
  523.            
  524.             if (specMode == MeasureSpec.EXACTLY) {
  525.                 // We were told how big to be
  526.                 result = specSize;
  527.             } else {
  528.                 // Measure the text (beware: ascent is a negative number)
  529.                 result = specSize;
  530.             }
  531.             return result;
  532.         }
  533.    
  534.         /**
  535.          * Render the text
  536.          *
  537.          * @see android.view.View#onDraw(android.graphics.Canvas)
  538.          */
  539.         //@Override
  540.         //protected void onDraw(Canvas canvas) {
  541.         //  super.onDraw(canvas);
  542.         //  canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
  543.         //}
  544.        
  545.         //---------------------------------------------------------------
  546.         // Curling. This handles touch events, the actual curling
  547.         // implementations and so on.
  548.         //---------------------------------------------------------------
  549.        
  550.         @Override
  551.         public boolean onTouchEvent(MotionEvent event) {
  552.             if (!bBlockTouchInput) {
  553.                
  554.                 // Get our finger position
  555.                 mFinger.x = event.getX();
  556.                 mFinger.y = event.getY();
  557.                 int width = getWidth();
  558.                
  559.                 // Depending on the action do what we need to
  560.                 switch (event.getAction()) {
  561.                 case MotionEvent.ACTION_DOWN:              
  562.                     mOldMovement.x = mFinger.x;
  563.                     mOldMovement.y = mFinger.y;
  564.                    
  565.                     // If we moved over the half of the display flip to next
  566.                     if (mOldMovement.x > (width >> 1)) {
  567.                         mMovement.x = mInitialEdgeOffset;
  568.                         mMovement.y = mInitialEdgeOffset;
  569.                        
  570.                         // Set the right movement flag
  571.                         bFlipRight = true;
  572.                     } else {
  573.                         // Set the left movement flag
  574.                         bFlipRight = false;
  575.                        
  576.                         // go to next previous page
  577.                         previousView();
  578.                        
  579.                         // Set new movement
  580.                         mMovement.x = IsCurlModeDynamic()?width<<1:width;
  581.                         mMovement.y = mInitialEdgeOffset;
  582.                     }
  583.                    
  584.                     break;
  585.                 case MotionEvent.ACTION_UP:            
  586.                     bUserMoves=false;
  587.                     bFlipping=true;
  588.                     FlipAnimationStep();
  589.                     break;
  590.                 case MotionEvent.ACTION_MOVE:
  591.                     bUserMoves=true;
  592.                    
  593.                     // Get movement
  594.                     mMovement.x -= mFinger.x - mOldMovement.x;
  595.                     mMovement.y -= mFinger.y - mOldMovement.y;
  596.                     mMovement = CapMovement(mMovement, true);
  597.                    
  598.                     // Make sure the y value get's locked at a nice level
  599.                     if ( mMovement.y  <= 1 )
  600.                         mMovement.y = 1;
  601.                    
  602.                     // Get movement direction
  603.                     if (mFinger.x < mOldMovement.x ) {
  604.                         bFlipRight = true;
  605.                     } else {
  606.                         bFlipRight = false;
  607.                     }
  608.                    
  609.                     // Save old movement values
  610.                     mOldMovement.x  = mFinger.x;
  611.                     mOldMovement.y  = mFinger.y;
  612.                    
  613.                     // Force a new draw call
  614.                     DoPageCurl();
  615.                     this.invalidate();
  616.                     break;
  617.                 }
  618.    
  619.             }
  620.            
  621.             // TODO: Only consume event if we need to.
  622.             return true;
  623.         }
  624.        
  625.         /**
  626.          * Make sure we never move too much, and make sure that if we
  627.          * move too much to add a displacement so that the movement will
  628.          * be still in our radius.
  629.          * @param radius - radius form the flip origin
  630.          * @param bMaintainMoveDir - Cap movement but do not change the
  631.          * current movement direction
  632.          * @return Corrected point
  633.          */
  634.         private Vector2D CapMovement(Vector2D point, boolean bMaintainMoveDir)
  635.         {
  636.             // Make sure we never ever move too much
  637.             if (point.distance(mOrigin) > mFlipRadius)
  638.             {
  639.                 if ( bMaintainMoveDir )
  640.                 {
  641.                     // Maintain the direction
  642.                     point = mOrigin.sum(point.sub(mOrigin).normalize().mult(mFlipRadius));
  643.                 }
  644.                 else
  645.                 {
  646.                     // Change direction
  647.                     if ( point.x > (mOrigin.x+mFlipRadius))
  648.                         point.x = (mOrigin.x+mFlipRadius);
  649.                     else if ( point.x < (mOrigin.x-mFlipRadius) )
  650.                         point.x = (mOrigin.x-mFlipRadius);
  651.                     point.y = (float) (Math.sin(Math.acos(Math.abs(point.x-mOrigin.x)/mFlipRadius))*mFlipRadius);
  652.                 }
  653.             }
  654.             return point;
  655.         }
  656.        
  657.         /**
  658.          * Execute a step of the flip animation
  659.          */
  660.         public void FlipAnimationStep() {
  661.             if ( !bFlipping )
  662.                 return;
  663.            
  664.             int width = getWidth();
  665.                
  666.             // No input when flipping
  667.             bBlockTouchInput = true;
  668.            
  669.             // Handle speed
  670.             float curlSpeed = mCurlSpeed;
  671.             if ( !bFlipRight )
  672.                 curlSpeed *= -1;
  673.            
  674.             // Move us
  675.             mMovement.x += curlSpeed;
  676.             mMovement = CapMovement(mMovement, false);
  677.            
  678.             // Create values
  679.             DoPageCurl();
  680.            
  681.             // Check for endings :D
  682.             if (mA.x < 1 || mA.x > width - 1) {
  683.                 bFlipping = false;
  684.                 if (bFlipRight) {
  685.                     //SwapViews();
  686.                     nextView();
  687.                 }
  688.                 ResetClipEdge();
  689.                
  690.                 // Create values
  691.                 DoPageCurl();
  692.    
  693.                 // Enable touch input after the next draw event
  694.                 bEnableInputAfterDraw = true;
  695.             }
  696.             else
  697.             {
  698.                 mAnimationHandler.sleep(mUpdateRate);
  699.             }
  700.            
  701.             // Force a new draw call
  702.             this.invalidate();
  703.         }
  704.        
  705.         /**
  706.          * Do the page curl depending on the methods we are using
  707.          */
  708.         private void DoPageCurl()
  709.         {
  710.             if(bFlipping){
  711.                 if ( IsCurlModeDynamic() )
  712.                     doDynamicCurl();
  713.                 else
  714.                     doSimpleCurl();
  715.                
  716.             } else {
  717.                 if ( IsCurlModeDynamic() )
  718.                     doDynamicCurl();
  719.                 else
  720.                     doSimpleCurl();
  721.             }
  722.         }
  723.        
  724.         /**
  725.          * Do a simple page curl effect
  726.          */
  727.         private void doSimpleCurl() {
  728.             int width = getWidth();
  729.             int height = getHeight();
  730.            
  731.             // Calculate point A
  732.             mA.x = width - mMovement.x;
  733.             mA.y = height;
  734.    
  735.             // Calculate point D
  736.             mD.x = 0;
  737.             mD.y = 0;
  738.             if (mA.x > width / 2) {
  739.                 mD.x = width;
  740.                 mD.y = height - (width - mA.x) * height / mA.x;
  741.             } else {
  742.                 mD.x = 2 * mA.x;
  743.                 mD.y = 0;
  744.             }
  745.            
  746.             // Now calculate E and F taking into account that the line
  747.             // AD is perpendicular to FB and EC. B and C are fixed points.
  748.             double angle = Math.atan((height - mD.y) / (mD.x + mMovement.x - width));
  749.             double _cos = Math.cos(2 * angle);
  750.             double _sin = Math.sin(2 * angle);
  751.    
  752.             // And get F
  753.             mF.x = (float) (width - mMovement.x + _cos * mMovement.x);
  754.             mF.y = (float) (height - _sin * mMovement.x);
  755.            
  756.             // If the x position of A is above half of the page we are still not
  757.             // folding the upper-right edge and so E and D are equal.
  758.             if (mA.x > width / 2) {
  759.                 mE.x = mD.x;
  760.                 mE.y = mD.y;
  761.             }
  762.             else
  763.             {
  764.                 // So get E
  765.                 mE.x = (float) (mD.x + _cos * (width - mD.x));
  766.                 mE.y = (float) -(_sin * (width - mD.x));
  767.             }
  768.         }
  769.    
  770.         /**
  771.          * Calculate the dynamic effect, that one that follows the users finger
  772.          */
  773.         private void doDynamicCurl() {
  774.             int width = getWidth();
  775.             int height = getHeight();
  776.    
  777.             // F will follow the finger, we add a small displacement
  778.             // So that we can see the edge
  779.             mF.x = width - mMovement.x+0.1f;
  780.             mF.y = height - mMovement.y+0.1f;
  781.            
  782.             // Set min points
  783.             if(mA.x==0) {
  784.                 mF.x= Math.min(mF.x, mOldF.x);
  785.                 mF.y= Math.max(mF.y, mOldF.y);
  786.             }
  787.            
  788.             // Get diffs
  789.             float deltaX = width-mF.x;
  790.             float deltaY = height-mF.y;
  791.    
  792.             float BH = (float) (Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2);
  793.             double tangAlpha = deltaY / deltaX;
  794.             double alpha = Math.atan(deltaY / deltaX);
  795.             double _cos = Math.cos(alpha);
  796.             double _sin = Math.sin(alpha);
  797.            
  798.             mA.x = (float) (width - (BH / _cos));
  799.             mA.y = height;
  800.            
  801.             mD.y = (float) (height - (BH / _sin));
  802.             mD.x = width;
  803.    
  804.             mA.x = Math.max(0,mA.x);
  805.             if(mA.x==0) {
  806.                 mOldF.x = mF.x;
  807.                 mOldF.y = mF.y;
  808.             }
  809.            
  810.             // Get W
  811.             mE.x = mD.x;
  812.             mE.y = mD.y;
  813.            
  814.             // Correct
  815.             if (mD.y < 0) {
  816.                 mD.x = width + (float) (tangAlpha * mD.y);
  817.                 mE.y = 0;
  818.                 mE.x = width + (float) (Math.tan(2 * alpha) * mD.y);
  819.             }
  820.         }
  821.    
  822.         /**
  823.          * Swap between the fore and back-ground.
  824.          */
  825.         @Deprecated
  826.         private void SwapViews() {
  827.             Bitmap temp = mForeground;
  828.             mForeground = mBackground;
  829.             mBackground = temp;
  830.         }
  831.        
  832.         /**
  833.          * Swap to next view
  834.          */
  835.         private void nextView() {
  836.             int foreIndex = mIndex + 1;
  837.             if(foreIndex >= mPages.size()) {
  838.                 foreIndex = 0;
  839.             }
  840.             int backIndex = foreIndex + 1;
  841.             if(backIndex >= mPages.size()) {
  842.                 backIndex = 0;
  843.             }
  844.             mIndex = foreIndex;
  845.             setViews(foreIndex, backIndex);
  846.         }
  847.        
  848.         /**
  849.          * Swap to previous view
  850.          */
  851.         private void previousView() {
  852.             int backIndex = mIndex;
  853.             int foreIndex = backIndex - 1;
  854.             if(foreIndex < 0) {
  855.                 foreIndex = mPages.size()-1;
  856.             }
  857.             mIndex = foreIndex;
  858.             setViews(foreIndex, backIndex);
  859.         }
  860.        
  861.         /**
  862.          * Set current fore and background
  863.          * @param foreground - Foreground view index
  864.          * @param background - Background view index
  865.          */
  866.         private void setViews(int foreground, int background) {
  867.             mForeground = mPages.get(foreground);
  868.             mBackground = mPages.get(background);
  869.         }
  870.        
  871.         //---------------------------------------------------------------
  872.         // Drawing methods
  873.         //---------------------------------------------------------------
  874.    
  875.         @Override
  876.         protected void onDraw(Canvas canvas) {
  877.             // Always refresh offsets
  878.             mCurrentLeft = getLeft();
  879.             mCurrentTop = getTop();
  880.            
  881.             // Translate the whole canvas
  882.             //canvas.translate(mCurrentLeft, mCurrentTop);
  883.            
  884.             // We need to initialize all size data when we first draw the view
  885.             if ( !bViewDrawn ) {
  886.                 bViewDrawn = true;
  887.                 onFirstDrawEvent(canvas);
  888.             }
  889.            
  890.             canvas.drawColor(Color.WHITE);
  891.            
  892.             // Curl pages
  893.             //DoPageCurl();
  894.            
  895.             // TODO: This just scales the views to the current
  896.             // width and height. We should add some logic for:
  897.             //  1) Maintain aspect ratio
  898.             //  2) Uniform scale
  899.             //  3) ...
  900.             Rect rect = new Rect();
  901.             rect.left = 0;
  902.             rect.top = 0;
  903.             rect.bottom = getHeight();
  904.             rect.right = getWidth();
  905.            
  906.             // First Page render
  907.             Paint paint = new Paint();
  908.            
  909.             // Draw our elements
  910.             drawForeground(canvas, rect, paint);
  911.             drawBackground(canvas, rect, paint);
  912.             drawCurlEdge(canvas);
  913.            
  914.             // Draw any debug info once we are done
  915.             if ( bEnableDebugMode )
  916.                 drawDebug(canvas);
  917.    
  918.             // Check if we can re-enable input
  919.             if ( bEnableInputAfterDraw )
  920.             {
  921.                 bBlockTouchInput = false;
  922.                 bEnableInputAfterDraw = false;
  923.             }
  924.            
  925.             // Restore canvas
  926.             //canvas.restore();
  927.         }
  928.        
  929.         /**
  930.          * Called on the first draw event of the view
  931.          * @param canvas
  932.          */
  933.         protected void onFirstDrawEvent(Canvas canvas) {
  934.            
  935.             mFlipRadius = getWidth();
  936.            
  937.             ResetClipEdge();
  938.             DoPageCurl();
  939.         }
  940.        
  941.         /**
  942.          * Draw the foreground
  943.          * @param canvas
  944.          * @param rect
  945.          * @param paint
  946.          */
  947.         private void drawForeground( Canvas canvas, Rect rect, Paint paint ) {
  948.             canvas.drawBitmap(mForeground, null, rect, paint);
  949.            
  950.             // Draw the page number (first page is 1 in real life :D
  951.             // there is no page number 0 hehe)
  952.             drawPageNum(canvas, mIndex);
  953.         }
  954.        
  955.         /**
  956.          * Create a Path used as a mask to draw the background page
  957.          * @return
  958.          */
  959.         private Path createBackgroundPath() {
  960.             Path path = new Path();
  961.             path.moveTo(mA.x, mA.y);
  962.             path.lineTo(mB.x, mB.y);
  963.             path.lineTo(mC.x, mC.y);
  964.             path.lineTo(mD.x, mD.y);
  965.             path.lineTo(mA.x, mA.y);
  966.             return path;
  967.         }
  968.        
  969.         /**
  970.          * Draw the background image.
  971.          * @param canvas
  972.          * @param rect
  973.          * @param paint
  974.          */
  975.         private void drawBackground( Canvas canvas, Rect rect, Paint paint ) {
  976.             Path mask = createBackgroundPath();
  977.            
  978.             // Save current canvas so we do not mess it up
  979.             canvas.save();
  980.             canvas.clipPath(mask);
  981.             canvas.drawBitmap(mBackground, null, rect, paint);
  982.            
  983.             // Draw the page number (first page is 1 in real life :D
  984.             // there is no page number 0 hehe)
  985.             drawPageNum(canvas, mIndex);
  986.            
  987.             canvas.restore();
  988.         }
  989.        
  990.         /**
  991.          * Creates a path used to draw the curl edge in.
  992.          * @return
  993.          */
  994.         private Path createCurlEdgePath() {
  995.             Path path = new Path();
  996.             path.moveTo(mA.x, mA.y);
  997.             path.lineTo(mD.x, mD.y);
  998.             path.lineTo(mE.x, mE.y);
  999.             path.lineTo(mF.x, mF.y);
  1000.             path.lineTo(mA.x, mA.y);
  1001.             return path;
  1002.         }
  1003.        
  1004.         /**
  1005.          * Draw the curl page edge
  1006.          * @param canvas
  1007.          */
  1008.         private void drawCurlEdge( Canvas canvas )
  1009.         {
  1010.             Path path = createCurlEdgePath();
  1011.             canvas.drawPath(path, mCurlEdgePaint);
  1012.         }
  1013.        
  1014.         /**
  1015.          * Draw page num (let this be a bit more custom)
  1016.          * @param canvas
  1017.          * @param pageNum
  1018.          */
  1019.         private void drawPageNum(Canvas canvas, int pageNum)
  1020.         {
  1021.             mTextPaint.setColor(Color.WHITE);
  1022.             String pageNumText = "- "+pageNum+" -";
  1023.             drawCentered(canvas, pageNumText,canvas.getHeight()-mTextPaint.getTextSize()-5,mTextPaint,mTextPaintShadow);
  1024.         }
  1025.        
  1026.         //---------------------------------------------------------------
  1027.         // Debug draw methods
  1028.         //---------------------------------------------------------------
  1029.        
  1030.         /**
  1031.          * Draw a text with a nice shadow
  1032.          */
  1033.         public static void drawTextShadowed(Canvas canvas, String text, float x, float y, Paint textPain, Paint shadowPaint) {
  1034.             canvas.drawText(text, x-1, y, shadowPaint);
  1035.             canvas.drawText(text, x, y+1, shadowPaint);
  1036.             canvas.drawText(text, x+1, y, shadowPaint);
  1037.             canvas.drawText(text, x, y-1, shadowPaint);    
  1038.             canvas.drawText(text, x, y, textPain);
  1039.         }
  1040.        
  1041.         /**
  1042.          * Draw a text with a nice shadow centered in the X axis
  1043.          * @param canvas
  1044.          * @param text
  1045.          * @param y
  1046.          * @param textPain
  1047.          * @param shadowPaint
  1048.          */
  1049.         public static void drawCentered(Canvas canvas, String text, float y, Paint textPain, Paint shadowPaint)
  1050.         {
  1051.             float posx = (canvas.getWidth() - textPain.measureText(text))/2;
  1052.             drawTextShadowed(canvas, text, posx, y, textPain, shadowPaint);
  1053.         }
  1054.        
  1055.         /**
  1056.          * Draw debug info
  1057.          * @param canvas
  1058.          */
  1059.         private void drawDebug(Canvas canvas)
  1060.         {
  1061.             float posX = 10;
  1062.             float posY = 20;
  1063.            
  1064.             Paint paint = new Paint();
  1065.             paint.setStrokeWidth(5);
  1066.             paint.setStyle(Style.STROKE);
  1067.            
  1068.             paint.setColor(Color.BLACK);       
  1069.             canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
  1070.            
  1071.             paint.setStrokeWidth(3);
  1072.             paint.setColor(Color.RED);     
  1073.             canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
  1074.            
  1075.             paint.setStrokeWidth(5);
  1076.             paint.setColor(Color.BLACK);
  1077.             canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
  1078.            
  1079.             paint.setStrokeWidth(3);
  1080.             paint.setColor(Color.RED);
  1081.             canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
  1082.            
  1083.             posY = debugDrawPoint(canvas,"A",mA,Color.RED,posX,posY);
  1084.             posY = debugDrawPoint(canvas,"B",mB,Color.GREEN,posX,posY);
  1085.             posY = debugDrawPoint(canvas,"C",mC,Color.BLUE,posX,posY);
  1086.             posY = debugDrawPoint(canvas,"D",mD,Color.CYAN,posX,posY);
  1087.             posY = debugDrawPoint(canvas,"E",mE,Color.YELLOW,posX,posY);
  1088.             posY = debugDrawPoint(canvas,"F",mF,Color.LTGRAY,posX,posY);
  1089.             posY = debugDrawPoint(canvas,"Mov",mMovement,Color.DKGRAY,posX,posY);
  1090.             posY = debugDrawPoint(canvas,"Origin",mOrigin,Color.MAGENTA,posX,posY);
  1091.             posY = debugDrawPoint(canvas,"Finger",mFinger,Color.GREEN,posX,posY);
  1092.            
  1093.             // Draw some curl stuff (Just some test)
  1094.             /*
  1095.             canvas.save();
  1096.             Vector2D center = new Vector2D(getWidth()/2,getHeight()/2);
  1097.             //canvas.rotate(315,center.x,center.y);
  1098.            
  1099.             // Test each lines
  1100.             //float radius = mA.distance(mD)/2.f;
  1101.             //float radius = mA.distance(mE)/2.f;
  1102.             float radius = mA.distance(mF)/2.f;
  1103.             //float radius = 10;
  1104.             float reduction = 4.f;
  1105.             RectF oval = new RectF();
  1106.             oval.top = center.y-radius/reduction;
  1107.             oval.bottom = center.y+radius/reduction;
  1108.             oval.left = center.x-radius;
  1109.             oval.right = center.x+radius;
  1110.             canvas.drawArc(oval, 0, 360, false, paint);
  1111.             canvas.restore();
  1112.             /**/
  1113.         }
  1114.        
  1115.         private float debugDrawPoint(Canvas canvas, String name, Vector2D point, int color, float posX, float posY) {  
  1116.             return debugDrawPoint(canvas,name+" "+point.toString(),point.x, point.y, color, posX, posY);
  1117.         }
  1118.        
  1119.         private float debugDrawPoint(Canvas canvas, String name, float X, float Y, int color, float posX, float posY) {
  1120.             mTextPaint.setColor(color);
  1121.             drawTextShadowed(canvas,name,posX , posY, mTextPaint,mTextPaintShadow);
  1122.             Paint paint = new Paint();
  1123.             paint.setStrokeWidth(5);
  1124.             paint.setColor(color); 
  1125.             canvas.drawPoint(X, Y, paint);
  1126.             return posY+15;
  1127.         }
  1128.    
  1129.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement