你的位置:首页 > 操作系统

[操作系统]Android 实现 IOS相机滑动控件


   IOS相比于Android,动画效果是一方面优势,IOS相机切换时滑动的动画很不错,看着是有一个3D的效果,而且变化感觉很自然。Android也可以通过Graphics下面的Camera可以实现3D效果,开始尝试着用这个做了一下,效果不理想,滑动之后各组文字之间的距离就变了,从立体空间来说这是合逻辑的,但是看着很别捏。IOS相机的滑动效果文字之间的间隔在滑动的时候是不变的。

  后面通过调整TextView X方向的scale使文字看着紧凑一点,然后通过计算的距离的方式,在滑动的时候保持各组文字之间的间隔一致,最后实现的效果还是和IOS的有一定的差距。先上个效果图的。

 

 下面逐步来说下怎么实现:

  MainaActivity.java:

  往自定义的控件加了6个TextView,对应各个模式。

  这里面还实现了一个手势监听,来识别滑动事件。对动画做了一些限制,角度小于30度,滑动距离大于15才能生效。

 1 package com.example.androidcustomnview; 2  3 import android.app.Activity; 4 import android.graphics.Color; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.GestureDetector; 8 import android.view.GestureDetector.OnGestureListener; 9 import android.view.View; 10 import android.view.View.OnTouchListener; 11 import android.view.MotionEvent; 12 import android.view.TextureView; 13 import android.view.ViewGroup; 14 import android.view.animation.Animation; 15 import android.view.animation.Animation.AnimationListener; 16 import android.view.animation.AnimationSet; 17 import android.view.animation.TranslateAnimation; 18 import android.widget.FrameLayout; 19 import android.widget.LinearLayout; 20 import android.widget.RelativeLayout; 21 import android.widget.TextView; 22  23 public class MainActivity extends Activity implements OnTouchListener{ 24  25   private static final String TAG = "MainActivity.TAG"; 26   CustomViewL mCustomViewL; 27   String[] name = new String[] {"延时摄影","慢动作","视频","拍照","正方形","全景"}; 28  29   GestureDetector mGestureDetector; 30   RelativeLayout rootView; 31   @Override 32   protected void onCreate(Bundle savedInstanceState) { 33     super.onCreate(savedInstanceState); 34     setContentView(R.layout.activity_main); 35     mCustomViewL = (CustomViewL) findViewById(R.id.mCustomView); 36     rootView = (RelativeLayout) findViewById(R.id.ViewRoot); 37     rootView.setOnTouchListener(this); 38     mCustomViewL.getParent(); 39     mCustomViewL.addIndicator(name); 40     mGestureDetector = new GestureDetector(this, new myGestureDetectorLis()); 48   } 49    50   class myGestureDetectorLis implements GestureDetector.OnGestureListener { 51      52     private static final int degreeLimit = 30; 53     private static final int distanceLimit = 15; 54      55     private boolean isScroll = false; 56     @Override 57     public boolean onDown(MotionEvent e) { 58       // TODO Auto-generated method stub 59       Log.d(TAG, "myGestureDetectorLis onDown"); 60       isScroll = false; 61       return true; 62     } 63     @Override 64     public void onShowPress(MotionEvent e) { 65       // TODO Auto-generated method stub 66        67     } 68     @Override 69     public boolean onSingleTapUp(MotionEvent e) { 70       // TODO Auto-generated method stub 71       return false; 72     } 73      74     @Override 75     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, 76         float distanceY) { 77       // TODO Auto-generated method stub 78       if (isScroll) return false; 79       double degree = Math.atan(Math.abs(e2.getY() - e1.getY()) / Math.abs(e2.getX() - e1.getX())) * 180 /Math.PI; 80       float delta = e2.getX() - e1.getX(); 81       if (delta > distanceLimit && degree < degreeLimit) { 82         Log.d(TAG, "向右滑"); 83         isScroll = true; 84         mCustomViewL.scrollRight(); 85       } else if (delta < -distanceLimit && degree < degreeLimit) { 86         Log.d(TAG, "向左滑"); 87         isScroll = true; 88         mCustomViewL.scrollLeft(); 89       } 90       return false; 91     } 92      93     @Override 94     public void onLongPress(MotionEvent e) { 95       // TODO Auto-generated method stub 96        97     } 98     @Override 99     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,100         float velocityY) {101       // TODO Auto-generated method stub102       return false;103     }104     105   }106 107   @Override108   public boolean onTouch(View v, MotionEvent event) {109     // TODO Auto-generated method stub110     return mGestureDetector.onTouchEvent(event);111   }112 113 114 }

 

CustomViewL.java:

  自定义的控件,继承自LinearLayout。在onLayout里面,重新计算了下各个子控件的位置,因为各组文字的scale是不一样的,必须重新Layout一下各个子控件的位置,是文字的显示区域和点击区域是一样的,这样给各个子控件设置的onClick事件才有效。

  dispatchDraw方法是重绘各个子控件,更具各个子控件到中心控件的位置的距离,设置了各个TextView X方向的scale,为了就是看着要有一个立体的效果。

  滑动之后,开始一个动画,动画结束之后重新requestLayout一下,重新计算下各个控件的位置。这个可以连续滑动的,如果这次动画在执行,会保存一下,等动画完了之后会接着跑下一个动画。各个子控件滑动距离的计算有兴趣的可以自己研究下,这里就不赘述了,其实也是数学知识。

 1 package com.example.androidcustomnview; 2  3 import android.content.Context; 4 import android.graphics.Camera; 5 import android.graphics.Canvas; 6 import android.graphics.Color; 7 import android.graphics.LinearGradient; 8 import android.graphics.Matrix; 9 import android.graphics.Paint; 10 import android.graphics.Shader; 11 import android.os.Handler; 12 import android.os.Message; 13 import android.util.AttributeSet; 14 import android.util.Log; 15 import android.view.MotionEvent; 16 import android.view.View; 17 import android.view.WindowManager; 18 import android.view.animation.Animation; 19 import android.view.animation.Animation.AnimationListener; 20 import android.view.animation.TranslateAnimation; 21 import android.widget.LinearLayout; 22 import android.widget.TextView; 23  24 public class CustomViewL extends LinearLayout { 25  26   private static final String TAG = "CustomViewL.TAG"; 27   private Matrix mMatrix; 28   Camera mCamera; 29   private int mCurrentItem = 2;   30   private int screenWidth; 31   private Paint mPaint; 32  33   public static final float ItemScale = 0.1f; 34  35   public CustomViewL(Context context) { 36     super(context); 37     // TODO Auto-generated constructor stub 38     initView(context); 39   } 40    41   public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr, 42       int defStyleRes) { 43     super(context, attrs, defStyleAttr, defStyleRes); 44     initView(context); 45   } 46  47   public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr) { 48     super(context, attrs, defStyleAttr); 49     initView(context); 50   } 51  52   public CustomViewL(Context context, AttributeSet attrs) { 53     super(context, attrs); 54     initView(context); 55   } 56    57   private void initView(Context context) { 60     screenWidth = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)) 61         .getDefaultDisplay().getWidth(); 63   } 64    65   @Override 66   protected void onLayout(boolean changed, int l, int t, int r, int b) { 67     Log.d(TAG, "onLayout "); 68     super.onLayout(changed, l , t, r, b); 69     View v = getChildAt(mCurrentItem); 70     int delta = getWidth() / 2 - v.getLeft() - v.getWidth()/2; 71      72     for (int i = 0; i < getChildCount(); i++) { 73       View v1 = getChildAt(i); 74       if (i == mCurrentItem) { 75         v1.layout(v1.getLeft() + delta, v1.getTop(),  76           v1.getRight() + delta, v1.getBottom()); 77         continue; 78       } 79       float mScale = Math.abs(i - mCurrentItem) * ItemScale; 80       int move = (int)(v1.getWidth() * mScale / 2); 81       if (i < mCurrentItem) { 82         for (int j = i + 1; j < mCurrentItem; j++) { 83           View v2 = getChildAt(j); 84           move += (int) (v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale); 85         } 86       } else { 87         for (int j = i - 1; j > mCurrentItem; j--) { 88           View v2 = getChildAt(j); 89           move += (int)(v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale); 90         } 91         move = -move; 92       } 93       v1.layout(v1.getLeft() + delta + move, v1.getTop(),  94           v1.getRight() + delta + move, v1.getBottom()); 95     } 96     mRequstLayout = false; 97   } 98  99   @Override100   protected void dispatchDraw(Canvas canvas) {101     int count = getChildCount();102     for (int i = 0; i < count; i++) {103       updateChildItem(canvas,i);104     }105   }106   107   public void updateChildItem(Canvas canvas,int item) {108 //    Log.d(TAG, "updateChildItem");110     View v = getChildAt(item);    111     float desi = 1- Math.abs(item - mCurrentItem) * ItemScale;112     ((TextView)v).setScaleX(desi);113     drawChild(canvas, v, getDrawingTime());114     updateTextColor();    115   }  118   private void updateTextColor() {119     for (int i =0 ; i < getChildCount(); i++) {120       if (i == mCurrentItem) {121         ((TextView)getChildAt(i)).setTextColor(Color.YELLOW);122       } else {123         ((TextView)getChildAt(i)).setTextColor(Color.WHITE);124       }125     }126   }   128   boolean scroolToRight = false;129   130   public void scrollRight() {131     if (mRequstLayout) return;132     if (mCurrentItem > 0) {133       if (mAnimationRunning) {134         if (AnimationRunningCount < 1) {135           currentItemCopy = mCurrentItem - 1;136           AnimationRunningCount++;137           scroolToRight = true;138         }139         return;140       }141       mCurrentItem--;142       startTraAnimation(mCurrentItem,mCurrentItem + 1);143       updateTextColor();144     }145   }146   147   private int currentItemCopy;148   public void scrollLeft() {149     if (mRequstLayout) return;150     if (mCurrentItem < getChildCount() - 1) {151       if (mAnimationRunning) {152         if (AnimationRunningCount < 1) {153           currentItemCopy = mCurrentItem + 1;154           AnimationRunningCount++;155           scroolToRight = false;156         }157         return;158       }159       mCurrentItem++;160       startTraAnimation(mCurrentItem,mCurrentItem-1);161       updateTextColor();162     }  163   }164   165   public void addIndicator(String[] name) {166     for (int i=0; i< name.length; i++) {167       TextView mTextView = new TextView(getContext());168       mTextView.setText(name[i]);169       mTextView.setTextColor(Color.WHITE);170       mTextView.setLines(1);171       LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(172           LinearLayout.LayoutParams.WRAP_CONTENT, 173           LinearLayout.LayoutParams.WRAP_CONTENT);174       ll.setMargins(20, 0, 20, 0);175       addView(mTextView,ll);176     }177   }178   179   class myAnimationListener implements android.view.animation.Animation.AnimationListener {180 181     @Override182     public void onAnimationStart(Animation animation) {183       Log.d(TAG, "onAnimationStart ");184       mAnimationRunning = true;185     }186     @Override187     public void onAnimationEnd(Animation animation) {188       // TODO Auto-generated method stub189       Log.d(TAG, "onAnimationEnd ");190 191       for (int i= 0; i < getChildCount(); i++) {192         getChildAt(i).clearAnimation();193       }194       mRequstLayout = true;195       requestLayout();196       mAnimationRunning = false;197       if (AnimationRunningCount > 0) {198         CustomViewL.this.post(new Runnable() {199           @Override200           public void run() {201             // TODO Auto-generated method stub202             AnimationRunningCount--;203             mCurrentItem = currentItemCopy;204             int lastItem = scroolToRight ? currentItemCopy + 1 : currentItemCopy - 1;205             startTraAnimation(currentItemCopy,lastItem);206             updateTextColor();207           }208         });209       }210     }211     @Override212     public void onAnimationRepeat(Animation animation) {213     }214     215   }216   217   private int AnimitionDurationTime = 300;218   private int AnimationRunningCount = 0;219   private boolean mAnimationRunning = false;220   private boolean mRequstLayout = false;221   public void startTraAnimation(int item,int last) {222     Log.d(TAG, "startTraAnimation item = " + item);223     View v = getChildAt(item);224     final int width = v.getWidth();225     final int childCount = getChildCount();226     int traslate = getWidth()/2 - v.getLeft() - width/2;227     228     int currentItemWidthScale = (int) (width * ItemScale);229 230     for (int i = 0; i < childCount; i++) {231       int delta = currentItemWidthScale / 2;      233       Log.d(TAG, " i = " + i + " delta before = " + delta);  234       if (i < item) {235         delta = -delta;236         for (int j = i; j < item; j++) {237           int a;238           if (i == j) {239             a = (int)(getChildAt(j).getWidth() * ItemScale / 2);240           } else {241             a = (int)(getChildAt(j).getWidth() * ItemScale);242           }243           delta = item < last ? delta - a : delta + a;244         }245       } else if (i > item){246         for (int j = item + 1; j <= i; j++) {247           int a;248           if (j == i) {249             a = (int)(getChildAt(j).getWidth() * ItemScale / 2);250           } else {251             a = (int)(getChildAt(j).getWidth() * ItemScale);252           }253           delta = item < last ? delta - a : delta + a;254         }255       } else {256         delta = 0;257       }258       Log.d(TAG, "delta = " + delta);259       delta += traslate;260       TranslateAnimation translateAni = new TranslateAnimation(0, delta, 0, 0);261       translateAni.setDuration(AnimitionDurationTime);262       translateAni.setFillAfter(true);263       if (i == item) translateAni.setAnimationListener(new myAnimationListener());264       mAnimationRunning = true;265       getChildAt(i).startAnimation(translateAni);266     }268   }269 }

 最后说一下布局文件,两边本来是要做一个阴影效果的,为了简便,复习了下PS,就在上面盖了张图片,显得两边有阴影。 

<RelativeLayout ="http://schemas.android.com/apk/res/android"  ="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  tools:context="com.example.androidcustomnview.MainActivity" >  <RelativeLayout    android:id="@+id/ViewRoot"    android:gravity="center"     android:layout_width="match_parent"    android:layout_height="match_parent">    <com.example.androidcustomnview.CustomViewL      android:orientation="horizontal"      android:background="@android:color/background_dark"      android:id="@+id/mCustomView"      android:layout_width="match_parent"      android:layout_height="wrap_content"      >          </com.example.androidcustomnview.CustomViewL>    <ImageView       android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:layout_alignLeft="@id/mCustomView"      android:layout_alignTop="@id/mCustomView"      android:layout_alignRight="@id/mCustomView"      android:layout_alignBottom="@id/mCustomView"      android:background="@drawable/test"/>      </RelativeLayout></RelativeLayout>

 

  整个来说其实也不复杂,有好些数学计算,几何问题,效果也没达到iphone的效果,如果有大神有想法,可以指导下。