안드로이드 개발 정보
(글 수 1,067)
안녕하세요 그냥가자입니다.
제목 쓰기가 좀 그러네요...
필요할거 같아서 만든 뷰인데요... 카드를 여러장 손에쥐고 돌려가면서 고르는식의 뷰입니다.
말로는 좀 설명이 어렵고 캡쳐모양을 보시면 아실겁니다.
SpinLayout.java
package a.b.c.CardRotate; import android.content.Context; import android.graphics.Camera; import android.graphics.Matrix; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.animation.Transformation; import android.widget.RelativeLayout; class SpinLayout extends RelativeLayout{ private double nowRadian; private double subRadian; private float startX; private float startY; private float beforeX; private float beforeY; int childAxisX = 0; int childAxisY = 0; private double sumRadian; private VelocityTracker vTracker = null; private Context mContext; private OnItemSelectedListener mOnItemSelectedListener; double mMaxDegree = 90.0; double mUnitDegree = 30.0; double mClickDistance = 10.0; public SpinLayout(Context c) { // TODO Auto-generated constructor stub this(c,null); } public SpinLayout(Context c, AttributeSet attr){ super(c, attr); setStaticTransformationsEnabled(true); mContext = c; } @Override public boolean onTouchEvent(MotionEvent event) { float nowX; float nowY; // TODO Auto-generated method stub int action = event.getAction(); if(this.vTracker == null){ this.vTracker = VelocityTracker.obtain(); }else{ this.vTracker.clear(); } switch (action){ case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_DOWN: nowX = event.getX(); nowY = event.getY(); if(action == MotionEvent.ACTION_DOWN){ startX = event.getX(); startY = event.getY(); initTouchEvent(event); } nowRadian = Math.toRadians(getRotatedDegree(beforeX, beforeY, nowX, nowY, childAxisX, childAxisY)); subRadian = nowRadian; sumRadian += subRadian; if(!isValidArea(Math.toDegrees(sumRadian), -mMaxDegree, 0.0)){ initTouchEvent(event); if(Math.toDegrees(sumRadian) > 0.0){ subRadian = subRadian - sumRadian; sumRadian = 0.0; }else{ subRadian = subRadian + (Math.toRadians(-mMaxDegree) - sumRadian); sumRadian = Math.toRadians(-mMaxDegree); } doBringToFront(Math.toDegrees(sumRadian), 0.5); this.invalidate(); break; } doBringToFront(Math.toDegrees(sumRadian), 0.5); this.invalidate(); beforeX = nowX; beforeY = nowY; break; case MotionEvent.ACTION_UP: double destDegree = 0.0; nowX = event.getX(); nowY = event.getY(); if(isClickEvent(nowX, nowY)){ CardView selctedCardView = null; for(int i=0;i<getChildCount();i++){ CardView cv = (CardView)getChildAt(i); double clickedDegree = getDegreeFromCartesian(nowX, nowY, childAxisX, childAxisY); clickedDegree = clickedDegree%360; if(clickedDegree > 180.0){ clickedDegree = clickedDegree - 360.0; }else if(clickedDegree < -180.0){ clickedDegree = 360.0+ clickedDegree; } Log.i("aaa", "degree="+clickedDegree); double radius = Math.sqrt((nowX-childAxisX)*(nowX-childAxisX)+(nowY-childAxisY)*(nowY-childAxisY)); if(cv.isClicked(clickedDegree, radius, childAxisX - cv.getLeft(), childAxisY-cv.getTop())){ selctedCardView = cv; } } if(selctedCardView != null){ if(selctedCardView == pickView(Math.toDegrees(sumRadian), 0.5)){ if(mOnItemSelectedListener!=null){ mOnItemSelectedListener.onItemSelected(selctedCardView); } } destDegree = -selctedCardView.getInitRotateDegree(); animateCardViews(Math.toDegrees(sumRadian), destDegree); } break; } double sumDegree = Math.toDegrees(sumRadian); double ceilDegree = Math.ceil(sumDegree / mUnitDegree)*mUnitDegree; double floorDegree = Math.floor(sumDegree / mUnitDegree)*mUnitDegree; if(sumDegree < 0){ if(Math.signum(sumDegree-(ceilDegree+floorDegree)/2) < 0.0){ destDegree = floorDegree; }else if(Math.signum(sumDegree-(ceilDegree+floorDegree)/2) > 0.0){ destDegree = ceilDegree; }else{ break; } } animateCardViews(sumDegree, destDegree); break; } return true; } private void doBringToFront(double degree, double rate){ View child = pickView(degree, rate); if(child == null){ return; } bringChildToFront(child); } private View pickView(double degree, double rate){ View pickChild = null; double unitDegree = 30.0; for(int i=0;i<getChildCount(); i++){ CardView cv = (CardView)getChildAt(i); if(cv.isSelected(degree, unitDegree, rate)){ pickChild = cv; } } return pickChild; } private boolean isClickEvent(float nowX, float nowY){ return Math.sqrt((nowX-startX)*(nowX-startX) + (nowY-startY)*(nowY-startY)) < mClickDistance; } private boolean isValidArea(double degree, double from, double to){ double newDegree = degree%360; if(newDegree > 180.0){ newDegree = newDegree - 360.0; }else if(newDegree < -180.0){ newDegree = newDegree + 360.0; } if(from <= newDegree && to >= newDegree){ return true; } return false; } private void initTouchEvent(MotionEvent event){ this.vTracker.recycle(); vTracker = null; this.vTracker = VelocityTracker.obtain(); beforeX = event.getX(); beforeY = event.getY(); } private double getRotatedDegree(float x1, float y1, float x2, float y2, float cx, float cy){ double startDegree = 0.0; double endDegree = 0.0; double rotateDegree = 0.0; startDegree = getDegreeFromCartesian(x1, y1, cx, cy); endDegree = getDegreeFromCartesian(x2, y2, cx, cy); rotateDegree = endDegree - startDegree; if(rotateDegree < -180.0){ rotateDegree = 360 + rotateDegree ; }else if(rotateDegree > 180.0){ rotateDegree = rotateDegree - 360; } return rotateDegree; } private double getDegreeFromCartesian(float nowX, float nowY, float centerX, float centerY){ double nowRadian = 0.0; double nowDegree = 0.0; if(nowX - centerX > 0){ nowRadian = Math.atan((nowY-centerY)/(nowX-centerX)) + Math.PI/2; }else if(nowX - centerX < 0){ nowRadian = Math.atan((nowY-centerY)/(nowX-centerX)) + Math.PI/2*3; } nowDegree = Math.toDegrees(nowRadian); return nowDegree; } private void animateCardViews(final double fromDegree, final double destDegree){ Thread animationThread = new Thread(new Runnable() { final long DURATION_TIME = 100; @Override public void run() { // TODO Auto-generated method stub long startTime = System.currentTimeMillis(); long endTime = startTime + DURATION_TIME; long nowTime = System.currentTimeMillis(); while((nowTime=System.currentTimeMillis()) <= endTime){ long interpolatedTime = nowTime - startTime; final double sumDegree = fromDegree + (destDegree - fromDegree)/DURATION_TIME * interpolatedTime; sumRadian = Math.toRadians(sumDegree); postRedraw(sumDegree); } if(sumRadian != destDegree){ sumRadian = Math.toRadians(destDegree); postRedraw(destDegree); } } }); animationThread.start(); } private void postRedraw(final double degree){ postInvalidate(); post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub doBringToFront(degree, 0.5); } }); } @Override protected boolean getChildStaticTransformation(View child, Transformation t) { // TODO Auto-generated method stub final int childWidth = child.getWidth(); final int childHeight = child.getHeight(); childAxisX = child.getLeft() + child.getWidth()/2; childAxisY = child.getTop()+ child.getHeight(); float alpha = 1.0f; CardView cv = null; if(child instanceof CardView){ cv = (CardView)child; cv.setDegree(Math.toDegrees(sumRadian)); if(!cv.isSelected(Math.toDegrees(sumRadian), mUnitDegree, 0.5)){ alpha = 1.0f; }else{ alpha = 1.0f; } } Camera mCamera = new Camera(); t.setTransformationType(Transformation.TYPE_MATRIX); mCamera.save(); final Matrix matrix = t.getMatrix(); t.setAlpha(alpha); mCamera.rotateZ((float)-cv.getRotatedDegree()); mCamera.getMatrix(matrix); matrix.preTranslate(-(childWidth/2), -(childHeight)); matrix.postTranslate((childWidth/2), (childHeight)); mCamera.restore(); return true; } @Override protected void onAttachedToWindow() { // TODO Auto-generated method stub super.onAttachedToWindow(); doBringToFront(Math.toDegrees(sumRadian), 0.5); } public double getNowRadian() { return nowRadian; } public void setNowRadian(double nowRadian) { this.nowRadian = nowRadian; } public interface OnItemSelectedListener{ public void onItemSelected(View v); } public void setMaxDegree(double degree){ mMaxDegree = degree; if(getChildCount()>1){ mUnitDegree = mMaxDegree / (getChildCount()-1); }else{ mUnitDegree = mMaxDegree; } for(int i=0; i < getChildCount() ; i++){ double initDegree = mUnitDegree*(getChildCount()-1-i); CardView cv = (CardView)getChildAt(i); cv.setInitRotatedDegree(initDegree); } sumRadian = 0.0; this.invalidate(); } public OnItemSelectedListener getmOnItemSelectedListener() { return mOnItemSelectedListener; } public void setmOnItemSelectedListener( OnItemSelectedListener mOnItemSelectedListener) { this.mOnItemSelectedListener = mOnItemSelectedListener; } }
CardView.java
package a.b.c.CardRotate; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.ImageView; public class CardView extends ImageView{ private double initRotatedDegree = 0.0; private double degree = 0.0; public CardView(Context context) { this(context, null); // TODO Auto-generated constructor stub } public CardView(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public CardView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } public double getInitRotateDegree(){ return initRotatedDegree; } public double getRotatedDegree() { return initRotatedDegree + degree; } public void setInitRotatedDegree(double rotatedDegree) { this.initRotatedDegree = rotatedDegree; } public void setDegree(double degree) { this.degree = degree; } public boolean isSelected(double degree, double unitDegree, double rate){ double fromDegree = -initRotatedDegree - (unitDegree*rate); double toDegree = -initRotatedDegree + (unitDegree*rate); return (fromDegree <= degree && toDegree > degree); } public boolean isClicked(double degree, double radius, int pivotX, int pivotY){ double nowDegree =degree - getRotatedDegree(); int nowX = (int)(radius*Math.cos(Math.toRadians(nowDegree-90.0))); int nowY = (int)(radius*Math.sin(Math.toRadians(nowDegree-90.0))); int left = -pivotX; int top = -pivotY; int right = this.getWidth() - pivotX; int bottom = this.getHeight() - pivotY; Rect r = new Rect(left, top, right, bottom); return r.contains(nowX, nowY); } }
MainActivity.java
package a.b.c.CardRotate; import kr.co.softcast.CardRotate.SpinLayout.OnItemSelectedListener; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.spin_service); SpinLayout spinLayout = (SpinLayout)findViewById(R.id.spinLinear); spinLayout.setMaxDegree(90.0 / 4 *3.5); spinLayout.setmOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(View v) { // TODO Auto-generated method stub int id = v.getId(); switch(id){ case R.id.imageView1: Toast.makeText(MainActivity.this, "1번 선택", 1000).show(); break; case R.id.imageView2: Toast.makeText(MainActivity.this, "2번 선택", 1000).show(); break; case R.id.imageView3: Toast.makeText(MainActivity.this, "3번 선택", 1000).show(); break; case R.id.imageView4: Toast.makeText(MainActivity.this, "4번 선택", 1000).show(); break; } } }); } }
spin_service.xml
<?xml version="1.0" encoding="utf-8"?> <a.b.c.CardRotate.SpinLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/spinLinear" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <a.b.c.CardRotate.CardView android:id="@+id/imageView4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/card_04" android:layout_centerInParent = "true" /> <a.b.c.CardRotate.CardView android:id="@+id/imageView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/card_03" android:layout_centerInParent = "true" /> <a.b.c.CardRotate.CardView android:id="@+id/imageView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/card_02" android:layout_centerInParent = "true" /> <a.b.c.CardRotate.CardView android:id="@+id/imageView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/card_01" android:layout_centerInParent = "true" /> </a.b.c.CardRotate.SpinLayout>
2011.01.11 13:35:54
말그대로 효과라는거죠... 이미지야 그냥 잴 많이 쓸만한 걸로 한거구요 ㅋ
카드 주고받는 보드게임에 효과로 쓸수도 있겠으나 카드가 많으면 겹침이 심해서 좀 안타깝군요....
4장정도 쓰면 딱인듯하네요
되는 기능은 다음과 같습니다.
1. 드래그로 인한 회전
2. 클릭으로 인한 Animation
3. 도킹(어중간하게 돌렸을때 카드쪽으로 붙는기능)
4. 가운데 있는 카드를 클릭했을 때 선택 확정하는 이벤트 콜백
2011.02.19 13:10:05
전 왜 이부분이 뻑날까요ㅠㅠ
제꺼에 맞춰서 바꿔줬는데도.ㅠㅠ
import
제꺼에 맞춰서 바꿔줬는데도.ㅠㅠ
import
kr.co.softcast.CardRotate.SpinLayout.OnItemSelectedListener;
아놔 그림..... ㅠ.ㅠ 누가 그림표시하는 방법좀 쪽지로 주세요 ㅠ.ㅠ