Android 自定义View实现抽屉效果

说明
主要算法是:动画当前值=起始值+(目标值-起始值)*interpolatedTime
其中interpolatedTime是一个0.0f~1.0f的数字,系统自己插值计算好了(默认是线性变化的),当然你可以自己写插值器
/**
* 由于上面不能使用scrollBy,那么这里就不能使用Scroller这个类来完成平滑移动了,还好我们有动画
*/
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;
/**
* Created by a on 2016/8/15.
*/
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;
}
/**
* @param event 向VelocityTracker添加MotionEvent
* @see android.view.VelocityTracker#obtain()
* @see android.view.VelocityTracker#addMovement(MotionEvent)
*/
private void acquireVelocityTracker(final MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
/**
* 释放VelocityTracker
*
* @see android.view.VelocityTracker#clear()
* @see android.view.VelocityTracker#recycle()
*/
private void releaseVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* 由于上面不能使用scrollBy,那么这里就不能使用Scroller这个类来完成平滑移动了,还好我们有动画
*/
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);
}
}
}
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# Android
# 自定义View实现抽屉效果
# 自定义View抽屉效果
# Android开发之DrawerLayout实现抽屉效果
# Android编程实现抽屉效果的方法详解
# Android自定义控件仿QQ抽屉效果
# Android DrawerLayout实现抽屉效果实例代码
# Android 抽屉效果的导航菜单实现代码实例
# Android实现自定义滑动式抽屉菜单效果
# Android App中DrawerLayout抽屉效果的菜单编写实例
# Android SlidingDrawer 抽屉效果的实现
# Android的Activity跳转动画各种效果整理
# Android Tween动画之RotateAnimation实现图片不停旋转效果实例介绍
# Android实现图片轮播效果的两种方法
# Android编程实现抽屉效果的方法示例
# 来完成
# 的是
# 多个
# 能有
# 会让
# 就不能
# 自定义
# 插值
# 瞬时速度
# 自己的
# 是一个
# 多点
# 不停地
# 好了
# 你可以
# 第一个
# 你会
# 希望能
# 第二个
# 不能用
相关文章:
如何选择高效稳定的ISP建站解决方案?
网站制作专业公司有哪些,如何制作一个企业网站,建设网站的基本步骤有哪些?
大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?
微课制作网站有哪些,微课网怎么进?
建站VPS推荐:2025年高性能服务器配置指南
如何配置支付宝与微信支付功能?
香港服务器如何优化才能显著提升网站加载速度?
如何快速搭建高效WAP手机网站?
建站之星北京办公室:智能建站系统与小程序生成方案解析
番禺网站制作公司哪家值得合作,番禺图书馆新馆开放了吗?
北京企业网站设计制作公司,北京铁路集团官方网站?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
兔展官网 在线制作,怎样制作微信请帖?
如何在IIS中新建站点并配置端口与物理路径?
阿里云网站搭建费用解析:服务器价格与建站成本优化指南
如何选择PHP开源工具快速搭建网站?
ppt制作免费网站有哪些,ppt模板免费下载网站?
如何获取PHP WAP自助建站系统源码?
如何通过可视化优化提升建站效果?
建站之星多图banner生成与模板自定义指南
太原网站制作公司有哪些,网约车营运证查询官网?
如何选择建站程序?包含哪些必备功能与类型?
如何快速搭建个人网站并优化SEO?
建站之星安装提示数据库无法连接如何解决?
建站主机核心功能解析:服务器选择与网站搭建流程指南
实现虚拟支付需哪些建站技术支撑?
装修招标网站设计制作流程,装修招标流程?
如何在IIS7上新建站点并设置安全权限?
教学论文网站制作软件有哪些,写论文用什么软件
?
网站制作报价单模板图片,小松挖机官方网站报价?
制作门户网站的参考文献在哪,小说网站怎么建立?
如何通过cPanel快速搭建网站?
湖北网站制作公司有哪些,湖北清能集团官网?
如何用腾讯建站主机快速创建免费网站?
定制建站价位费用解析与套餐推荐全攻略
建站之星如何保障用户数据免受黑客入侵?
建站之星五站合一营销型网站搭建攻略,流量入口全覆盖优化指南
电商网站制作价格怎么算,网上拍卖流程以及规则?
如何选择高性价比服务器搭建个人网站?
如何选择高效便捷的WAP商城建站系统?
专业制作网站的公司哪家好,建立一个公司网站的费用.有哪些部分,分别要多少钱?
如何用免费手机建站系统零基础打造专业网站?
北京建设网站制作公司,北京古代建筑博物馆预约官网?
Swift开发中switch语句值绑定模式
建站之星手机一键生成:多端自适应+小程序开发快速建站指南
视频网站app制作软件,有什么好的视频聊天网站或者软件?
如何快速搭建虚拟主机网站?新手必看指南
建站主机空间推荐 高性价比配置与快速部署方案解析
如何快速上传自定义模板至建站之星?
javascript中的try catch异常捕获机制用法分析
*请认真填写需求信息,我们会在24小时内与您取得联系。