안녕하세요 그냥가자입니다.
제목 쓰기가 좀 그러네요...
필요할거 같아서 만든 뷰인데요... 카드를 여러장 손에쥐고 돌려가면서 고르는식의 뷰입니다.
말로는 좀 설명이 어렵고 캡쳐모양을 보시면 아실겁니다.

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>

 spinCard.png