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 stub
102             return false;
103         }
104
105     }
106
107     @Override
108     public boolean onTouch(View v, MotionEvent event) {
109         // TODO Auto-generated method stub
110         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     @Override
100     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         @Override
182         public void onAnimationStart(Animation animation) {
183             Log.d(TAG, "onAnimationStart ");
184             mAnimationRunning = true;
185         }
186         @Override
187         public void onAnimationEnd(Animation animation) {
188             // TODO Auto-generated method stub
189             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                     @Override
200                     public void run() {
201                         // TODO Auto-generated method stub
202                         AnimationRunningCount--;
203                         mCurrentItem = currentItemCopy;
204                         int lastItem = scroolToRight ? currentItemCopy + 1 : currentItemCopy - 1;
205                         startTraAnimation(currentItemCopy,lastItem);
206                         updateTextColor();
207                     }
208                 });
209             }
210         }
211         @Override
212         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 xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="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的效果,如果有大神有想法,可以指导下。

时间: 2024-11-03 22:48:35

Android 实现 IOS相机滑动控件的相关文章

Android嵌套滑动控件的冲突解决和ViewPager适配当前子控件高度不留空白的办法

最近项目有一个需求,需要多层可滑动控件的嵌套展示,demo效果如下,demo的下载地址在最后 咋一看好像挺简单啊,不就是一个ScrollView + ViewPager + ListView吗,我开始也这样觉得,也用的这种方式实现,结果始终和效果不对劲.这里总结几点问题: 两个或两个以上的滑动控件嵌套时,如果layout_height采用的是wrap_content会造成内部滑动控件的高度不能正确的计算,会导致内部滑动控件的高度始终为0,除非你用定值设置,比如300dp. 两个相同滑动方向的滑动

Android自定义LinearLayout实现左右侧滑菜单,完美兼容ListView、ScrollView、ViewPager等滑动控件

国际惯例,先来效果图 在阅读本文章之前,请确定熟悉[Scroller]相关的知识,如果不熟悉,请小伙伴儿先百度后再来吧. 假如你已经知道[Scroller]了,那么就接着往下看吧. 首先,我们把侧拉菜单的构造给解析出来.多次观看上面的效果图,我们可以得出以下的结论. 整体可以看做是一个ViewGroup,这个ViewGroup包含了最多三个子View(分别是左菜单的红色View.中间正文内容的白色View.右菜单的蓝色View): 三个子View(我称为UI界面,因为代码中的Java类就取名这个

Android中滑动控件的不显示

1.背景介绍 在使用ScrollView和ListView这样的控件的时候,默认在右手边上是有一个滑动的控件的.在我们用手指滑动的时候,显示出来这个控件会不那么舒服,影响用户体验度.这里就是来说明一下,怎么样不显示滑动控件. 2.ScrollView不显示 直接上代码,如下: <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" android:la

Android 结合滑动控件ListView滑动删除

一转眼就15年了,希望大家15年升职加薪走上人生巅峰 这篇博客是结合上一篇ListView滑动删除之Viewgroup打造滑动控件(修正版)博客所完成的,先上个效果图吧. 其实实现起来并不复杂 1,解决滑动冲突 因为我们的自定义滑动控件和ListView本身的滑动事件会产生各种冲突,所以我们可以自定义ListView并重写onInterceptTouchEvent方法. 我们先来了解一下android事件的分发,当用户触摸屏幕时会先去调用ViewGroup的dispatchTouchEvent方

iOS中的UIScorllView(滑动控件,时机控制)的基本使用

#import "RootViewController.h" #define kScreenWidth [UIScreen mainScreen].bounds.size.width #define kScreenHeight [UIScreen mainScreen].bounds.size.height @interface RootViewController () <UIScrollViewDelegate> @end @implementation RootVie

scrollView滑动控件

sd 是iOS中的滑动控件,可以来解决当药显示内容个区域超过屏幕大小时,可以通过滑动操作看全内容区域,他是滑动控件的基类.UITableView.UITextView的父类 属性 设置内容区域的大小 contentSize 关闭水平指示器 showsHorizontalScrollIndicator 关闭竖直指示器 showsVerticalScrollIndicator 设置是否可以滑动 scrollEnabled 关闭反弹效果 bounces 设置偏移量 contentOffset其实是修改

前端心得---仿IOS拾取器控件(转轮控件)

希望做一个类似IOS拾取器的控件,在IOS上该控件的效果是这样的:,我也把该效果称之为为轮子效果. 要实现这个效果,能够用到的技术点非常简单,无非是transform的translate3d和rotate,不过要想很好的实现,还要建立一个精确的数学模型,来解决如何[摆放]的问题.特别是这个效果不是静态的,需要满足鼠标滑动的时,这个轮子要转起来,这就需要仔细思索了.当然,在最开始重点还是要搞清楚自变量是什么.因变量是什么.它们之间的关系是什么以及该需求的一些性质.找到了好的性质,可以减轻工作量,并

基于ViewPagerIndicator的UnderlinePageIndicator,ViewPager选项卡底部滑块衬线滑动控件

<基于ViewPagerIndicator的UnderlinePageIndicator,ViewPager选项卡底部滑块衬线滑动控件> 基于github上的第三方开源的ViewPagerIndicator的UnderlinePageIndicator( 附:地址),自己写的一个在选项卡底部有衬线的滑动控件. 控件效果图如图所示. 有一个特别的效果是:头部的选项卡在ViewPager切换过程中,底部的滑块也随之动态渐渐滑动过渡. 代码: MainActivity.java package zh

【Android 初学】3、控件布局初步

什么是控件布局 所谓的控件布局方法,就是指控制控件在Activity当中的位置.大小.颜色以及其他控件样式属性的方法. 控件的布局,在android中,有两种方法: 1.使用布局文件完成控件布局(相对简单) 2.在Java代码中完成控件布局(动态布局.更灵活.也相对复杂) 布局方法分类 1.Linear Layout(最容易掌握) 线性布局:可以纵向布局.也可以横向布局. 2.Ralative Layout(最常用使用.熟悉WEB开发的人就很熟悉) 3.ListView 4.Grid View