当前位置: 移动技术网 > 移动技术>移动开发>Android > Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解

Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解

2019年07月24日  | 移动技术网移动技术  | 我要评论

一、淘宝商品详情页效果

我们的效果

二、实现思路

     使用两个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(); 
  } 
 } 
} 

源码:

github地址

以上所述是小编给大家介绍的android 仿淘宝、京东商品详情页向上拖动查看图文详情控件demo详解,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网