본문 바로가기

프로그래밍/Android

뷰 전환시 3차원 에니메이션 적용하기

 

 

뷰 3차원 회전

 

뷰 에니메이션에 대해서 기존에 포스팅 했는데, 이번엔 뷰 전환시 3차원으로

회전하며 전환하는 에니메이션에 대해 정리차원으로 포스팅한다.

 

소스는 안드로이드 sdk의 샘플(API Demos) Views > Animation > 3D Transition

구현되어 있다.

소스코드는 Rotate3dAnimation.java / Transition3d.java

레이아웃은 animations_main_screen.xml

 

1. 에니메이션 클래스 구현

기존 에니메이션 클래스는 2차원 이동과 회전을 지원하고 있는데, 이를 상속받아 3차원 회전 기능을 넣는다. 방식은 갤러리 구현시와 같이 Matrix , Camera를 사용한 회전으로 처리한다.

Animation 클래스의 applyTransformation 메쏘드를 오버라이드해서

뷰의 변화를 직접 코딩해 준다.

 

아래는 Rotate3dAnimation.java 소스

 

public class Rotate3dAnimation extends Animation {

    private final float mFromDegrees;

    private final float mToDegrees;

    private final float mCenterX;

    private final float mCenterY;

    private final float mDepthZ;

    private final boolean mReverse;

    private Camera mCamera;

    /**

     * Creates a new 3D rotation on the Y axis. The rotation is defined by its

     * start angle and its end angle. Both angles are in degrees. The rotation

     * is performed around a center point on the 2D space, definied by a pair

     * of X and Y coordinates, called centerX and centerY. When the animation

     * starts, a translation on the Z axis (depth) is performed. The length

     * of the translation can be specified, as well as whether the translation

     * should be reversed in time.

     *

     * @param fromDegrees the start angle of the 3D rotation

     * @param toDegrees the end angle of the 3D rotation

     * @param centerX the X center of the 3D rotation

     * @param centerY the Y center of the 3D rotation

     * @param reverse true if the translation should be reversed, false otherwise

     */

    public Rotate3dAnimation(float fromDegrees, float toDegrees,

            float centerX, float centerY, float depthZ, boolean reverse) {

        mFromDegrees = fromDegrees;

        mToDegrees = toDegrees;

        mCenterX = centerX;

        mCenterY = centerY;

        mDepthZ = depthZ;

        mReverse = reverse;

    }

    @Override

    public void initialize(int width, int height, int parentWidth, int parentHeight) {

        super.initialize(width, height, parentWidth, parentHeight);

        mCamera = new Camera();

    }

    @Override

    protected void applyTransformation(float interpolatedTime, Transformation t) {

        final float fromDegrees = mFromDegrees;

        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);

        final float centerX = mCenterX;

        final float centerY = mCenterY;

        final Camera camera = mCamera;

        final Matrix matrix = t.getMatrix();

        camera.save();

        if (mReverse) {

            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);

        } else {

            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));

        }

        camera.rotateY(degrees);

        camera.getMatrix(matrix);

        camera.restore();

        matrix.preTranslate(-centerX, -centerY);

        matrix.postTranslate(centerX, centerY);

    }

}

applyTransformation 메쏘드만 살펴보자~

인자로 에니메이션 시간(interpolatedTime) 과 변형을 위한 Transformation 값을 받는다.

에니메이션 시간은 0부터 1 사이값으로 

진행상태에 따라 이 값이 변경되어 호출된다.

 

 

현재 카메라 위치를 save

camera.save();

 

z축 계산. 리버스 모드 일경우 뷰가 점점 가까워지고, 멀어지는 등의 효과를 위해

z축을 이동한다.

camera.translate

 

그리고, y축을 기반으로 회전.

 camera.rotateY(degrees);

이 회전된 데이터의 행렬값을 저장. 이 행렬값이 실제 변형에 이용된다.

 camera.getMatrix(matrix);

카메라를 원래 값으로 복귀

camera.restore();

 

회전을 처리하기 전에 해주어야 할 작업이 원점으로 이동하는 일이다.

3D에서는 모델변환, 월드변환과 같은 용어가 나오는데..위의 작업은 모델에 맞춰진 작업이기에

모델변화으로 처리되어야 한다.

즉, 4*4 의 3차원 행렬을 -centerX, -centerY 이동 행렬에 곱해 원점으로 이동하고,

변형 후 다시 centerX, centerY 이동행렬에 곱해 원래 위치로 이동하게 한다.

 

변형전.

matrix.preTranslate(-centerX, -centerY);

 

변형후.

matrix.postTranslate(centerX, centerY);

 

 

2. 액티비티 구현 (Transition3d.java)

리스트 아이템을 클릭하면, 해당 리스트에 연결된 이미지를 3d 에니메이션으로

보여주는 예제이다.

 

public class Transition3d extends Activity implements

        AdapterView.OnItemClickListener, View.on_clickListener {

    private ListView mPhotosList;

    private ViewGroup mContainer;

    private ImageView mImageView;

    // Names of the photos we show in the list

    private static final String[] PHOTOS_NAMES = new String[] {

            "Lyon",

            "Livermore",

            "Tahoe Pier",

            "Lake Tahoe",

            "Grand Canyon",

            "Bodie"

    };

    // Resource identifiers for the photos we want to display

    private static final int[] PHOTOS_RESOURCES = new int[] {

            R.drawable.photo1,

            R.drawable.photo2,

            R.drawable.photo3,

            R.drawable.photo4,

            R.drawable.photo5,

            R.drawable.photo6

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.animations_main_screen);

        mPhotosList = (ListView) findViewById(android.R.id.list);

        mImageView = (ImageView) findViewById(R.id.picture);

        mContainer = (ViewGroup) findViewById(R.id.container);

        // Prepare the ListView

        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

                android.R.layout.simple_list_item_1, PHOTOS_NAMES);

        mPhotosList.setAdapter(adapter);

        mPhotosList.setOnItemClickListener(this);

        // Prepare the ImageView

        mImageView.setClickable(true);

        mImageView.setFocusable(true);

        mImageView.seton_clickListener(this);

        // Since we are caching large views, we want to keep their cache

        // between each animation

        mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);

    }

    /**

     * Setup a new 3D rotation on the container view.

     *

     * @param position the item that was clicked to show a picture, or -1 to show the list

     * @param start the start angle at which the rotation must begin

     * @param end the end angle of the rotation

     */

    private void applyRotation(int position, float start, float end) {

        // Find the center of the container

        final float centerX = mContainer.getWidth() / 2.0f;

        final float centerY = mContainer.getHeight() / 2.0f;

        // Create a new 3D rotation with the supplied parameter

        // The animation listener is used to trigger the next animation

        final Rotate3dAnimation rotation =

                new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);

        rotation.setDuration(500);

        rotation.setFillAfter(true);

        rotation.setInterpolator(new AccelerateInterpolator());

        rotation.setAnimationListener(new DisplayNextView(position));

        mContainer.startAnimation(rotation);

    }

    public void onItemClick(AdapterView parent, View v, int position, long id) {

        // Pre-load the image then start the animation

        mImageView.setImageResource(PHOTOS_RESOURCES[position]);

        applyRotation(position, 0, 90);

    }

    public void on_click(View v) {

        applyRotation(-1, 180, 90);

    }

    /**

     * This class listens for the end of the first half of the animation.

     * It then posts a new action that effectively swaps the views when the container

     * is rotated 90 degrees and thus invisible.

     */

    private final class DisplayNextView implements Animation.AnimationListener {

        private final int mPosition;

        private DisplayNextView(int position) {

            mPosition = position;

        }

        public void onAnimationStart(Animation animation) {

        }

        public void onAnimationEnd(Animation animation) {

            mContainer.post(new SwapViews(mPosition));

        }

        public void onAnimationRepeat(Animation animation) {

        }

    }

    /**

     * This class is responsible for swapping the views and start the second

     * half of the animation.

     */

    private final class SwapViews implements Runnable {

        private final int mPosition;

        public SwapViews(int position) {

            mPosition = position;

        }

        public void run() {

            final float centerX = mContainer.getWidth() / 2.0f;

            final float centerY = mContainer.getHeight() / 2.0f;

            Rotate3dAnimation rotation;

            

            if (mPosition > -1) {

                mPhotosList.setVisibility(View.GONE);

                mImageView.setVisibility(View.VISIBLE);

                mImageView.requestFocus();

                rotation = new Rotate3dAnimation(90, 180, centerX, centerY, 310.0f, false);

            } else {

                mImageView.setVisibility(View.GONE);

                mPhotosList.setVisibility(View.VISIBLE);

                mPhotosList.requestFocus();

                rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);

            }

            rotation.setDuration(500);

            rotation.setFillAfter(true);

            rotation.setInterpolator(new DecelerateInterpolator());

            mContainer.startAnimation(rotation);

        }

    }

}

 

 

 

mContainer가 메인 레이아웃이고, 에니메이션을 위해 캐쉬 설정을 아래와 같이 지정한다.

 mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);

 

Animation 설정은 기존 Animation과 동일하며, 콜백 수신을 위해 AnimationListener 를 등록해준다.

final Rotate3dAnimation rotation =

                new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);

        rotation.setDuration(500);

        rotation.setFillAfter(true);

        rotation.setInterpolator(new AccelerateInterpolator());

        rotation.setAnimationListener(new DisplayNextView(position));

        mContainer.startAnimation(rotation);

 

에니메이션이 종료되면 AnimationListener 로 콜백이 넘어오는데

onAnimationEnd에서 변경할 뷰의 에니메이션을 시작시킨다.

즉, 90도는 현재 보이는 뷰의 회전, 나머지 90은 변경할 뷰의 반대 회전이 된다.

 

기존 뷰를 감추고, 새뷰를 보이면서 새로운 에니메이션을 시작~

'프로그래밍 > Android' 카테고리의 다른 글

이미지 클리핑 기초  (0) 2011.08.18
ffmpeg 빌드 #3  (0) 2011.07.07
ffmpeg 빌드 #2 ffmpeg 옵션별 빌드  (0) 2011.07.06
ffmpeg 라이브러리 빌드 #1  (0) 2011.06.30
Drag&drop listview  (0) 2010.10.06
안드로이드 위젯 배경 이미지, 나인패치  (0) 2010.08.17
옵션메뉴 배경 변경  (0) 2010.08.10
갤러리, 커버플로우 구현  (0) 2010.07.27
AppWidget 여러 개의 위젯을 가진 앱위젯  (0) 2010.07.09
OpenGL 사용하기  (0) 2010.06.27