栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

Android 自定义View实现抽屉效果

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android 自定义View实现抽屉效果

Android 自定义View实现抽屉效果

说明

  1. 这个自定义View,没有处理好多点触摸问题
  2. View跟着手指移动,没有采用传统的scrollBy方法,而是通过不停地重新布局子View的方式,来使得子View产生滚动效果menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight);
  3. 相应的,由于没有使用scrollBy方法,就没有产生getScrollX值,所以不能通过Scroller的startScroll方法来完成手指离开后的平滑滚动效果,而是使用了Animation动画的applyTransformation方法来完成插值,从而实现动画效果

主要算法是:动画当前值=起始值+(目标值-起始值)*interpolatedTime

其中interpolatedTime是一个0.0f~1.0f的数字,系统自己插值计算好了(默认是线性变化的),当然你可以自己写插值器

 
  class MyAnimation extends Animation {

    private int viewCurrentLfet;
    private int viewStartLfet;
    private int viewTargetLfet;
    private int viewWidth;
    private View view;
    private int cha;

    public MyAnimation(View view, int viewStartLfet, int viewTargetLfet, int viewWidth) {
      this.view = view;
      this.viewStartLfet = viewStartLfet;
      this.viewTargetLfet = viewTargetLfet;
      this.viewWidth = viewWidth;
      cha = viewTargetLfet - viewStartLfet;
      setDuration(Math.abs(cha));
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
      super.applyTransformation(interpolatedTime, t);

      viewCurrentLfet = (int) (viewStartLfet + cha * interpolatedTime);
      view.layout(viewCurrentLfet, 0, viewCurrentLfet + viewWidth, menuHeight);


    }
  }

完整代码

package com.sunshine.choutidemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.Transformation;


public class ChouTiView extends ViewGroup {

  private View mainView;
  private View menuView;
  private int menuWidth;
  private int downX;
  private int lastX;
  private int moveX;
  private int deltaX;
  private int menuLeft;
  private int mainLeft;
  private int menuHeight;
  private int mainWidth;
  private int mainHeight;
  private int menuLeftBorder;
  private int mainLeftBorder;
  private int menuRightBorder;
  private int mainRightBorder;
  private int mMaxVelocity;
  private VelocityTracker mVelocityTracker;
  private int mPointerId;
  private float velocityX;
  private float velocityY;

  public ChouTiView(Context context) {
    super(context);
    init();
  }


  public ChouTiView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  private void init() {
//   0.获得此次最大速率
    mMaxVelocity = ViewConfiguration.get(getContext()).getMaximumFlingVelocity();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mainView.measure(widthMeasureSpec, heightMeasureSpec);
    menuView.measure(widthMeasureSpec, heightMeasureSpec);
//    获得子View的正确宽度(只能获取具体的数字值),但是不能这样获取高度,因为这里match—parent为-1
    menuWidth = menuView.getLayoutParams().width;
    menuLeft = (int) (-menuWidth * 0.5);
    menuLeftBorder = (int) (-menuWidth * 0.5);
    menuRightBorder = 0;
    mainLeft = 0;
    mainLeftBorder = 0;
    mainRightBorder = menuWidth;

  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    menuHeight = b;
    mainWidth = r;
    mainHeight = b;
    mainView.layout(l, t, r, b);
    menuView.layout(menuLeft, t, menuLeft + menuWidth, b);

  }

  @Override
  protected void onFinishInflate() {
    super.onFinishInflate();
    mainView = getChildAt(1);
    menuView = getChildAt(0);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    final int action = event.getActionMasked();

    acquireVelocityTracker(event); //1.向VelocityTracker添加MotionEvent
    final VelocityTracker verTracker = mVelocityTracker;
    switch (action) {

      case MotionEvent.ACTION_DOWN:
 //2.求第一个触点的id, 此时可能有多个触点,但至少一个
 // 获取索引为0的手指id
 mPointerId = event.getPointerId(0);
 downX = (int) event.getX();
 lastX = downX;
 break;

      case MotionEvent.ACTION_MOVE:
// 获取当前手指id所对应的索引,虽然在ACTION_DOWN的时候,我们默认选取索引为0
 // 的手指,但当有第二个手指触摸,并且先前有效的手指up之后,我们会调整有效手指

 // 屏幕上可能有多个手指,我们需要保证使用的是同一个手指的移动轨迹,
 // 因此此处不能使用event.getActionIndex()来获得索引
 final int pointerIndex = event.findPointerIndex(mPointerId);


 moveX = (int) event.getX(pointerIndex);
 deltaX = moveX - lastX;
// 把触摸移动引起的增量,体现在menu和main的左侧left上
 menuLeft = (int) (menuLeft + deltaX * 0.43);//让菜单移动的慢一点
 mainLeft = mainLeft + deltaX;
// 让菜单根据手指增量移动,考虑两侧边界问题(通过不停地layout实现移动效果)
// 为何不适用scrollBy,因为scrollBy移动的是外层的大View,现在需求是分别移动这个大view内的两个小View
// scrollBy的话,会让菜单和主页面同时移动,不会产生错位效果,
// 你会想,那让小view自己scrollBy,这样也是不行的,
// 因为让小view,例如menu调用scrollBy的话,会让menu自己的边框在动,
// 看上去,是menu内部的文字在移动,但是menu并没有在外层的大View里移动
// 说的很拗口,但是真的不能用scrollBy
 if (menuLeft >= menuRightBorder) {
   menuLeft = menuRightBorder;
 } else if (menuLeft <= menuLeftBorder) {
   menuLeft = menuLeftBorder;
 }
 menuView.layout(menuLeft, 0, menuLeft + menuWidth, menuHeight);


// 让主页面根据手指增量移动,考虑两侧边界问题
 if (mainLeft >= mainRightBorder) {
   mainLeft = mainRightBorder;
 } else if (mainLeft <= mainLeftBorder) {
   mainLeft = mainLeftBorder;
 }
 mainView.layout(mainLeft, 0, mainLeft + mainWidth, mainHeight);

 lastX = moveX;
 break;


      case MotionEvent.ACTION_UP:
 //3.求伪瞬时速度
 verTracker.computeCurrentVelocity(1000, mMaxVelocity);
 velocityX = verTracker.getXVelocity(mPointerId);
 Log.e("qwe", velocityX + "/" + mMaxVelocity);
 if (velocityX > 1000) {
   smoothToMenu();
 } else if (velocityX < -2000) {
   smoothToMain();
 } else {
// 判断松手的位置,如果大于1/2.5的菜单宽度就打开菜单,否则打开主页面

   if (mainLeft > menuWidth / 2.5) {
     Log.e("qqq", "显示菜单");
     smoothToMenu();
   } else {
     Log.e("qqq", "显示主页面");
     smoothToMain();
   }
 }
// 4.ACTION_UP释放VelocityTracker,交给其他控件使用
 releaseVelocityTracker();
 break;
      case MotionEvent.ACTION_CANCEL:

// 4.ACTION_UP释放VelocityTracker,交给其他控件使用
 releaseVelocityTracker();

      case MotionEvent.ACTION_POINTER_UP:
 // 获取离开屏幕的手指的索引
 int pointerIndexLeave = event.getActionIndex();
 int pointerIdLeave = event.getPointerId(pointerIndexLeave);
 if (mPointerId == pointerIdLeave) {
   // 离开屏幕的正是目前的有效手指,此处需要重新调整,并且需要重置VelocityTracker
   int reIndex = pointerIndexLeave == 0 ? 1 : 0;
   mPointerId = event.getPointerId(reIndex);
   // 调整触摸位置,防止出现跳动
   downX = (int) event.getX(reIndex);
//   y = event.getY(reIndex);
   releaseVelocityTracker();
 }
 releaseVelocityTracker();

 break;
    }


    return true;
  }

  private void smoothToMain() {
    MyAnimation menuAnimation = new MyAnimation(menuView, menuLeft, menuLeftBorder, menuWidth);
    MyAnimation mainAnimation = new MyAnimation(mainView, mainLeft, mainLeftBorder, mainWidth);
    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(menuAnimation);
    animationSet.addAnimation(mainAnimation);
    startAnimation(animationSet);
    //一定记得更新menu和main的左侧状态,这影响到了,再次手指触摸时候的动画,否则突变
    menuLeft = menuLeftBorder;
    mainLeft = mainLeftBorder;
  }

  private void smoothToMenu() {
    MyAnimation menuAnimation = new MyAnimation(menuView, menuLeft, menuRightBorder, menuWidth);
    MyAnimation mainAnimation = new MyAnimation(mainView, mainLeft, mainRightBorder, mainWidth);
    AnimationSet animationSet = new AnimationSet(true);
    animationSet.addAnimation(menuAnimation);
    animationSet.addAnimation(mainAnimation);
    startAnimation(animationSet);
    //一定记得更新menu和main的左侧状态,这影响到了,再次手指触摸时候的动画,否则突变
    menuLeft = menuRightBorder;
    mainLeft = mainRightBorder;
  }


  
  private void acquireVelocityTracker(final MotionEvent event) {
    if (null == mVelocityTracker) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(event);
  }

  
  private void releaseVelocityTracker() {
    if (null != mVelocityTracker) {
      mVelocityTracker.clear();
      mVelocityTracker.recycle();
      mVelocityTracker = null;
    }
  }


  
  class MyAnimation extends Animation {

    private int viewCurrentLfet;
    private int viewStartLfet;
    private int viewTargetLfet;
    private int viewWidth;
    private View view;
    private int cha;

    public MyAnimation(View view, int viewStartLfet, int viewTargetLfet, int viewWidth) {
      this.view = view;
      this.viewStartLfet = viewStartLfet;
      this.viewTargetLfet = viewTargetLfet;
      this.viewWidth = viewWidth;
      cha = viewTargetLfet - viewStartLfet;
      setDuration(Math.abs(cha));
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
      super.applyTransformation(interpolatedTime, t);

      viewCurrentLfet = (int) (viewStartLfet + cha * interpolatedTime);
      view.layout(viewCurrentLfet, 0, viewCurrentLfet + viewWidth, menuHeight);


    }
  }

}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/158700.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号