Android仿淘宝商品拖动查看详情及标题栏渐变功能
绪论
最近一直比较忙,也没抽出时间来写博客,也不得不说是自己犯了懒癌,人要是一懒就什么事都不想做了,如果不能坚持下来的话,那么估计就废了,��。最近自己攒了好多东西,接下来的时间我会慢慢都分享出来的。好了废话不多说了,下面我们开始正题:
今天要分享的是淘宝的详情页,之前在淘宝上买东西的时候看到淘宝的详情页效果比较不错,所以今天就来仿一下它的效果吧,可能没有淘宝的好,希望见谅啊。
先上效果图:
这是淘宝的:
我自己做的:
怎么样效果还差不多吧?gif图效果看的不太清楚,见谅。
下面我们来看看怎么实现的吧
实现
首先我们分析淘宝布局的界面难点大致就下面3个部分:
*标题栏渐变,文字渐变隐藏
*透明通知栏(支持到4.4)
*继续拖动查看详情,scrollview有一个弹性的效果
前两点我之前都已经实现过了,这里就不做过多介绍了,不清楚的小伙伴可以看我之前的文章:
android带你解析scrollview–仿qq空间标题栏渐变
下面我们主要介绍一下scrollview继续拖动查看详情有一个弹性动画的效果:
仔细分析这个效果我们知道上面的布局可以滑动,当滑动到下面的布局时候同时下面的布局依然可以滑动,所以我们自定义一个view来包含两个scrollview,上面一个下面一个;然后监听scrollview滑动到底部添加动画效果让其滑动到下面的scrollview,当下面的scrollview滑动到顶部的时候再添加一个向上的动画让其滑动到上面的scrollview。
具体的可以看一下代码,注释已经很详细了:
import android.content.context; import android.os.handler; import android.os.message; import android.util.attributeset; import android.util.log; import android.view.motionevent; import android.view.velocitytracker; import android.view.view; import android.widget.relativelayout; import android.widget.scrollview; import java.util.timer; import java.util.timertask; /** * 包含两个scrollview的容器 * */ public class scrollviewcontainer extends relativelayout { /** * 自动上滑 */ public static final int auto_up = 0; /** * 自动下滑 */ public static final int auto_down = 1; /** * 动画完成 */ public static final int done = 2; /** * 动画速度 */ public static final float speed = 8.5f; private boolean ismeasured = false; /** * 用于计算手滑动的速度 */ private velocitytracker vt; private int mviewheight; private int mviewwidth; private view topview; private view bottomview; private boolean canpulldown; private boolean canpullup; private int state = done; /** * 记录当前展示的是哪个view,0是topview,1是bottomview */ private int mcurrentviewindex = 0; /** * 手滑动距离,这个是控制布局的主要变量 */ private float mmovelen; private mytimer mtimer; private float mlasty; /** * 用于控制是否变动布局的另一个条件,mevents==0时布局可以拖拽了,mevents==-1时可以舍弃将要到来的第一个move事件, * 这点是去除多点拖动剧变的关键 */ private int mevents; boolean istuninginterface=true; private handler handler = new handler() { @override public void handlemessage(message msg) { if (mmovelen != 0) { if (state == auto_up) { mmovelen -= speed; if (mmovelen <= -mviewheight) { mmovelen = -mviewheight; state = done; mcurrentviewindex = 1; if(istuninginterface){ istuninginterface=false; } } } else if (state == auto_down) { mmovelen += speed; if (mmovelen >= 0) { mmovelen = 0; state = done; mcurrentviewindex = 0; } } else { mtimer.cancel(); } } requestlayout(); } }; public scrollviewcontainer(context context) { super(context); init(); } public scrollviewcontainer(context context, attributeset attrs) { super(context, attrs); init(); } public scrollviewcontainer(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); init(); } private void init() { mtimer = new mytimer(handler); } @override public boolean dispatchtouchevent(motionevent ev) { switch (ev.getactionmasked()) { case motionevent.action_down: if (vt == null) vt = velocitytracker.obtain();//获得velocitytracker类实例 else vt.clear(); mlasty = ev.gety(); system.out.println("---action_down-mlasty------"+ev.gety()); vt.addmovement(ev); mevents = 0; break; case motionevent.action_pointer_down: case motionevent.action_pointer_up: // 多一只手指按下或抬起时舍弃将要到来的第一个事件move,防止多点拖拽的bug mevents = -1; break; case motionevent.action_move: vt.addmovement(ev);//将事件加入到velocitytracker类实例中 if (canpullup && mcurrentviewindex == 0 && mevents == 0) { mmovelen += (ev.gety() - mlasty); // 防止上下越界 if (mmovelen > 0) { mmovelen = 0; mcurrentviewindex = 0; } else if (mmovelen < -mviewheight) { mmovelen = -mviewheight; mcurrentviewindex = 1; if(istuninginterface){ istuninginterface=false; } } if (mmovelen < -8) { // 防止事件冲突 ev.setaction(motionevent.action_cancel); } } else if (canpulldown && mcurrentviewindex == 1 && mevents == 0) { mmovelen += (ev.gety() - mlasty); // 防止上下越界 if (mmovelen < -mviewheight) { mmovelen = -mviewheight; mcurrentviewindex = 1; } else if (mmovelen > 0) { mmovelen = 0; mcurrentviewindex = 0; } if (mmovelen > 8 - mviewheight) { // 防止事件冲突 ev.setaction(motionevent.action_cancel); } } else mevents++; mlasty = ev.gety(); // requestlayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onmeasure onlayout来对重新设置自己位置。 requestlayout(); break; case motionevent.action_up: mlasty = ev.gety(); vt.addmovement(ev); //参数:units 你想要指定的得到的速度单位,如果值为1,代表1毫秒运动了多少像素。如果值为1000,代表 1秒内运动了多少像素 vt.computecurrentvelocity(700); // 获取y方向的速度 可以通过getxvelocity()和getyvelocity()获得横向和竖向的速率 float myv = vt.getyvelocity(); if (mmovelen == 0 || mmovelen == -mviewheight) break; if (math.abs(myv) < 500) { // 速度小于一定值的时候当作静止释放,这时候两个view往哪移动取决于滑动的距离 if (mmovelen <= -mviewheight / 2) { state = auto_up; } else if (mmovelen > -mviewheight / 2) { state = auto_down; } } else { // 抬起手指时速度方向决定两个view往哪移动 if (myv < 0) state = auto_up; else state = auto_down; } mtimer.schedule(2); try { vt.recycle(); vt=null; } catch (exception e) { e.printstacktrace(); } break; } super.dispatchtouchevent(ev); return true; } @override protected void onlayout(boolean changed, int l, int t, int r, int b) { topview.layout(0, (int) mmovelen, mviewwidth, topview.getmeasuredheight() + (int) mmovelen); bottomview.layout(0, topview.getmeasuredheight() + (int) mmovelen, mviewwidth, topview.getmeasuredheight() + (int) mmovelen + bottomview.getmeasuredheight()); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); if (!ismeasured) { ismeasured = true; mviewheight = getmeasuredheight(); mviewwidth = getmeasuredwidth(); system.out.println("======onmeasure====mviewheight======"+mviewheight); system.out.println("======onmeasure====mviewwidth======"+mviewwidth); topview = getchildat(0); bottomview = getchildat(1); bottomview.setontouchlistener(bottomviewtouchlistener); topview.setontouchlistener(topviewtouchlistener); } } private ontouchlistener topviewtouchlistener = new ontouchlistener() { @override public boolean ontouch(view v, motionevent event) { scrollview sv = (scrollview) v; log.i("------getscrolly------", sv.getscrolly()+""); log.i("------getmeasuredheight---getchildat---", sv.getchildat(0).getmeasuredheight()+""); log.i("------getmeasuredheight---sv---", sv.getmeasuredheight()+""); if (sv.getscrolly() == (sv.getchildat(0).getmeasuredheight() - sv .getmeasuredheight()) && mcurrentviewindex == 0) canpullup = true; else canpullup = false; return false; } }; private ontouchlistener bottomviewtouchlistener = new ontouchlistener() { @override public boolean ontouch(view v, motionevent event) { scrollview sv = (scrollview) v; if (sv.getscrolly() == 0 && mcurrentviewindex == 1) canpulldown = true; else canpulldown = false; return false; } }; class mytimer { private handler handler; private timer timer; private mytask mtask; public mytimer(handler handler) { this.handler = handler; timer = new timer(); } public void schedule(long period) { if (mtask != null) { mtask.cancel(); mtask = null; } mtask = new mytask(handler); timer.schedule(mtask, 0, period); } public void cancel() { if (mtask != null) { mtask.cancel(); mtask = null; } } class mytask extends timertask { private handler handler; public mytask(handler handler) { this.handler = handler; } @override public void run() { handler.obtainmessage().sendtotarget(); } } } }
最后我们在xml布局里面最外层用这个scrollviewcontainer包裹着两个scrollview分别为上面的布局和下面的布局就可以了,当然因为我们上面的布局还涉及到标题栏渐变,所以上面的scrollview要自定义滑动监听,上面提到的文章里面有详细介绍。好了今天就到这里,有时间我会把代码整理出来放到我的github上的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。