Android实现IOS相机滑动控件

 更新时间:2020年08月20日 10:15:33   作者:梦想,就在不远方  
这篇文章主要为大家详细介绍了Android实现IOS相机滑动控件的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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()); 48 }
 
 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使用Handler造成内存泄露问题

    解决Android使用Handler造成内存泄露问题

    内存泄露的危害就是会使虚拟机占用内存过高,导致OOM(内存溢出),程序出错。接下来通过本文给大家分享Android使用Handler造成内存泄露问题及解决方法,一起看看吧
    2017-08-08
  • Android编程实现自动检测版本及自动升级的方法

    Android编程实现自动检测版本及自动升级的方法

    这篇文章主要介绍了Android编程实现自动检测版本及自动升级的方法,涉及Android版本检测,匹配,下载及自动安装等技巧,需要的朋友可以参考下
    2016-01-01
  • Android底部菜单栏(RadioGroup+Fragment)美化

    Android底部菜单栏(RadioGroup+Fragment)美化

    这篇文章主要介绍了Android底部菜单栏RadioGroup+Fragment美化,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • Android EditText被软键盘遮盖的处理方法

    Android EditText被软键盘遮盖的处理方法

    android app新增了透明栏效果,结果发现键盘弹起后会遮盖屏幕底部的EditText,没有像想象中的调整窗口大小,并滚动ScrollView,将EditText显示在键盘上方。下面小编把解决方法记录一下,特此分享到脚本之家平台,感兴趣的朋友一起看看吧
    2016-10-10
  • Android之带group指示器的ExpandableListView(自写)

    Android之带group指示器的ExpandableListView(自写)

    Android缺省的ExpandableListView的group header无法固定在界面上,在网上搜索了好多都不尽人意,于是乎在别人的基础上改进了一点点,原理都差不多
    2013-06-06
  • Android仿百度壁纸客户端之搭建主框架(一)

    Android仿百度壁纸客户端之搭建主框架(一)

    这篇文章主要介绍了Android仿百度壁纸客户端,搭建主框架,自定义Tab+ViewPager+Fragment,具有一定的实用性,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • Android实现拍照或者选取本地图片

    Android实现拍照或者选取本地图片

    这篇文章主要为大家详细介绍了Android实现拍照或者选取本地图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Android手机屏幕敲击解锁功能代码

    Android手机屏幕敲击解锁功能代码

    Android手机支持敲击屏幕解锁,敲击屏幕解锁是一项很实用的功能,本文以android平台为例使用java代码实现Android手机屏幕敲击解锁功能,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-07-07
  • Android中超大图片无法显示的问题解决

    Android中超大图片无法显示的问题解决

    最近在工作中发现一个问题,在做图片浏览的时候发现超大图图片无法显示,无奈只能上网找解决方法,后来通过测试找到了解决的方法,下面这篇文章就主要介绍了Android中超大图无法显示的问题解决方法,需要的朋友可以参考借鉴。
    2017-01-01
  • Android仿今日头条顶部导航栏效果的实例代码

    Android仿今日头条顶部导航栏效果的实例代码

    这篇文章主要介绍了Android之仿今日头条顶部导航栏效果的实例代码,具有很好的参考价值,希望对大家有所帮助,一起跟随小编过来看看吧
    2018-05-05

最新评论