Android 自定义View实现抽屉效果
程序员文章站
2023-11-24 12:09:34
android 自定义view实现抽屉效果
说明
这个自定义view,没有处理好多点触摸问题
view跟着手指移动,没有采用传统的scrollby方法,而...
android 自定义view实现抽屉效果
说明
- 这个自定义view,没有处理好多点触摸问题
- view跟着手指移动,没有采用传统的scrollby方法,而是通过不停地重新布局子view的方式,来使得子view产生滚动效果menuview.layout(menuleft, 0, menuleft + menuwidth, menuheight);
- 相应的,由于没有使用scrollby方法,就没有产生getscrollx值,所以不能通过scroller的startscroll方法来完成手指离开后的平滑滚动效果,而是使用了animation动画的applytransformation方法来完成插值,从而实现动画效果
主要算法是:动画当前值=起始值+(目标值-起始值)*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); } } }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
下一篇: JavaSE入门语法