前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >解决水平ListView在ScrollView中出现的滑动冲突

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

作者头像
xiangzhihong
发布2018-02-01 14:35:56
1.6K0
发布2018-02-01 14:35:56
举报
文章被收录于专栏:向治洪向治洪向治洪

解决的问题有两个: 

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-02-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档