/* * Copyright (C) 2016 eschao <esc.chao@gmail.com> * * 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 com.eschao.android.widget.sample.pageflip; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.widget.Toast; import com.eschao.android.widget.pageflip.Page; import com.eschao.android.widget.pageflip.PageFlip; import com.eschao.android.widget.pageflip.PageFlipState; /** * Double pages render * <p> * Some key points here: * <ul> * <li>First page is which page user is clicking on or moving by finger * Sometimes it is left page on screen, sometimes it is right page. * Second page is leftover page against the first page * </li> * <li>mPageNo is always the number of left page instead of first page</li> * </ul> * </p> * <p> * Every screen 'Page' contains 3 page contents, so it need 3 textures: * <ul> * <li>First texture: first page content of this 'Page'</li> * <li>Back texture: the second page content of this 'Page'</li> * <li>Second texture: the third page content of this 'Page'</li> * </ul> * </p> * * @author eschao */ public class DoublePagesRender extends PageRender { /** * Constructor * @see {@link #PageRender(Context, PageFlip, Handler, int)} */ public DoublePagesRender(Context context, PageFlip pageFlip, Handler handler, int pageNo) { super(context, pageFlip, handler, pageNo); } /** * Draw page frame */ public void onDrawFrame() { // 1. delete unused textures to save memory mPageFlip.deleteUnusedTextures(); // 2. there are two pages for representing the whole screen, we need to // draw them one by one final Page first = mPageFlip.getFirstPage(); final Page second = mPageFlip.getSecondPage(); // 3. check if the first texture is valid for first page, if not, // create it with relative content if (!first.isFirstTextureSet()) { drawPage(first.isLeftPage() ? mPageNo : mPageNo + 1); first.setFirstTexture(mBitmap); } // 4. check if the first texture is valid for second page if (!second.isFirstTextureSet()) { drawPage(second.isLeftPage() ? mPageNo : mPageNo + 1); second.setFirstTexture(mBitmap); } // 5. handle drawing command triggered from finger moving and animating if (mDrawCommand == DRAW_MOVING_FRAME || mDrawCommand == DRAW_ANIMATING_FRAME) { // before drawing, check if back texture of first page is valid // Remember: the first page is always the fold page if (!first.isBackTextureSet()) { drawPage(first.isLeftPage() ? mPageNo - 1 : mPageNo + 2); first.setBackTexture(mBitmap); } // check the second texture of first page is valid. if (!first.isSecondTextureSet()) { drawPage(first.isLeftPage() ? mPageNo - 2 : mPageNo + 3); first.setSecondTexture(mBitmap); } // draw frame for page flip mPageFlip.drawFlipFrame(); } // draw stationary page without flipping else if (mDrawCommand == DRAW_FULL_PAGE){ mPageFlip.drawPageFrame(); } // 6. send message to main thread to notify drawing is ended so that // we can continue to calculate next animation frame if need. // Remember: the drawing operation is always in GL thread instead of // main thread Message msg = Message.obtain(); msg.what = MSG_ENDED_DRAWING_FRAME; msg.arg1 = mDrawCommand; mHandler.sendMessage(msg); } /** * Handle GL surface is changed * * @param width surface width * @param height surface height */ public void onSurfaceChanged(int width, int height) { // recycle bitmap resources if need if (mBackgroundBitmap != null) { mBackgroundBitmap.recycle(); } if (mBitmap != null) { mBitmap.recycle(); } // create bitmap and canvas for page //mBackgroundBitmap = background; Page page = mPageFlip.getFirstPage(); int pageW = (int)page.width(); int pageH = (int)page.height(); mBitmap = Bitmap.createBitmap(pageW, pageH, Bitmap.Config.ARGB_8888); mCanvas.setBitmap(mBitmap); LoadBitmapTask.get(mContext).set(pageW, pageH, 2); } /** * Handle ended drawing event * In here, we only tackle the animation drawing event, If we need to * continue requesting render, please return true. Remember this function * will be called in main thread * * @param what event type * @return ture if need render again */ public boolean onEndedDrawing(int what) { if (what == DRAW_ANIMATING_FRAME) { boolean isAnimating = mPageFlip.animating(); // continue animating if (isAnimating) { mDrawCommand = DRAW_ANIMATING_FRAME; return true; } // animation is finished else { // should handle forward flip to update page number and exchange // textures between first and second pages. Don't have to handle // backward flip since there is no such state happened in double // page mode if (mPageFlip.getFlipState() == PageFlipState.END_WITH_FORWARD) { final Page first = mPageFlip.getFirstPage(); final Page second = mPageFlip.getSecondPage(); second.swapTexturesWithPage(first); // update page number for left page if (first.isLeftPage()) { mPageNo -= 2; } else { mPageNo += 2; } } mDrawCommand = DRAW_FULL_PAGE; return true; } } return false; } /** * Draw page content * * @param number page number */ private void drawPage(int number) { final int width = mCanvas.getWidth(); final int height = mCanvas.getHeight(); Paint p = new Paint(); p.setFilterBitmap(true); // 1. draw background bitmap Bitmap background = LoadBitmapTask.get(mContext).getBitmap(); Rect rect = new Rect(0, 0, width, height); if (width > height) { mCanvas.rotate(90); mCanvas.drawBitmap(background, null, rect, p); mCanvas.rotate(-90); } else { mCanvas.drawBitmap(background, null, rect, p); } background.recycle(); background = null; // 2. draw page number int fontSize = (int)(80 * mContext.getResources().getDisplayMetrics() .scaledDensity); p.setColor(Color.WHITE); p.setStrokeWidth(1); p.setAntiAlias(true); p.setShadowLayer(5.0f, 8.0f, 8.0f, Color.BLACK); p.setTextSize(fontSize); String text = String.valueOf(number); if (number < 1) { text = "Preface"; } else if (number > MAX_PAGES) { text = "End"; } float textWidth = p.measureText(text); float y = height - p.getTextSize() - 20; mCanvas.drawText(text, (width - textWidth) / 2, y, p); if (number == 1) { String firstPage = "The First Page"; p.setTextSize(calcFontSize(16)); float w = p.measureText(firstPage); float h = p.getTextSize(); mCanvas.drawText(firstPage, (width - w) / 2, y + 5 + h, p); } else if (number == MAX_PAGES) { String lastPage = "The Last Page"; p.setTextSize(calcFontSize(16)); float w = p.measureText(lastPage); float h = p.getTextSize(); mCanvas.drawText(lastPage, (width - w) / 2, y + 5 + h, p); } } /** * If page can flip forward * * @return true if it can flip forward */ public boolean canFlipForward() { final Page page = mPageFlip.getFirstPage(); // current page is left page if (page.isLeftPage()) { return (mPageNo > 1); } // current page is right page return (mPageNo + 2 <= MAX_PAGES); } /** * Don't need to handle backward flip * * @return always false */ public boolean canFlipBackward() { return false; } }