首頁  >  文章  >  Java  >  Android 仿淘寶、京東商品詳情頁向上拖曳查看圖文詳情控制DEMO詳解

Android 仿淘寶、京東商品詳情頁向上拖曳查看圖文詳情控制DEMO詳解

高洛峰
高洛峰原創
2017-02-08 17:10:332066瀏覽

本文介紹android 仿淘寶、京東商品詳情頁向上拖曳查看圖文詳情控制DEMO詳解,使用兩個scrollView,兩個scrollView 垂直排列,透過自訂viewGroup來控制兩個scrollView的垂直排列,以及滑動事件的處理。對android 拖曳查看圖文詳情知識有興趣的朋友一起學習吧

一、淘寶商品詳情頁效果

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

我們的效果

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(); 
  } 
 } 
}

  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滑動事件的問題

       滾動事件監聽。

/** 
 * 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地址查看圖文詳情控制DEMO詳解,希望對大家有幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對PHP中文網的支持! 🎜🎜更多Android 仿淘寶、京東商品詳情頁向上拖曳查看圖文詳情控制項DEMO詳解相關文章請關注PHP中文網! 🎜
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn