解决水平ListView在ScrollView中出现的滑动冲突
时间:2022-04-26
本文章向大家介绍解决水平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
- Centos7修改默认网卡名(改为eth0)以及网卡启动报错RTNETLINK answers: File exists处理
- 通过企业分布式缓存共享运行时数据
- 移植SlidingMenu Android library,和安装example出现的问题解决
- 无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(2)插入记录
- Centos中yum方式安装java
- 微信小程序新革命催生新物种新物种带来大红利!玩转行业新玩法
- 无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(3)删除记录
- 部署Zipkin分布式性能追踪日志系统的操作记录
- 无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(4)显示记录
- Android 2.x中使用actionbar - Actionbarsherlock (2)
- python读txt和xml
- 让Jexus支持高并发请求的优化技巧
- 数据压缩算法LZO (C#)
- Html之初体验
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- (译)针对 Kubernetes 工作负载的策略工具
- 又被逼着优化代码,这次我干掉了出入参 Log日志
- 想去看机会?这10道最高频的手撕代码题都会了吗?
- 你知道Python中的4种变量作用域是哪些吗?
- 图解 Python 浅拷贝与深拷贝
- 打卡群刷题总结0716——不同路径
- 原理 + 代码|手把手教你用Python实现智能推荐算法
- 机器学习必刷题-基础概念篇(1):为什么用AUC做评价指标?
- 机器学习必刷题-手撕推导篇(3):FM与softmax
- Python面试必刷题系列(4)
- SQL面试必刷题(1) Case When
- 张量的数学运算
- 数据结构高频面试题-图
- nn.functional和nn.Module
- 20分钟学会DBSCAN聚类算法