뷰 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 |