Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解
一、淘宝商品详情页效果
我们的效果
二、实现思路
使用两个scrollview,两个scrollview 竖直排列,通过自定义viewgroup来控制两个scrollview的竖直排列,以及滑动事件的处理。如下图
三、具体实现
1、继承viewgroup自定义布局view 重写onmeasure()和onlayout方法,在onlayout方法中完成对两个子scrollview的竖直排列布局,代码如下:
布局文件:
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.baoyunlong.view.pulluptoloadmore.mainactivity"> <com.baoyunlong.view.pulluptoloadmore.pulluptoloadmore android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.baoyunlong.view.pulluptoloadmore.myscrollview android:layout_width="match_parent" android:layout_height="match_parent" android:fillviewport="true"> <linearlayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <imageview android:scaletype="fitxy" android:src="@drawable/a1" android:layout_width="match_parent" android:layout_height="180dp" /> <textview android:text="这里是标题" android:textsize="18dp" android:layout_marginright="10dp" android:layout_marginleft="10dp" android:layout_margintop="10dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> <textview android:layout_margintop="10dp" android:text="子标题" android:layout_marginleft="10dp" android:layout_marginright="10dp" android:textsize="18dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> .............. <linearlayout android:layout_height="0dp" android:layout_weight="1" android:gravity="bottom" android:layout_width="match_parent"> <textview android:layout_width="match_parent" android:layout_height="wrap_content" android:height="50dp" android:background="#b11" android:gravity="center" android:text="继续拖动查看图文详情" android:textcolor="#000" /> </linearlayout> </linearlayout> </com.baoyunlong.view.pulluptoloadmore.myscrollview> <com.baoyunlong.view.pulluptoloadmore.myscrollview android:layout_width="match_parent" android:layout_height="match_parent" android:fillviewport="true"> <linearlayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/a1" /> <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/a3" /> ......... </linearlayout> </com.baoyunlong.view.pulluptoloadmore.myscrollview> </com.baoyunlong.view.pulluptoloadmore.pulluptoloadmore> </relativelayout>
代码:
public class pulluptoloadmore extends viewgroup { public pulluptoloadmore(context context) { super(context); } public pulluptoloadmore(context context, attributeset attrs) { super(context, attrs); } public pulluptoloadmore(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); measurechildren(widthmeasurespec, heightmeasurespec); } @override protected void onlayout(boolean changed, int l, int t, int r, int b) { int childcount = getchildcount(); int childtop = t; for (int i = 0; i < childcount; i++) { view child = getchildat(i); child.layout(l, childtop, r, childtop + child.getmeasuredheight()); childtop += child.getmeasuredheight(); } } }
2、处理滑动事件
规则如下 :
(1)、当处于第一屏时 第一个scrollview已经滑动到底部并且滑动方向是往上滑动,这个时候滑动事件应该交给父view处理也就是拦截事件让onintercepttouchevent返回true.然后父view通过scrollby()方法滚动,显示出第二个scrollview。
(2)、当处于第二屏时 第二个scrollview已经滑动到顶部并且滑动方向是往下滑动,这个时候滑动事件交给父view处理,根据滑动事件显示出第一个scrollview。
(3)、当手指离开屏幕时,根据滑动速度来决定是回弹到第一个scrollview还是第二个scrollview,通过velocitytracker来获取滑动速度。
3、一些细节的处理
(1)、如果仔细看观察淘宝的实现效果你会发现,当你滑动到刚刚看到 “继续拖动,查看图文详情”的时候,手指抬起,然后再按下重新向上拖动你会发现,第二页并不会划出来,而是停留在了“继续拖动,查看图文详情”的底部,京东的效果也是一样。这样用户体验不太好,我们来优化一下。其实通过查看scrollview的源码可以看出来,这是因为scrollview类的ontouchevent方法的默认实现,调用了parent.requestdisallowintercepttouchevent(true)方法 阻止了我们拦截事件,导致我们父view的onintercepttouchevent方法无法执行,也就拦截不到事件,拦截不到事件我们的ontouchevent就无法执行,ontouchevent无法执行,我们写在ontouchevent里面的滚动逻辑就执行不到了,导致了上面我们看到的划不动的效果。解决方法就是,我们需要重写dispatchtouchevent()方法,防止子view干扰我们,这样我们滑动的时候就可以一气呵成了。代码如下:
@override public boolean dispatchtouchevent(motionevent ev) { //防止子view禁止父view拦截事件 this.requestdisallowintercepttouchevent(false); return super.dispatchtouchevent(ev); }
(2)、监听scrollview滑动事件的问题
scrollview没有提供滚动事件的监听方法,也就没法判断是否滚动到了顶部,或者底部,这里我们继承scrollview 自己实现滚动事件监听。
/** * created by baoyunlong on 16/6/8. */ public class myscrollview extends scrollview { private static string tag=myscrollview.class.getname(); public void setscrolllistener(scrolllistener scrolllistener) { this.mscrolllistener = scrolllistener; } private scrolllistener mscrolllistener; public myscrollview(context context) { super(context); } public myscrollview(context context, attributeset attrs) { super(context, attrs); } public myscrollview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); } @override public boolean ontouchevent(motionevent ev) { switch (ev.getaction()){ case motionevent.action_move: if(mscrolllistener!=null){ int contentheight=getchildat(0).getheight(); int scrollheight=getheight(); int scrolly=getscrolly(); mscrolllistener.onscroll(scrolly); if(scrolly+scrollheight>=contentheight||contentheight<=scrollheight){ mscrolllistener.onscrolltobottom(); }else { mscrolllistener.notbottom(); } if(scrolly==0){ mscrolllistener.onscrolltotop(); } } break; } boolean result=super.ontouchevent(ev); requestdisallowintercepttouchevent(false); return result; } public interface scrolllistener{ void onscrolltobottom(); void onscrolltotop(); void onscroll(int scrolly); void notbottom(); }
4、完整代码如下
/** * created by baoyunlong on 16/6/8. */ public class pulluptoloadmore extends viewgroup { public static string tag = pulluptoloadmore.class.getname(); myscrollview topscrollview, bottomscrollview; velocitytracker velocitytracker = velocitytracker.obtain(); scroller scroller = new scroller(getcontext()); int currposition = 0; int position1y; int lasty; public int scaledtouchslop;//最小滑动距离 int speed = 200; boolean isintercept; public boolean bottomscrollviewisintop = false; public boolean topscrollviewisbottom = false; public pulluptoloadmore(context context) { super(context); init(); } public pulluptoloadmore(context context, attributeset attrs) { super(context, attrs); init(); } public pulluptoloadmore(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); init(); } private void init() { post(new runnable() { @override public void run() { topscrollview = (myscrollview) getchildat(0); bottomscrollview = (myscrollview) getchildat(1); topscrollview.setscrolllistener(new myscrollview.scrolllistener() { @override public void onscrolltobottom() { topscrollviewisbottom = true; } @override public void onscrolltotop() { } @override public void onscroll(int scrolly) { } @override public void notbottom() { topscrollviewisbottom = false; } }); bottomscrollview.setscrolllistener(new myscrollview.scrolllistener() { @override public void onscrolltobottom() { } @override public void onscrolltotop() { } @override public void onscroll(int scrolly) { if (scrolly == 0) { bottomscrollviewisintop = true; } else { bottomscrollviewisintop = false; } } @override public void notbottom() { } }); position1y = topscrollview.getbottom(); scaledtouchslop = viewconfiguration.get(getcontext()).getscaledtouchslop(); } }); } @override public boolean dispatchtouchevent(motionevent ev) { //防止子view禁止父view拦截事件 this.requestdisallowintercepttouchevent(false); return super.dispatchtouchevent(ev); } @override public boolean onintercepttouchevent(motionevent ev) { int y = (int) ev.gety(); switch (ev.getaction()) { case motionevent.action_down: lasty = y; break; case motionevent.action_move: //判断是否已经滚动到了底部 if (topscrollviewisbottom) { int dy = lasty - y; //判断是否是向上滑动和是否在第一屏 if (dy > 0 && currposition == 0) { if (dy >= scaledtouchslop) { isintercept = true;//拦截事件 lasty=y; } } } if (bottomscrollviewisintop) { int dy = lasty - y; //判断是否是向下滑动和是否在第二屏 if (dy < 0 && currposition == 1) { if (math.abs(dy) >= scaledtouchslop) { isintercept = true; } } } break; } return isintercept; } @override public boolean ontouchevent(motionevent event) { int y = (int) event.gety(); velocitytracker.addmovement(event); switch (event.getaction()) { case motionevent.action_move: int dy = lasty - y; if (getscrolly() + dy < 0) { dy = getscrolly() + dy + math.abs(getscrolly() + dy); } if (getscrolly() + dy + getheight() > bottomscrollview.getbottom()) { dy = dy - (getscrolly() + dy - (bottomscrollview.getbottom() - getheight())); } scrollby(0, dy); break; case motionevent.action_up: isintercept = false; velocitytracker.computecurrentvelocity(1000); float yvelocity = velocitytracker.getyvelocity(); if (currposition == 0) { if (yvelocity < 0 && yvelocity < -speed) { smoothscroll(position1y); currposition = 1; } else { smoothscroll(0); } } else { if (yvelocity > 0 && yvelocity > speed) { smoothscroll(0); currposition = 0; } else { smoothscroll(position1y); } } break; } lasty = y; return true; } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); measurechildren(widthmeasurespec, heightmeasurespec); } @override protected void onlayout(boolean changed, int l, int t, int r, int b) { int childcount = getchildcount(); int childtop = t; for (int i = 0; i < childcount; i++) { view child = getchildat(i); child.layout(l, childtop, r, childtop + child.getmeasuredheight()); childtop += child.getmeasuredheight(); } } //通过scroller实现弹性滑动 private void smoothscroll(int tarty) { int dy = tarty - getscrolly(); scroller.startscroll(getscrollx(), getscrolly(), 0, dy); invalidate(); } @override public void computescroll() { if (scroller.computescrolloffset()) { scrollto(scroller.getcurrx(), scroller.getcurry()); postinvalidate(); } } }
源码:
以上所述是小编给大家介绍的android 仿淘宝、京东商品详情页向上拖动查看图文详情控件demo详解,希望对大家有所帮助
上一篇: java 线程同步详细介绍及实例代码