본문 바로가기

IT/Android

안드로이드 pageCurlSample 코드

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