欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现IOS相机滑动控件

程序员文章站 2024-03-06 15:03:50
ios相比于android,动画效果是一方面优势,ios相机切换时滑动的动画很不错,看着是有一个3d的效果,而且变化感觉很自然。android也可以通过graphics下面...

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

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

Android实现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的效果,如果有大神有想法,可以指导下。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。