`
大头K
  • 浏览: 187000 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

实现水平ListView,并且解决水平ListView在ScrollView中出现的滑动冲突

阅读更多
先上图:



解决的问题有两个:
1)实现水平滑动的ListView。重写AdapterView,上代码:
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,以达到流畅滑动:
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
  • 大小: 144.3 KB
1
1
分享到:
评论

相关推荐

    android完美解决listView与ScrollView滑动冲突

    总之,解决Android中的ListView与ScrollView滑动冲突问题,需要理解事件分发机制,以及掌握各种滚动控件的特性和API。通过合理的设计和编程,可以实现两者无缝协作,提供流畅的用户体验。在提供的压缩包文件...

    解决scrollView和listview滚动冲突,实现listview滑动到顶端和底部之后还能拖拽一定的距离,松开后返回

    在Android开发中,ScrollView和ListView是两种常用的布局组件,它们分别用于实现可滚动的大视图和展示多行可滚动的数据列表。然而,在实际应用中,当这两种组件同时存在于一个界面时,滚动冲突的问题就会出现,导致...

    Android中自定义ListView,解决scrollview嵌套listview 滑动事件冲突

    在提供的MyListView文件中,可能就包含了自定义ListView解决滑动冲突的实现代码。通过查看和分析这个文件,我们可以学习到如何在代码层面解决此类问题,加深对Android事件分发机制的理解,并提升自定义控件的能力。 ...

    android 解决ScrollView和listView嵌套冲突问题(保证在ScrollView中滑动listView只响应listView的滑动)

    本篇将详细介绍如何解决这种冲突,确保在ScrollView中滑动ListView时,只响应ListView的滑动。 首先,我们要理解ScrollView和ListView的工作原理。ScrollView是一个可滚动的布局容器,它可以包含一个或多个视图,并...

    ScrollView嵌套ListView滑动冲突的解决方法

    当需要在一个界面中同时展示大量数据和一些额外内容时,可能会将ListView嵌套在ScrollView中,但这种做法往往会导致滑动冲突的问题,即用户难以确定是想滚动整个ScrollView还是单独滑动ListView。本文将深入探讨这个...

    解决listview潜逃在scrollview中的滑动冲突

    然而,当ListView被嵌套在ScrollView中时,就会出现所谓的“滑动冲突”问题。这是因为两者都具有滚动功能,系统无法判断应该响应哪个组件的滑动事件。本篇将详细介绍如何解决这个问题。 首先,我们需要理解滑动冲突...

    解决Android中ListView和ScrollView嵌套冲突

    解决ListView和ScrollView嵌套冲突的一种常见方法是避免直接在ScrollView内使用ListView。如果数据量不大,可以考虑改用LinearLayout或者NestedScrollView,它们可以更好地与ScrollView协同工作。但对于大数据集,...

    ScrollView嵌套ListVIew两者之间滑动问题

    然而,当在一个ScrollView中嵌套一个ListView时,就会出现一些特殊的问题,主要是关于滑动事件的处理,即所谓的“滑动冲突”。 首先,我们要理解滑动冲突的本质。当ScrollView和ListView同时存在于同一个布局中,...

    ScrollView与ListView的滑动冲突

    在Android开发过程中,我们经常会遇到ScrollView与ListView或GridView组合使用时出现的滑动冲突问题。这类问题通常表现为:当ScrollView内部包含一个ListView或GridView时,用户滑动屏幕时可能无法正常滚动列表中的...

    listview与scrollview冲突问题解决

    然而,在某些场景下,开发者可能希望在一个`ScrollView`中嵌套一个`ListView`,以实现更复杂的设计需求。然而,这种嵌套设计往往会导致`ListView`无法正常滚动的问题,即`ListView`似乎只显示一行多一点的内容,而...

    解决listview和scrollview冲突

    在Android开发中,ListView和ScrollView是两种常用的布局控件,它们各自有其特定的使用场景。ListView主要用于显示大量可滚动的数据列表,而ScrollView则用于包裹任何视图,使其可以滚动查看完整内容。当在一个布局...

    ScrollView+ListView 解决滑动冲突

    在上述代码中,我们检查了ListView是否位于顶部并且没有滚动空间,如果是,则不拦截触摸事件,让事件传递给ListView,否则,ScrollView会处理滑动事件。 ### 内部拦截法(OnTouchEvent) 内部拦截法主要是在...

    viewpage嵌套listview,效果:滑动listview删除,滑动非listview区域viewpage翻页

    标题"viewpage嵌套listview,效果:滑动listview删除,滑动非listview区域viewpage翻页"描述的就是这样一个功能:在ViewPager中嵌套了一个ListView,ListView支持滑动删除,而当滑动到ListView之外的区域时,...

    水平 横线 listView 嵌套在 ScrollView中 已经处理滑动问题

    在Android开发中,有时我们需要创建一个布局,其中包含一个可以滚动的垂直视图(如ScrollView)和一个可横向滑动的列表(如HorizontalListView)。这样的布局设计可以为用户提供丰富的交互体验,但同时也带来了一些...

    ScrollView嵌套ListView滑动冲突的解决方案

    当需要在一个界面中同时展示大量数据和一些额外的内容时,我们可能会将ListView嵌套在ScrollView内,但这常常会导致滑动冲突的问题。本篇文章将深入探讨这个问题,并提供解决方案。 滑动冲突的原因: 1. **天然冲突...

    listView和scrollView焦点冲突问题

    在一些项目中需要用到listview嵌套在scrollview中的情况,但会碰到焦点冲突,scrollview焦点覆盖listview造成listview不能滑动,这种情况时要在scrollview中添加android:fillViewport="true"这句,并在listview中...

    ScrollView+ListView冲突问题完美解决

    此外,为了更好地管理ScrollView和ListView的组合,我们还可以使用NestedScrollView替代ScrollView,NestedScrollView是Android支持库中的一个组件,它内置了解决滑动冲突的机制。只需要将ListView作为...

    Android解决ScrollView和ListView冲突问题Demo

    本Demo通过自定义ListView,实现了在ScrollView中嵌套ListView而不发生冲突的效果,从而提供了一种有效的解决方案。通过学习和实践这个Demo,开发者可以更深入地理解Android布局组件的交互,并能更好地处理类似的...

    自定义ViewPager,完美解决ListView和ScrollView事件冲突

    自定义的ViewPager,可以实现页面之前的相互切换,可以...同时该ViewPager解决了传统ViewPager和ListView,ScrollView滑动冲突问题,当然ListView的滑动冲突实在ScrollView中解决的。实践表明,滑动切换效果较好。

Global site tag (gtag.js) - Google Analytics