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

先上图:

[img]http://dl2.iteye.com/upload/attachment/0094/0570/11bc8b98-6fdf-3cfd-bbcd-357d2c4c2f03.png[/img]

解决的问题有两个:
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;
}
}
}



好了,大功告成!
以下系项目的源代码下载地址:
[url]http://download.csdn.net/detail/qq15989177612/6943633[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值