본문 바로가기

Android

Android에 3D 회전 효과를 내는 방법


 

마지막 항목에 Android SDK m5 - rc14 용 전자책 리더를 출시했습니다.
그래서 적용한 3D 회전 효과를 내는 방법을 소개합니다.

 

1. 하고 싶은 것 

현재보고있는 화면을 지도자っと회전하여 뒷면에 다른 이미지를 표시한다.


2. 준비

지도자 90도로 회전하여 테이블과 뒷면의 모양을 변경하기 때문에, 얼굴과 뒷면을 준비합니다.

레이아웃의 XML은 다음과 같습니다.

 

main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/container"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent">
  <ImageView android:id="@+id/front"
             android:src="@drawable/front"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent" />
  <ImageView android:id="@+id/back"
             android:src="@drawable/back"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"
             android:visibility="gone" />
</FrameLayout>

 

 FrameLayout을 회전시킵니다.
그렇게 하므로써 front와 back 표시를 전환합니다.
초기 상태는 Hidden (gone)입니다.

 


3. 리소스 검색
public void onCreate(Bundle icicle) {
  super.onCreate(icicle);
  setContentView(R.layout.main);
  this.mContainer = (ViewGroup) findViewById(R.id.container);
  this.frontView = (ImageView) findViewById(R.id.front);
  this.backView = (ImageView) findViewById(R.id.back);
}

이것은 Android의 기본입니다.


4. 0 ~ 90도 회전

먼저 앞면을 그대로 90도 회전시킵니다.

float start = 0f;
float mid = 90f;
 
this.centerX = mContainer.getWidth() / 2.0f;
this.centerY = mContainer.getHeight() / 2.0f;
 
Rotate3dAnimation rot;
rot = new Rotate3dAnimation(start, mid, centerX, centerY, depth, true);
rot.setDuration(500);
rot.setAnimationListener(new DisplayNextView(mid, end, depth));
mContainer.startAnimation(rot);

 


3-1. Rotate3dAnimation

 

Rotate3dAnimation은 Animation 클래스를 상속받는 고유 클래스입니다.
ApiDemo을 그대로 사용하지달라고합니다.
애니메이션, Animation 클래스의 applyTransformation 메소드를 Override 합니다.


void applyTransformation(float interpolatedTime, Transformation t) { ... }

 

interpolateTime은 0(으)로부터1까지의 값입니다..
조금 복잡합니다만,0(으)로부터1까지 연속한 값이 매개변수로 건네받아 applyTransformation메소드가 계속 불려짐니다. 시계열로 설명하면 다음과 같습니다.

.


applyTransformation(0f, t);
applyTransformation(0.2f, t);
applyTransformation(0.4f, t);
applyTransformation(0.6f, t);
applyTransformation(0.8f, t);
applyTransformation(1.0f, t);

 

위의 setDuration에서 지정한 500ms 사이에서 할수 있도록 Setting 되어 있는데

이 예제는 0.2 간격으로 부를 것입니다. 만약 이 간격을 변경하려면


rot.setInterpolator(new AccelerateInterpolator());

를 설정합니다. 거리 시간을 변경하면서 applyTransformation이 부를 수있습니다.

조금 복잡하지만 인수 interpolatedTime에 따라 대상을 이동하거나,보기 각도를 변화시키고하면 애니메이션을 구현할 수있습니다.
회전의 경우 카메라를 회전합니다.


Camera camera = new Camera();
camera.rotateY(degrees);

ststartAnimation는 이름 그대로 애니메이션을 시작합니다.

 

3-2. DisplayNextView

 

DisplayNextView은 Animation.AnimationListener를 implements하는 클래스입니다.
이것은 애니메이션의 이벤트를 Listening하는 Listener 입니다. 
이에 해당되는 이벤트는 다음과 같이 3 가지.

 

void onAnimationEnd()
void onAnimationRepeat()
void onAnimationStart()

 

이번에는 0 도에서 90 도까지 회전 애니메이션이 끝난 후 나머지 90 도에서 180 도까지 회전시킵니다.
따라서 onAnimationEnd 이벤트를 사용합니다.

 



5. 90 ~ 180도 회전

 onAniamtionEnd 에서는 잔여 회전을시킵니다。교체 작업은 기본적으로 지금까지와 동일합니다.
Rotate3dAnimation를 사용합니다. 그 전에, 표시면을 바꿉니다.

겉(표)로부터 이면으로 전환하는 예를 나타냅니다

 

this.frontView.setVisibility(View.GONE);
this.backView.setVisibility(View.VISIBLE);

 

설명은 다음과 같습니다.


private class DisplayNextView implements AnimationListener {
  public void onAnimationStart() { }
  public void onAnimationRepeat() { }
  public void onAnimationEnd() {
    mContainer.post(new Runnable() {
      frontView.setVisibility(View.GONE);
      backView.setVisibility(View.VISIBLE);
 
      Rotate3dAnimation rot;
      rot = new Rotate3dAnimation(mid, end, centerX, centerY, depth, false);
      rot.setDuration(500);
      rot.setInterpolator(new AccelerateInterpolator());
      mContainer.startAnimation(rot);
    });
  }
}

이상으로3차원의 회전을 구현할 수 있었습니다.

 
6. Download

eclipse Project 파일입니다. 참고 바랍니다.

 

rotation3d.zip

이것으로 3D애니메이션은 테크니컬한 측면을 살펴 보았습니다..
조금 복잡하지만, 머지않아 누군가가 간단한 라이브러리를 만들 것이라는 생가도 듭니다..
하지만 기초를 알고 있는 것도 손해도 아니라고 생각 듭니다.

 

package com.adamrocker.android.rotation3d;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation.AnimationListener;
import android.widget.ImageView;
 
public class Rotation3D extends Activity {
  private boolean isFront = true;
  private int DURATION = 500;
  private ViewGroup mContainer;
  private ImageView frontView, backView;
  private float centerX;
  private float centerY;
 
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);
    this.mContainer = (ViewGroup) findViewById(R.id.container);
    this.frontView = (ImageView) findViewById(R.id.front);
    this.backView = (ImageView) findViewById(R.id.back);
  }
 
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    super.onKeyDown(keyCode, event);
    switch (keyCode) {
    case KeyEvent.KEYCODE_SPACE:
      if (isFront) {
        applyRotation(0f, 90f, 180f, 0f);
      } else {
        applyRotation(180f, 270f, 360f, 0f);
      }
      break;
    }
    return false;
  }
 
  /* start(으)로부터end까지Y축회전한다 */
  private void applyRotation(float start, float mid, float end, float depth) {
    this.centerX = mContainer.getWidth() / 2.0f;
    this.centerY = mContainer.getHeight() / 2.0f;
 
    Rotate3dAnimation rot = new Rotate3dAnimation(start, mid, centerX, centerY,
        depth, true);
    rot.setDuration(DURATION);
    // rot.setInterpolator(new AccelerateInterpolator());
    rot.setAnimationListener(new DisplayNextView(mid, end, depth));
    mContainer.startAnimation(rot);
  }
 
  private class DisplayNextView implements AnimationListener {
    private float mid;
    private float end;
    private float depth;
 
    public DisplayNextView(float mid, float end, float depth) {
      this.mid = mid;
      this.end = end;
      this.depth = depth;
    }
 
    public void onAnimationEnd() {
      mContainer.post(new Runnable() {
        public void run() {
          if (isFront) {
            frontView.setVisibility(View.GONE);
            backView.setVisibility(View.VISIBLE);
            isFront = false;
          } else {
            frontView.setVisibility(View.VISIBLE);
            backView.setVisibility(View.GONE);
            isFront = true;
          }
 
          Rotate3dAnimation rot = new Rotate3dAnimation(mid, end, centerX,
              centerY, depth, false);
          rot.setDuration(DURATION);
          rot.setInterpolator(new AccelerateInterpolator());
          mContainer.startAnimation(rot);
        }
      });
    }
 
    public void onAnimationStart() {}
 
    public void onAnimationRepeat() {}
  }
}