时间:2021-05-21
Android 自定义View实现抽屉效果
说明
主要算法是:动画当前值=起始值+(目标值-起始值)*interpolatedTime
其中interpolatedTime是一个0.0f~1.0f的数字,系统自己插值计算好了(默认是线性变化的),当然你可以自己写插值器
完整代码
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); } }}感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文实例为大家分享了Android自定义View实现抖音飘动红心效果的具体代码,供大家参考,具体内容如下自定义View——抖音飘动红心效果展示动画效果使用自定义
前言Android开发中,常常自定义View实现自己想要的效果,当然自定义View也是Android开发中比较难的部分,涉及到的知识有Canvas(画布),Pa
自定义View是android开发的一个重要技能,用android提供的2/3D绘制相关类可以实现非常多炫酷的效果,需要实打实的编程基础。但是自定义View又是
前言:Android开发中,自定义View实现自己想要的效果已成为一项必备的技能,当然自定义View也是Android开发中比较难的部分,涉及到的知识有Canv
Android自定义View的构造函数自定义View是Android中一个常见的需求,每个自定义的View都需要实现三个基本的构造函数,而这三个构造函数又有两种