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

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

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

  MainaActivity.java:

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

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

 package com.example.androidcustomnview;

 import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.MotionEvent;
import android.view.TextureView;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView; public class MainActivity extends Activity implements OnTouchListener{ private static final String TAG = "MainActivity.TAG";
CustomViewL mCustomViewL;
String[] name = new String[] {"延时摄影","慢动作","视频","拍照","正方形","全景"}; GestureDetector mGestureDetector;
RelativeLayout rootView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCustomViewL = (CustomViewL) findViewById(R.id.mCustomView);
rootView = (RelativeLayout) findViewById(R.id.ViewRoot);
rootView.setOnTouchListener(this);
mCustomViewL.getParent();
mCustomViewL.addIndicator(name);
mGestureDetector = new GestureDetector(this, new myGestureDetectorLis()); } class myGestureDetectorLis implements GestureDetector.OnGestureListener { private static final int degreeLimit = 30;
private static final int distanceLimit = 15; private boolean isScroll = false;
@Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
Log.d(TAG, "myGestureDetectorLis onDown");
isScroll = false;
return true;
}
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub }
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
} @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
// TODO Auto-generated method stub
if (isScroll) return false;
double degree = Math.atan(Math.abs(e2.getY() - e1.getY()) / Math.abs(e2.getX() - e1.getX())) * 180 /Math.PI;
float delta = e2.getX() - e1.getX();
if (delta > distanceLimit && degree < degreeLimit) {
Log.d(TAG, "向右滑");
isScroll = true;
mCustomViewL.scrollRight();
} else if (delta < -distanceLimit && degree < degreeLimit) {
Log.d(TAG, "向左滑");
isScroll = true;
mCustomViewL.scrollLeft();
}
return false;
} @Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub }
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// TODO Auto-generated method stub
return false;
} } @Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
return mGestureDetector.onTouchEvent(event);
} }

CustomViewL.java:

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

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

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

 package com.example.androidcustomnview;

 import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import android.widget.TextView; public class CustomViewL extends LinearLayout { private static final String TAG = "CustomViewL.TAG";
private Matrix mMatrix;
Camera mCamera;
private int mCurrentItem = 2;
private int screenWidth;
private Paint mPaint; public static final float ItemScale = 0.1f; public CustomViewL(Context context) {
super(context);
// TODO Auto-generated constructor stub
initView(context);
} public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
} public CustomViewL(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
} public CustomViewL(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
} private void initView(Context context) {
screenWidth = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getWidth();
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "onLayout ");
super.onLayout(changed, l , t, r, b);
View v = getChildAt(mCurrentItem);
int delta = getWidth() / 2 - v.getLeft() - v.getWidth()/2; for (int i = 0; i < getChildCount(); i++) {
View v1 = getChildAt(i);
if (i == mCurrentItem) {
v1.layout(v1.getLeft() + delta, v1.getTop(),
v1.getRight() + delta, v1.getBottom());
continue;
}
float mScale = Math.abs(i - mCurrentItem) * ItemScale;
int move = (int)(v1.getWidth() * mScale / 2);
if (i < mCurrentItem) {
for (int j = i + 1; j < mCurrentItem; j++) {
View v2 = getChildAt(j);
move += (int) (v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale);
}
} else {
for (int j = i - 1; j > mCurrentItem; j--) {
View v2 = getChildAt(j);
move += (int)(v2.getWidth() * Math.abs(j - mCurrentItem) * ItemScale);
}
move = -move;
}
v1.layout(v1.getLeft() + delta + move, v1.getTop(),
v1.getRight() + delta + move, v1.getBottom());
}
mRequstLayout = false;
} @Override
protected void dispatchDraw(Canvas canvas) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
updateChildItem(canvas,i);
}
} public void updateChildItem(Canvas canvas,int item) {
// Log.d(TAG, "updateChildItem");
View v = getChildAt(item);
float desi = 1- Math.abs(item - mCurrentItem) * ItemScale;
((TextView)v).setScaleX(desi);
drawChild(canvas, v, getDrawingTime());
updateTextColor();
}
private void updateTextColor() {
for (int i =0 ; i < getChildCount(); i++) {
if (i == mCurrentItem) {
((TextView)getChildAt(i)).setTextColor(Color.YELLOW);
} else {
((TextView)getChildAt(i)).setTextColor(Color.WHITE);
}
}
}
boolean scroolToRight = false; public void scrollRight() {
if (mRequstLayout) return;
if (mCurrentItem > 0) {
if (mAnimationRunning) {
if (AnimationRunningCount < 1) {
currentItemCopy = mCurrentItem - 1;
AnimationRunningCount++;
scroolToRight = true;
}
return;
}
mCurrentItem--;
startTraAnimation(mCurrentItem,mCurrentItem + 1);
updateTextColor();
}
} private int currentItemCopy;
public void scrollLeft() {
if (mRequstLayout) return;
if (mCurrentItem < getChildCount() - 1) {
if (mAnimationRunning) {
if (AnimationRunningCount < 1) {
currentItemCopy = mCurrentItem + 1;
AnimationRunningCount++;
scroolToRight = false;
}
return;
}
mCurrentItem++;
startTraAnimation(mCurrentItem,mCurrentItem-1);
updateTextColor();
}
} public void addIndicator(String[] name) {
for (int i=0; i< name.length; i++) {
TextView mTextView = new TextView(getContext());
mTextView.setText(name[i]);
mTextView.setTextColor(Color.WHITE);
mTextView.setLines(1);
LinearLayout.LayoutParams ll = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
ll.setMargins(20, 0, 20, 0);
addView(mTextView,ll);
}
} class myAnimationListener implements android.view.animation.Animation.AnimationListener { @Override
public void onAnimationStart(Animation animation) {
Log.d(TAG, "onAnimationStart ");
mAnimationRunning = true;
}
@Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
Log.d(TAG, "onAnimationEnd "); for (int i= 0; i < getChildCount(); i++) {
getChildAt(i).clearAnimation();
}
mRequstLayout = true;
requestLayout();
mAnimationRunning = false;
if (AnimationRunningCount > 0) {
CustomViewL.this.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
AnimationRunningCount--;
mCurrentItem = currentItemCopy;
int lastItem = scroolToRight ? currentItemCopy + 1 : currentItemCopy - 1;
startTraAnimation(currentItemCopy,lastItem);
updateTextColor();
}
});
}
}
@Override
public void onAnimationRepeat(Animation animation) {
} } private int AnimitionDurationTime = 300;
private int AnimationRunningCount = 0;
private boolean mAnimationRunning = false;
private boolean mRequstLayout = false;
public void startTraAnimation(int item,int last) {
Log.d(TAG, "startTraAnimation item = " + item);
View v = getChildAt(item);
final int width = v.getWidth();
final int childCount = getChildCount();
int traslate = getWidth()/2 - v.getLeft() - width/2; int currentItemWidthScale = (int) (width * ItemScale); for (int i = 0; i < childCount; i++) {
int delta = currentItemWidthScale / 2;
Log.d(TAG, " i = " + i + " delta before = " + delta);
if (i < item) {
delta = -delta;
for (int j = i; j < item; j++) {
int a;
if (i == j) {
a = (int)(getChildAt(j).getWidth() * ItemScale / 2);
} else {
a = (int)(getChildAt(j).getWidth() * ItemScale);
}
delta = item < last ? delta - a : delta + a;
}
} else if (i > item){
for (int j = item + 1; j <= i; j++) {
int a;
if (j == i) {
a = (int)(getChildAt(j).getWidth() * ItemScale / 2);
} else {
a = (int)(getChildAt(j).getWidth() * ItemScale);
}
delta = item < last ? delta - a : delta + a;
}
} else {
delta = 0;
}
Log.d(TAG, "delta = " + delta);
delta += traslate;
TranslateAnimation translateAni = new TranslateAnimation(0, delta, 0, 0);
translateAni.setDuration(AnimitionDurationTime);
translateAni.setFillAfter(true);
if (i == item) translateAni.setAnimationListener(new myAnimationListener());
mAnimationRunning = true;
getChildAt(i).startAnimation(translateAni);
}
}
}

最后说一下布局文件,两边本来是要做一个阴影效果的,为了简便,复习了下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的效果,如果有大神有想法,可以指导下。

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

  1. Android进阶篇-时间滑动控件

    仿Iphone时间选择滑动控件: WheelView.java: /** * @author Administrator * * 时间滑动滚轮 */ public class WheelView ex ...

  2. 从0到1搭建移动App功能自动化测试平台(2):操作iOS应用的控件

    转自:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-Appium-interrogate-iOS-UI ...

  3. 基于webview的选择滑动控件(PC和wap版)

    有了webview,大家开发ios或者安卓的app就方便很多啦. 第一可以增量更新: 第二webview可以同时兼容ios和安卓,减少开发量哦. --------------------------- ...

  4. iOS基础UI控件介绍-Swift版

    iOS基础UI控件总结 iOS基础控件包括以下几类: 1.继承自NSObject:(暂列为控件) UIColor //颜色 UIImage //图像 2.继承自UIView: 只能相应手势UIGest ...

  5. 【Android】Anroid5.0+新控件---酷炫标题栏的简单学习

    Android5.0+推出的新控件感觉特别酷,最近想模仿大神做个看图App出来,所以先把这些新控件用熟悉了. 新控件的介绍.使用等等网上相应的文章已经特别多了,题主也没那能力去写篇详解出来,本篇随笔记 ...

  6. Android开发中目前流行控件和知识点总结

    Android开发中目前流行控件和知识点总结   1.SlidingMenu 滑动菜单 应用案例:Facebook . Path 2.0 .人人.网易新闻 下载地址: https://github.c ...

  7. Android自动化测试中AccessibilityService获取控件信息(1)

    Android自动化测试中AccessibilityService获取控件信息(1) 分类: android自动化测试2014-03-24 15:31 3455人阅读 评论(16) 收藏 举报 and ...

  8. Android UI开发之开源控件项目整理

    一.Banner 1.https://github.com/youth5201314/banner Android广告图片轮播控件,支持无限循环和多种主题,可以灵活设置轮播样式.动画.轮播和切换时间. ...

  9. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

随机推荐

  1. Linux查看硬件信息,主板型号及内存硬件,驱动设备,查看设备,查看CPU。

    用硬件检测程序kuduz探测新硬件:service kudzu start ( or restart) 查看CPU信息:cat /proc/cpuinfo 查看板卡信息:cat /proc/pci 查 ...

  2. centos6.5 无线网卡配置

    来自:http://liqirui.blog.51cto.com/4662702/1344877         http://wiki.centos.org/zh/HowTos/Laptops/Wp ...

  3. android 中对apache httpclient及httpurlconnection的选择

    在官方blog中,android工程师谈到了如何去选择apache client和httpurlconnection的问题: 原文见http://android-developers.blogspot ...

  4. python 零散记录(七)(下) 新式类 旧式类 多继承 mro 类属性 对象属性

    python新式类 旧式类: python2.2之前的类称为旧式类,之后的为新式类.在各自版本中默认声明的类就是各自的新式类或旧式类,但在2.2中声明新式类要手动标明: 这是旧式类为了声明为新式类的方 ...

  5. FZOJ--2221-- RunningMan 福建第六届省赛

    题目链接:http://acm.hust.edu.cn/vjudge/contest/127149#problem/J 题目大意: 因为总共就分三个队,因为两个队都要选取最优的策略,不论B队咋放,要使 ...

  6. MongoDB:The Definitive Guide CHAPTER 1 Introduction

    MongoDB is a powerful, flexible, and scalable data store. It combines the ability to scale out with ...

  7. HDU 4604 Deque 二分最长上升子序列

    题目大意就是给一个deque 然后有n个数,依次进行操作,每种操作,你可以把这个数放在deque首部,也可以放在尾部,也可以扔掉不管,但是要保证deque中的数是非递减的.最要求deque中最长能是多 ...

  8. linux下安装php的swoole扩展模块(安装后php加载不出来?)

    应开发同事要求,需要安装php的扩展模块swoole.swoole是一种PHP高级Web开发框架,框架不是为了提升网站的性能,而是为了提升网站的开发效率,以最少的性能损耗,换取最大的开发效率. 假设服 ...

  9. jetty之安装,配置,部署,运行

    上篇文章中详解了关于什么是jetty,后续文章主要是介绍jetty的使用.本章介绍jetty环境的配置及部署war包. 1. 安装 1. 先下载一个jetty的压缩包,下载地址:http://www. ...

  10. ABAP提示信息对话框

     1.   call function 'POPUP_TO_CONFIRM_WITH_MESSAGE'         exporting           diagnosetext1 = '数据为 ...