package com.aincc.libtest.activity.flip.internal; import javax.microedition.khronos.opengles.GL10; import android.graphics.Bitmap; import android.view.MotionEvent; import android.view.View; import com.aincc.lib.util.Logger; import com.aincc.libtest.activity.flip.FlipViewGroup; /* Copyright 2012 Aphid Mobile 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. */ /** * * <h3><b>FlipCards</b></h3></br> * * 플립카드 관리 (current/prev/next) * * @author aincc@barusoft.com * @version 1.0.0 * @since 1.0.0 */ public class FlipCards { private static final float TIP_SPEED = 1f; private static final int MAX_TIP_ANGLE = 60; private static final int STATE_TIP = 0; private static final float ACCELERATION = 0.618f; private static final float MOVEMENT_RATE = 1.5f; private static final int STATE_TOUCH = 1; private static final int STATE_AUTO_ROTATE = 2; private static final int STATE_STOP = 3; private static final Object LOCK = new Object(); private int tempCurrentViewId = -1; private Position startPosition = Position.init; private Position endPosition = Position.up; private boolean nextPage = false; private View newView; private FlipViewGroup flipViewGroup; /** * 전면 텍스쳐 */ private Texture frontTexture; /** * 전면 상단 카드 */ private Card frontTopCard; /** * 전면 하단 카드 */ private Card frontBottomCard; /** * 후면 텍스쳐 */ private Texture backTexture; /** * 후면 상단 카드 */ private Card backTopCard; /** * 후면 하단 카드 */ private Card backBottomCard; /** * 현재 페이지 비트맵 */ private Bitmap currentBitmap; /** * 이전 페이지 비트맵 */ private Bitmap prevBitmap; /** * 다음 페이지 비트맵 */ private Bitmap nextBitmap; /** * */ private Bitmap upBitmap; /** * */ private Bitmap downBitmap; /** * 각도 */ private float angle = 0f; /** * */ private boolean forward = true; /** * 애니메이션 프레임 */ private int animatedFrame = 0; /** * 상태 */ private int state = STATE_TOUCH; /** * 생성자 * * @since 1.0.0 */ public FlipCards() { frontTopCard = new Card(); frontBottomCard = new Card(); backTopCard = new Card(); backBottomCard = new Card(); frontTopCard.setAxis(Card.AXIS_BOTTOM); frontBottomCard.setAxis(Card.AXIS_TOP); backTopCard.setAxis(Card.AXIS_BOTTOM); backBottomCard.setAxis(Card.AXIS_TOP); } /** * 플립 레이아웃 지정 * * @since 1.0.0 * @param flipViewGroup */ public void setFlipViewGroup(FlipViewGroup flipViewGroup) { this.flipViewGroup = flipViewGroup; } /** * * @since 1.0.0 * @param delta */ public void rotateBy(float delta) { angle += delta; if (angle > 180) angle = 180; else if (angle < 0) angle = 0; } /** * * @since 1.0.0 * @param state */ public void setState(int state) { if (this.state != state) { this.state = state; animatedFrame = 0; } } /** * * @since 1.0.0 * @param gl */ public void draw(GL10 gl) { applyTexture(gl); if (frontTexture == null) return; switch (state) { case STATE_TIP: { if (angle >= 180) forward = false; else if (angle <= 0) forward = true; rotateBy((forward ? TIP_SPEED : -TIP_SPEED)); if (angle > 90 && angle <= 180 - MAX_TIP_ANGLE) { forward = true; } else if (angle < 90 && angle >= MAX_TIP_ANGLE) { forward = false; } } break; case STATE_TOUCH: case STATE_STOP: break; case STATE_AUTO_ROTATE: { animatedFrame++; rotateBy((forward ? ACCELERATION : -ACCELERATION) * animatedFrame); whenPageLies(); // move to whenPageLies(); // if (angle >= 180 || angle <= 0) // { // // setState(STATE_TIP); // 2012.08.28 aincc : 애니메이션 효과 유지 해제로 변경. // setState(STATE_STOP); // } } break; default: // Logger.e("Invalid state: " + state); break; } synchronized (LOCK) { if (Position.down == startPosition) { drawDebendsOnAngleStartDown(gl); } else if (Position.up == startPosition) { drawDebendsOnAngleStartUp(gl); } else if (Position.init == startPosition) { drawInStartInit(gl); } } } /** * 페이지 넘김 시 호출 * * @since 1.0.0 */ private void whenPageLies() { // Logger.v("FlipCards::whenPageLies()"); if (angle >= 180 || angle <= -0) { setState(STATE_STOP); setEndposition(); if (nextPage && startPosition != endPosition) { flipViewGroup.setCurrentItem(tempCurrentViewId); onPageLiesDown(startPosition == Position.up ? true : false); } } } /** * 각도에 따른 endPosition 설정 * * @since 1.0.0 */ private void setEndposition() { if (angle >= 180) endPosition = Position.up; else if (angle <= 0) endPosition = Position.down; } /** * * @since 1.0.0 * @param gl */ private void drawDebendsOnAngleStartDown(GL10 gl) { if (angle < 90) { drawInStartDownLowerPart(gl); } else { drawInStartDownUpperPart(gl); } } /** * * @since 1.0.0 * @param gl */ private void drawDebendsOnAngleStartUp(GL10 gl) { if (angle < 90) { drawInStartUpLowerPart(gl); } else { drawInStartUpUpperPart(gl); } } /** * 초기상태 그리기 * * @since 1.0.0 * @param gl */ private void drawInStartInit(GL10 gl) { // Logger.v("FlipCards::drawInStartInit()"); frontTopCard.setAngle(0); frontTopCard.draw(gl); backTopCard.setAngle(0); backTopCard.draw(gl); backBottomCard.setAngle(0); backBottomCard.draw(gl); frontBottomCard.setAngle(0); frontBottomCard.draw(gl); } /** * * @since 1.0.0 * @param gl */ private void drawInStartDownLowerPart(GL10 gl) { // Logger.v("FlipCards::drawInStartDownLowerPart()"); frontTopCard.setAngle(0); frontTopCard.draw(gl); backBottomCard.setAngle(0); backBottomCard.draw(gl); frontBottomCard.setAngle(angle); frontBottomCard.draw(gl); } /** * * @since 1.0.0 * @param gl */ private void drawInStartDownUpperPart(GL10 gl) { // Logger.v("FlipCards::drawInStartDownUpperPart()"); backTopCard.setAngle(180 - angle); frontTopCard.setAngle(0); frontTopCard.draw(gl); backTopCard.draw(gl); backBottomCard.setAngle(0); backBottomCard.draw(gl); } /** * * @since 1.0.0 * @param gl */ private void drawInStartUpLowerPart(GL10 gl) { // Logger.v("FlipCards::drawInStartUpLowerPart()"); frontBottomCard.setAngle(0); frontBottomCard.draw(gl); backTopCard.setAngle(0); backTopCard.draw(gl); backBottomCard.setAngle(angle); backBottomCard.draw(gl); } /** * * @since 1.0.0 * @param gl */ private void drawInStartUpUpperPart(GL10 gl) { // Logger.v("FlipCards::drawInStartUpUpperPart()"); frontBottomCard.setAngle(0); frontBottomCard.draw(gl); backTopCard.setAngle(0); backTopCard.draw(gl); frontTopCard.setAngle(180 - angle); frontTopCard.draw(gl); } /** * * @since 1.0.0 * @param gl */ private void applyTexture(GL10 gl) { if (upBitmap != null) { if (frontTexture != null) frontTexture.destroy(gl); frontTexture = Texture.createTexture(upBitmap, gl); frontTopCard.setTexture(frontTexture); frontBottomCard.setTexture(frontTexture); frontTopCard.setCardVertices(new float[] { 0f, upBitmap.getHeight(), 0f, // top left 0f, upBitmap.getHeight() / 2.0f, 0f, // bottom left upBitmap.getWidth(), upBitmap.getHeight() / 2f, 0f, // bottom // right upBitmap.getWidth(), upBitmap.getHeight(), 0f // top right }); frontTopCard.setTextureCoordinates(new float[] { 0f, 0f, 0f, upBitmap.getHeight() / 2f / (float) frontTexture.getHeight(), upBitmap.getWidth() / (float) frontTexture.getWidth(), upBitmap.getHeight() / 2f / (float) frontTexture.getHeight(), upBitmap.getWidth() / (float) frontTexture.getWidth(), 0f }); frontBottomCard.setCardVertices(new float[] { 0f, upBitmap.getHeight() / 2f, 0f, // top left 0f, 0f, 0f, // bottom left upBitmap.getWidth(), 0f, 0f, // bottom right upBitmap.getWidth(), upBitmap.getHeight() / 2f, 0f // top // right }); frontBottomCard.setTextureCoordinates(new float[] { 0f, upBitmap.getHeight() / 2f / (float) frontTexture.getHeight(), 0f, upBitmap.getHeight() / (float) frontTexture.getHeight(), upBitmap.getWidth() / (float) frontTexture.getWidth(), upBitmap.getHeight() / (float) frontTexture.getHeight(), upBitmap.getWidth() / (float) frontTexture.getWidth(), upBitmap.getHeight() / 2f / (float) frontTexture.getHeight() }); FlipRenderer.checkError(gl); // upBitmap.recycle(); upBitmap = null; } if (downBitmap != null) { if (backTexture != null) backTexture.destroy(gl); backTexture = Texture.createTexture(downBitmap, gl); backTopCard.setTexture(backTexture); backBottomCard.setTexture(backTexture); backTopCard.setCardVertices(new float[] { 0f, downBitmap.getHeight(), 0f, // top left 0f, downBitmap.getHeight() / 2.0f, 0f, // bottom left downBitmap.getWidth(), downBitmap.getHeight() / 2f, 0f, // bottom // right downBitmap.getWidth(), downBitmap.getHeight(), 0f // top // right }); backTopCard.setTextureCoordinates(new float[] { 0f, 0f, 0f, downBitmap.getHeight() / 2f / (float) backTexture.getHeight(), downBitmap.getWidth() / (float) backTexture.getWidth(), downBitmap.getHeight() / 2f / (float) backTexture.getHeight(), downBitmap.getWidth() / (float) backTexture.getWidth(), 0f }); backBottomCard.setCardVertices(new float[] { 0f, downBitmap.getHeight() / 2f, 0f, // top left 0f, 0f, 0f, // bottom left downBitmap.getWidth(), 0f, 0f, // bottom right downBitmap.getWidth(), downBitmap.getHeight() / 2f, 0f // top // right }); backBottomCard.setTextureCoordinates(new float[] { 0f, downBitmap.getHeight() / 2f / (float) backTexture.getHeight(), 0f, downBitmap.getHeight() / (float) backTexture.getHeight(), downBitmap.getWidth() / (float) backTexture.getWidth(), downBitmap.getHeight() / (float) backTexture.getHeight(), downBitmap.getWidth() / (float) backTexture.getWidth(), downBitmap.getHeight() / 2f / (float) backTexture.getHeight() }); FlipRenderer.checkError(gl); // downBitmap.recycle(); downBitmap = null; } } /** * * @since 1.0.0 */ public void invalidateTexture() { // Texture is vanished when the gl context is gone, no need to delete it explicitly frontTexture = null; backTexture = null; } /** * 텍스쳐 비트맵 재생성 * * @since 1.0.0 * @param currentView * @param frontView * @param backView */ public void reloadTexture(View currentView, View prevView, View nextView) { Logger.v("FlipCards::reloadTexture()"); currentBitmap = FlipUtils.takeScreenShot(currentView); prevBitmap = FlipUtils.takeScreenShot(prevView); nextBitmap = FlipUtils.takeScreenShot(nextView); } /** * * @since 1.0.0 * @param up */ private void onPageLiesDown(boolean up) { Logger.v("FlipCards::onPageLiesDown() up = " + up); if (!up) { prevBitmap = currentBitmap; currentBitmap = nextBitmap; nextBitmap = FlipUtils.takeScreenShot(newView); } else { nextBitmap = currentBitmap; currentBitmap = prevBitmap; prevBitmap = FlipUtils.takeScreenShot(newView); } } /** * * @since 1.0.0 * @param up */ private void reloadTextures(boolean up) { upBitmap = currentBitmap; if (!up) { downBitmap = nextBitmap; } else { downBitmap = prevBitmap; } } /** * * @since 1.0.0 */ public void reloadFirstTexture() { upBitmap = currentBitmap; } /** * * @since 1.0.0 */ private void reloadEndPointTextures() { upBitmap = currentBitmap; downBitmap = currentBitmap; } /** * * @since 1.0.0 * @param up * @param tempCurrentViewId * @param newView */ public void handleTouchEventDown(boolean up, int tempCurrentViewId, View newView) { if (angle < 180 && angle > 0) return; this.newView = newView; this.tempCurrentViewId = tempCurrentViewId; setStartAngleAndtextures(up); startPosition = up ? Position.up : Position.down; } /** * * @since 1.0.0 * @param up */ private void setStartAngleAndtextures(boolean up) { Logger.v("FlipCards::setStartAngleAndtextures() up = " + up); if (up && prevBitmap != null) { reloadTextures(up); synchronized (LOCK) { angle = 180f; } nextPage = true; } else if (!up && nextBitmap != null) { reloadTextures(up); synchronized (LOCK) { angle = 0f; } nextPage = true; } else { reloadEndPointTextures(); nextPage = false; } Logger.v("FlipCards::setStartAngleAndtextures() nextPage = " + nextPage); } /** * 마지막 Y 좌표 */ private float lastY = -1; /** * * @since 1.0.0 * @param event * @return */ public boolean handleTouchEvent(MotionEvent event) { if (frontTexture == null) return false; float delta; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastY = event.getY(); setState(STATE_TOUCH); return true; case MotionEvent.ACTION_MOVE: delta = lastY - event.getY(); rotateBy(180 * delta / frontTexture.getContentHeight() * MOVEMENT_RATE); lastY = event.getY(); return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: delta = lastY - event.getY(); rotateBy(180 * delta / frontTexture.getContentHeight() * MOVEMENT_RATE); if (angle < 90) forward = false; else forward = true; setState(STATE_AUTO_ROTATE); return true; } return false; } } /** * * <h3><b>Position</b></h3></br> * * @author aincc@barusoft.com * @version 1.0.0 * @since 1.0.0 */ enum Position { up, down, init }