안드로이드 개발 정보
(글 수 1,069)
안녕하세요 그냥가자입니다.
제목 쓰기가 좀 그러네요...
필요할거 같아서 만든 뷰인데요... 카드를 여러장 손에쥐고 돌려가면서 고르는식의 뷰입니다.
말로는 좀 설명이 어렵고 캡쳐모양을 보시면 아실겁니다.
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;




아놔 그림..... ㅠ.ㅠ 누가 그림표시하는 방법좀 쪽지로 주세요 ㅠ.ㅠ