解决水平ListView在ScrollView中出现的滑动冲突

解决的问题有两个: 

1)实现水平滑动的ListView。重写AdapterView,上代码: 

Java代码  

 package com.liucanwen.horizontallistview.view;  
  
 import java.util.LinkedList;  
 import java.util.Queue;  
  
 import android.content.Context;  
 import android.database.DataSetObserver;  
 import android.graphics.Rect;  
 import android.util.AttributeSet;  
 import android.view.GestureDetector;  
 import android.view.GestureDetector.OnGestureListener;  
 import android.view.View.MeasureSpec;  
 import android.view.MotionEvent;  
 import android.view.View;  
 import android.widget.AdapterView;  
 import android.widget.ListAdapter;  
 import android.widget.Scroller;  
  
 /** 
  * 重写ListView,以达到水平滑动 
  */ 
 public class HorizontalListView extends AdapterView<ListAdapter>  
 {  
  
  public boolean mAlwaysOverrideTouch = true;  
  protected ListAdapter mAdapter;  
  private int mLeftViewIndex = -1;  
  private int mRightViewIndex = 0;  
  protected int mCurrentX;  
  protected int mNextX;  
  private int mMaxX = Integer.MAX_VALUE;  
  private int mDisplayOffset = 0;  
  protected Scroller mScroller;  
  private GestureDetector mGesture;  
  private Queue<View> mRemovedViewQueue = new LinkedList<View>();  
  private OnItemSelectedListener mOnItemSelected;  
  private OnItemClickListener mOnItemClicked;  
  private OnItemLongClickListener mOnItemLongClicked;  
  private boolean mDataChanged = false;  
  
  public HorizontalListView(Context context, AttributeSet attrs)  
     {  
  super(context, attrs);  
         initView();  
     }  
  
  private synchronized void initView()  
     {  
         mLeftViewIndex = -1;  
         mRightViewIndex = 0;  
         mDisplayOffset = 0;  
         mCurrentX = 0;  
         mNextX = 0;  
         mMaxX = Integer.MAX_VALUE;  
         mScroller = new Scroller(getContext());  
         mGesture = new GestureDetector(getContext(), mOnGesture);  
     }  
  
  @Override 
  public void setOnItemSelectedListener(  
             AdapterView.OnItemSelectedListener listener)  
     {  
         mOnItemSelected = listener;  
     }  
  
  @Override 
  public void setOnItemClickListener(AdapterView.OnItemClickListener listener)  
     {  
         mOnItemClicked = listener;  
     }  
  
  @Override 
  public void setOnItemLongClickListener(  
             AdapterView.OnItemLongClickListener listener)  
     {  
         mOnItemLongClicked = listener;  
     }  
  
  private DataSetObserver mDataObserver = new DataSetObserver()  
     {  
  
  @Override 
  public void onChanged()  
         {  
  synchronized (HorizontalListView.this)  
             {  
                 mDataChanged = true;  
             }  
             invalidate();  
             requestLayout();  
         }  
  
  @Override 
  public void onInvalidated()  
         {  
             reset();  
             invalidate();  
             requestLayout();  
         }  
  
     };  
  
  @Override 
  public ListAdapter getAdapter()  
     {  
  return mAdapter;  
     }  
  
  @Override 
  public View getSelectedView()  
     {  
  // TODO: implement 
  return null;  
     }  
  
  @Override 
  public void setAdapter(ListAdapter adapter)  
     {  
  if (mAdapter != null)  
         {  
             mAdapter.unregisterDataSetObserver(mDataObserver);  
         }  
         mAdapter = adapter;  
         mAdapter.registerDataSetObserver(mDataObserver);  
         reset();  
     }  
  
  private synchronized void reset()  
     {  
         initView();  
         removeAllViewsInLayout();  
         requestLayout();  
     }  
  
  @Override 
  public void setSelection(int position)  
     {  
  // TODO: implement 
     }  
  
  private void addAndMeasureChild(final View child, int viewPos)  
     {  
         LayoutParams params = child.getLayoutParams();  
  if (params == null)  
         {  
             params = new LayoutParams(LayoutParams.FILL_PARENT,  
                     LayoutParams.FILL_PARENT);  
         }  
  
         addViewInLayout(child, viewPos, params, true);  
         child.measure(  
                 MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),  
                 MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));  
     }  
  
  @Override 
  protected synchronized void onLayout(boolean changed, int left, int top,  
  int right, int bottom)  
     {  
  super.onLayout(changed, left, top, right, bottom);  
  
  if (mAdapter == null)  
         {  
  return;  
         }  
  
  if (mDataChanged)  
         {  
  int oldCurrentX = mCurrentX;  
             initView();  
             removeAllViewsInLayout();  
             mNextX = oldCurrentX;  
             mDataChanged = false;  
         }  
  
  if (mScroller.computeScrollOffset())  
         {  
  int scrollx = mScroller.getCurrX();  
             mNextX = scrollx;  
         }  
  
  if (mNextX <= 0)  
         {  
             mNextX = 0;  
             mScroller.forceFinished(true);  
         }  
  if (mNextX >= mMaxX)  
         {  
             mNextX = mMaxX;  
             mScroller.forceFinished(true);  
         }  
  
  int dx = mCurrentX - mNextX;  
  
         removeNonVisibleItems(dx);  
         fillList(dx);  
         positionItems(dx);  
  
         mCurrentX = mNextX;  
  
  if (!mScroller.isFinished())  
         {  
             post(new Runnable()  
             {  
  @Override 
  public void run()  
                 {  
                     requestLayout();  
                 }  
             });  
  
         }  
     }  
  
  private void fillList(final int dx)  
     {  
  int edge = 0;  
         View child = getChildAt(getChildCount() - 1);  
  if (child != null)  
         {  
             edge = child.getRight();  
         }  
         fillListRight(edge, dx);  
  
         edge = 0;  
         child = getChildAt(0);  
  if (child != null)  
         {  
             edge = child.getLeft();  
         }  
         fillListLeft(edge, dx);  
  
     }  
  
  private void fillListRight(int rightEdge, final int dx)  
     {  
  while (rightEdge + dx < getWidth()  
                 && mRightViewIndex < mAdapter.getCount())  
         {  
  
             View child = mAdapter.getView(mRightViewIndex,  
                     mRemovedViewQueue.poll(), this);  
             addAndMeasureChild(child, -1);  
             rightEdge += child.getMeasuredWidth();  
  
  if (mRightViewIndex == mAdapter.getCount() - 1)  
             {  
                 mMaxX = mCurrentX + rightEdge - getWidth();  
             }  
  
  if (mMaxX < 0)  
             {  
                 mMaxX = 0;  
             }  
             mRightViewIndex++;  
         }  
  
     }  
  
  private void fillListLeft(int leftEdge, final int dx)  
     {  
  while (leftEdge + dx > 0 && mLeftViewIndex >= 0)  
         {  
             View child = mAdapter.getView(mLeftViewIndex,  
                     mRemovedViewQueue.poll(), this);  
             addAndMeasureChild(child, 0);  
             leftEdge -= child.getMeasuredWidth();  
             mLeftViewIndex--;  
             mDisplayOffset -= child.getMeasuredWidth();  
         }  
     }  
  
  private void removeNonVisibleItems(final int dx)  
     {  
         View child = getChildAt(0);  
  while (child != null && child.getRight() + dx <= 0)  
         {  
             mDisplayOffset += child.getMeasuredWidth();  
             mRemovedViewQueue.offer(child);  
             removeViewInLayout(child);  
             mLeftViewIndex++;  
             child = getChildAt(0);  
  
         }  
  
         child = getChildAt(getChildCount() - 1);  
  while (child != null && child.getLeft() + dx >= getWidth())  
         {  
             mRemovedViewQueue.offer(child);  
             removeViewInLayout(child);  
             mRightViewIndex--;  
             child = getChildAt(getChildCount() - 1);  
         }  
     }  
  
  private void positionItems(final int dx)  
     {  
  if (getChildCount() > 0)  
         {  
             mDisplayOffset += dx;  
  int left = mDisplayOffset;  
  for (int i = 0; i < getChildCount(); i++)  
             {  
                 View child = getChildAt(i);  
  int childWidth = child.getMeasuredWidth();  
                 child.layout(left, 0, left + childWidth,  
                         child.getMeasuredHeight());  
                 left += childWidth + child.getPaddingRight();  
             }  
         }  
     }  
  
  public synchronized void scrollTo(int x)  
     {  
         mScroller.startScroll(mNextX, 0, x - mNextX, 0);  
         requestLayout();  
     }  
  
  @Override 
  public boolean dispatchTouchEvent(MotionEvent ev)  
     {  
  boolean handled = super.dispatchTouchEvent(ev);  
         handled |= mGesture.onTouchEvent(ev);  
  return handled;  
     }  
  
  protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
  float velocityY)  
     {  
  synchronized (HorizontalListView.this)  
         {  
             mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0);  
         }  
         requestLayout();  
  
  return true;  
     }  
  
  protected boolean onDown(MotionEvent e)  
     {  
         mScroller.forceFinished(true);  
  return true;  
     }  
  
  private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener()  
     {  
  
  @Override 
  public boolean onDown(MotionEvent e)  
         {  
  return HorizontalListView.this.onDown(e);  
         }  
  
  @Override 
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
  float velocityY)  
         {  
  return HorizontalListView.this 
                     .onFling(e1, e2, velocityX, velocityY);  
         }  
  
  @Override 
  public boolean onScroll(MotionEvent e1, MotionEvent e2,  
  float distanceX, float distanceY)  
         {  
  
  synchronized (HorizontalListView.this)  
             {  
                 mNextX += (int) distanceX;  
             }  
             requestLayout();  
  
  return true;  
         }  
  
  @Override 
  public boolean onSingleTapConfirmed(MotionEvent e)  
         {  
  for (int i = 0; i < getChildCount(); i++)  
             {  
                 View child = getChildAt(i);  
  if (isEventWithinView(e, child))  
                 {  
  if (mOnItemClicked != null)  
                     {  
                         mOnItemClicked.onItemClick(HorizontalListView.this,  
                                 child, mLeftViewIndex + 1 + i,  
                                 mAdapter.getItemId(mLeftViewIndex + 1 + i));  
                     }  
  if (mOnItemSelected != null)  
                     {  
                         mOnItemSelected.onItemSelected(HorizontalListView.this,  
                                 child, mLeftViewIndex + 1 + i,  
                                 mAdapter.getItemId(mLeftViewIndex + 1 + i));  
                     }  
  break;  
                 }  
  
             }  
  return true;  
         }  
  
  @Override 
  public void onLongPress(MotionEvent e)  
         {  
  int childCount = getChildCount();  
  for (int i = 0; i < childCount; i++)  
             {  
                 View child = getChildAt(i);  
  if (isEventWithinView(e, child))  
                 {  
  if (mOnItemLongClicked != null)  
                     {  
                         mOnItemLongClicked.onItemLongClick(  
                                 HorizontalListView.this, child, mLeftViewIndex  
                                         + 1 + i,  
                                 mAdapter.getItemId(mLeftViewIndex + 1 + i));  
                     }  
  break;  
                 }  
  
             }  
         }  
  
  private boolean isEventWithinView(MotionEvent e, View child)  
         {  
             Rect viewRect = new Rect();  
  int[] childPosition = new int[2];  
             child.getLocationOnScreen(childPosition);  
  int left = childPosition[0];  
  int right = left + child.getWidth();  
  int top = childPosition[1];  
  int bottom = top + child.getHeight();  
             viewRect.set(left, top, right, bottom);  
  return viewRect.contains((int) e.getRawX(), (int) e.getRawY());  
         }  
     };  
  
 }  
2)第一步实现了水平滑动,往往我们会把这个水平ListView放到ScrollView里面(见截图实现),而这两个控件恰好滑动会有冲突,滑动水平ListView时会有卡顿,因此重写ScrollView,以达到流畅滑动: 
Java代码  
?
 package com.liucanwen.horizontallistview.view;  
  
 import android.content.Context;  
 import android.util.AttributeSet;  
 import android.view.GestureDetector;  
 import android.view.GestureDetector.SimpleOnGestureListener;  
 import android.view.MotionEvent;  
 import android.view.View;  
 import android.widget.ScrollView;  
  
 /** 
  * 重写ScrollView,以解决ScrollView与水平listView滑动时冲突 
  */ 
 public class MyScrollView extends ScrollView  
 {  
  private GestureDetector mGestureDetector;  
     View.OnTouchListener mGestureListener;  
  
  public MyScrollView(Context context, AttributeSet attrs)  
     {  
  super(context, attrs);  
         mGestureDetector = new GestureDetector(new YScrollDetector());  
         setFadingEdgeLength(0);  
     }  
  
  @Override 
  public boolean onInterceptTouchEvent(MotionEvent ev)  
     {  
  return super.onInterceptTouchEvent(ev)  
                 && mGestureDetector.onTouchEvent(ev);  
     }  
  
  class YScrollDetector extends SimpleOnGestureListener  
     {  
  @Override 
  public boolean onScroll(MotionEvent e1, MotionEvent e2,  
  float distanceX, float distanceY)  
         {  
  if (Math.abs(distanceY) > Math.abs(distanceX))  
             {  
  return true;  
             }  
  return false;  
         }  
     }  
 }  

好了,大功告成! 

以下系项目的源代码下载地址: 

http://download.csdn.net/detail/qq15989177612/6943633

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android开发指南

9.视差特效、回弹动画、overScrollBy

37190
来自专栏向治洪

android自定义listview实现header悬浮框效果

之前在使用iOS时,看到过一种分组的View,每一组都有一个Header,在上下滑动的时候,会有一个悬浮的Header,这种体验觉得很不错,请看下图: ? ...

81260
来自专栏肖蕾的博客

使用Kotlin为你的APP自定义一个统一的标题栏

19450
来自专栏水击三千

Android学习Tabhost、gallery、listview、imageswitcher

Tabhost控件又称分页控件,在很多的开发语言中都存在。它可以拥有多个标签页,每个标签页可以拥有不同的内容。android中,一个标签页可以放 一个view或...

29960
来自专栏编程之路

羊皮书APP(Android版)开发系列(十七)Android 底部菜单栏实现

22220
来自专栏Android Note

RecycleView之GridLayoutManager的ItemDecoration

1.4K60
来自专栏Android干货

安卓开发_深入学习ViewPager控件

40280
来自专栏向治洪

viewgroup实现item拖动效果

网络上关于GridView可拖动的例子已经不少了,包括带动画不带动画的都有一堆,但几乎都是通过继承Android原生控件GridView来扩展的,当然这种实现方...

27160
来自专栏非著名程序员

Android实现滑动刻度尺效果,选择身高体重和生日

刻度尺效果虽然看起来很美,我个人认为很不实用,即使再不实用,也有用的,鉴于群里成员对我的苦苦哀求,我就分享一个他用不到的,横屏滑动刻度尺,因为他需要竖屏的,哈哈...

297100
来自专栏向治洪

android 实现倒影

首先,文章中出现的Gallery 已经不再适用,替代方法请看我的另一篇文章http://blog.csdn.net/xiangzhihong8/article...

25350

扫码关注云+社区

领取腾讯云代金券