ListView 行内的 ViewPager 阻止触发 onItemClick
ViewPager inside ListView row prevents onItemClick to be fired
我在 ListView 的每一行中都有一个 ViewPager。它工作正常,当用户使用滑动手势时它会更改其中的视图,但它会阻止调用 ListView 的 onItemClick 方法。我知道 ViewPager 是罪魁祸首,因为当我隐藏它时,会调用 onItemClick,所以这就是我正在尝试的:
我创建了一个 ViewGroup 作为行 (RowView)。此 ViewGroup 已覆盖 onInterceptTouchEvent
以避免 ViewPager 在我检测到单击时处理进一步的触摸事件。但是 onItemClick 回调仍然没有被调用。并且列表选择器不会在点击时显示。我希望这两个功能起作用。
这是来自 RowView 的 onInterceptTouchEvent 的样子:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int actionMasked = ev.getActionMasked();
switch(actionMasked) {
case MotionEvent.ACTION_DOWN:
Log.d("RowView", "OnInterceptTouchEvent - Down");
tapDetector.onTouchEvent(ev);
return false;
case MotionEvent.ACTION_CANCEL:
Log.d("RowView", "OnInterceptTouchEvent - Cancel");
tapDetector.onTouchEvent(ev);
break;
case MotionEvent.ACTION_UP:
if(tapDetector.onTouchEvent(ev)) {
Log.d("RowView", "OnInterceptTouchEvent - UP!");
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
有什么解决这个问题的建议吗?
编辑:有关当 ViewPager 处于活动状态时如何不调用 MainActivity 中的 onItemClick 的无效示例(Lollipop 列表选择器也未出现)
MainActivity
ListView listView = (ListView) findViewById(R.id.main_list);
listView.setAdapter(new MainListAdapter(this, 30));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d(TAG, "onItemClick: " + position);
}
});
List item XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
android:descendantFocusability="blocksDescendants"
>
<TextView
android:id="@+id/row_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
/>
<android.support.v4.view.ViewPager
android:id="@+id/row_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
/>
</RelativeLayout>
List adapter:
public class MainListAdapter extends BaseAdapter {
private Context context;
private LayoutInflater inflater;
private int count;
public MainListAdapter(Context context, int count) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.count = count;
}
@Override
public int getCount() {
return count;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = createRow(parent, holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(Integer.toString(position));
int randomPages = (int) (new Random().nextDouble()*5+2);
holder.viewPager.setAdapter(new NumAdapter(context, randomPages));
return convertView;
}
private View createRow(ViewGroup parent, ViewHolder holder) {
View view = inflater.inflate(R.layout.row_main_listview, parent, false);
holder.textView = (TextView) view.findViewById(R.id.row_num);
holder.viewPager = (ViewPager) view.findViewById(R.id.row_viewpager);
view.setTag(holder);
return view;
}
private static class ViewHolder {
public TextView textView;
public ViewPager viewPager;
}
}
ViewPager's Adapter:
public class NumAdapter extends PagerAdapter {
private LayoutInflater inflater;
private int count;
public NumAdapter(Context context, int count) {
this.inflater = LayoutInflater.from(context);
this.count = count;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView textView = (TextView) inflater.inflate(R.layout.page_viewpager, container, false);
textView.setText("Page " + position);
container.addView(textView);
return textView;
}
@Override
public int getCount() {
return count;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
}
您需要执行以下操作之一:
在 Viewpager
的 child 上设置 setOnClickListener
或查找
android:descendantFocusability="beforeDescendants"
希望对您有所帮助。
我认为重写 listview onintercepter 比重写 viewgroup 更好。
TouchEvent 流程简单:
Acitivity 触摸事件 -> viewgroup.dispatchtouchevent -> viewgroup.intercepter..-> view.dispatchtouch... -> .....
在这种情况下 list.dispatch 调用。将事件扔给 ViewPager.dispatch
。但在 ViewPager.dispatchtouchevent
之前,调用 ListView.intercepterTouchEvent
.
if dispatchTouchEvent
return false
调用 parent View
的 TouchEvent
但 return true
调用流下降。
if intercepterTouchEvent
return true
不调用 child dispatchTouchEvent
但 return false
调用 child dispatchTouchEvent
。
所以 listview.intercepterTouchEvent
return true
, 调用 onItemClick
.
所以如果 listView.intercepterTouchEvent
return true
,没有刷 viewPager
项。
你可以知道用户的动作滑动或点击 2 方式。
TouchEvent
和 guesturedetector
..
在列表视图中 IntercepterTouchEvent(Event ev);
VelocityTracker mVelocityTracker;
PointF mLastPoint;
public mListView(Context context) {
super(context);
init();
}
public mListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public mListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mLastPoint = new PointF();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(100);
int x = (int)Math.abs(mVelocityTracker.getXVelocity());
int move_x = (int)Math.abs(ev.getX() - mLastPoint.x);
Log.d("ListView","speed : " + x +" move_x : " + move_x);
//here x is drag speed. (pixel/s)
// change value left right both value you want speed and move amount
if(move_x < 100 || x <100) {
mLastPoint.set(ev.getX(), ev.getY());
return true;
}
break;
case MotionEvent.ACTION_DOWN:
mLastPoint.set(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.recycle();mVelocityTracker = null;
break;
}
return super.onInterceptTouchEvent(ev);
}
您可以滑动速度大约 100 或移动量 100 像素。如果不执行点击事件。
希望本文能对您有所帮助......
并添加编辑一些代码。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(10);
int x = (int)Math.abs(mVelocityTracker.getXVelocity());
int move_x = (int)Math.abs(ev.getX() - mLastPoint.x);
int move_y = (int)Math.abs(ev.getY() - mLastPoint.y);
Log.d("ListView","speed : " + x +" move_x : " + move_x + " move_y : "+ move_y);
if(move_x < move_y || x < 10) {
mLastPoint.set(ev.getX(), ev.getY());
return true;
}else if(move_x > move_y){
return false;
}
break;
case MotionEvent.ACTION_DOWN:
mLastPoint.set(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("ListView", "dispatch");
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
if(mVelocityTracker != null){mVelocityTracker.recycle();mVelocityTracker = null;}
break;
}
return super.dispatchTouchEvent(ev);;
}
我认为这应该可以解决问题。
viewPager.getParent().requestDisallowInterceptTouchEvent(false);
希望对您有所帮助。
由于您的 viewpaper 是您的列表视图 child,它会消耗触摸事件。您可以通过使您的 viewpager child 不可点击来防止这种情况,即
TextView textView =(TextView)inflater.inflate(R.layout.page_viewpager,container, false);
textView.setText("Page " + position);
textView.setClickable(false);
我们的想法是在视图寻呼机而不是 listView 上执行点击侦听器
在getView方法中添加viewPager时,将其标签设置为行的位置
holder.viewPager.setTag(position);
然后给viewPager添加点击监听器(getView也一样)
viewPager.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int position = v.getTag();
Log.d(TAG, "onItemClick: " + position);
//Fire a delegate or notification of whatever you want to do on the item click. You now have the position
myClickListener.onItemClicked(position);
}
});
问题出在列表适配器查看器上。当我尝试实现相同的功能(列表视图行内的 viewpager)时,我遇到了类似的问题。我通过这样做解决了它----
在 viewholder 对象上设置 onclicklistener,而不是直接在 listview 上设置。为此,您必须在适配器 class.
上实施 onitemclicklistener
我建议在每个 ViewPager
实例本身上设置 OnClickListener
并避免使用 ListView 的 onItemClickListener
。然后,您也可以完全删除 onInterceptTouchEvent()
。那将是最简单和稳定的解决方案。更少的代码 - 更少的错误 ;)
尽管列表选择器不起作用,但这是我最终为解决此问题所做的工作。这可以改进,但就目前而言,这是我喜欢它工作方式的唯一解决方法。
public class CustomListView extends ListView implements AbsListView.OnScrollListener {
/*
* Used for detect taps
*/
private GestureDetector tapDetector;
/*
* As we need to set our own OnScrollListener, this stores the one
* used outside, if any
*/
private OnScrollListener onScrollListener;
private boolean isScrolling = false;
public CustomListView(Context context) {
super(context);
initView(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
private void initView(Context context) {
tapDetector = new GestureDetector(context, new TapListener());
super.setOnScrollListener(this);
}
@Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isTap = tapDetector.onTouchEvent(ev);
if(ev.getActionMasked() == MotionEvent.ACTION_UP) {
// Don't perform the click if the ListView is scrolling
// so it is able to stop the scroll
if(isTap && !isScrolling && hasOnItemClickListener()) {
int itemPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
performItemClick(this, itemPosition, getItemIdAtPosition(itemPosition));
}
}
return super.dispatchTouchEvent(ev);
}
public boolean hasOnItemClickListener() {
return getOnItemClickListener() != null;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
isScrolling = scrollState != OnScrollListener.SCROLL_STATE_IDLE;
if(this.onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(this.onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
对 Adrian 的回答的改进:
public class CustomListView extends ListView {
private GestureDetector gestureDetector;
public CustomListView(Context context) {
super(context);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
private class CustomListViewOnGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent ev) {
int firstVisiblePosition = getFirstVisiblePosition();
int itemPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
int childIndex = itemPosition - firstVisiblePosition;
View view = getChildAt(childIndex);
performItemClick(view, itemPosition, getItemIdAtPosition(itemPosition));
return true;
}
}
}
我在 ListView 的每一行中都有一个 ViewPager。它工作正常,当用户使用滑动手势时它会更改其中的视图,但它会阻止调用 ListView 的 onItemClick 方法。我知道 ViewPager 是罪魁祸首,因为当我隐藏它时,会调用 onItemClick,所以这就是我正在尝试的:
我创建了一个 ViewGroup 作为行 (RowView)。此 ViewGroup 已覆盖 onInterceptTouchEvent
以避免 ViewPager 在我检测到单击时处理进一步的触摸事件。但是 onItemClick 回调仍然没有被调用。并且列表选择器不会在点击时显示。我希望这两个功能起作用。
这是来自 RowView 的 onInterceptTouchEvent 的样子:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int actionMasked = ev.getActionMasked();
switch(actionMasked) {
case MotionEvent.ACTION_DOWN:
Log.d("RowView", "OnInterceptTouchEvent - Down");
tapDetector.onTouchEvent(ev);
return false;
case MotionEvent.ACTION_CANCEL:
Log.d("RowView", "OnInterceptTouchEvent - Cancel");
tapDetector.onTouchEvent(ev);
break;
case MotionEvent.ACTION_UP:
if(tapDetector.onTouchEvent(ev)) {
Log.d("RowView", "OnInterceptTouchEvent - UP!");
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
有什么解决这个问题的建议吗?
编辑:有关当 ViewPager 处于活动状态时如何不调用 MainActivity 中的 onItemClick 的无效示例(Lollipop 列表选择器也未出现)
MainActivity
ListView listView = (ListView) findViewById(R.id.main_list);
listView.setAdapter(new MainListAdapter(this, 30));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d(TAG, "onItemClick: " + position);
}
});
List item XML:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"
android:descendantFocusability="blocksDescendants"
>
<TextView
android:id="@+id/row_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
/>
<android.support.v4.view.ViewPager
android:id="@+id/row_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
/>
</RelativeLayout>
List adapter:
public class MainListAdapter extends BaseAdapter {
private Context context;
private LayoutInflater inflater;
private int count;
public MainListAdapter(Context context, int count) {
this.context = context;
this.inflater = LayoutInflater.from(context);
this.count = count;
}
@Override
public int getCount() {
return count;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = createRow(parent, holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(Integer.toString(position));
int randomPages = (int) (new Random().nextDouble()*5+2);
holder.viewPager.setAdapter(new NumAdapter(context, randomPages));
return convertView;
}
private View createRow(ViewGroup parent, ViewHolder holder) {
View view = inflater.inflate(R.layout.row_main_listview, parent, false);
holder.textView = (TextView) view.findViewById(R.id.row_num);
holder.viewPager = (ViewPager) view.findViewById(R.id.row_viewpager);
view.setTag(holder);
return view;
}
private static class ViewHolder {
public TextView textView;
public ViewPager viewPager;
}
}
ViewPager's Adapter:
public class NumAdapter extends PagerAdapter {
private LayoutInflater inflater;
private int count;
public NumAdapter(Context context, int count) {
this.inflater = LayoutInflater.from(context);
this.count = count;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView textView = (TextView) inflater.inflate(R.layout.page_viewpager, container, false);
textView.setText("Page " + position);
container.addView(textView);
return textView;
}
@Override
public int getCount() {
return count;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
}
您需要执行以下操作之一:
在 Viewpager
的 child 上设置 setOnClickListener
或查找
android:descendantFocusability="beforeDescendants"
希望对您有所帮助。
我认为重写 listview onintercepter 比重写 viewgroup 更好。
TouchEvent 流程简单:
Acitivity 触摸事件 -> viewgroup.dispatchtouchevent -> viewgroup.intercepter..-> view.dispatchtouch... -> .....
在这种情况下 list.dispatch 调用。将事件扔给 ViewPager.dispatch
。但在 ViewPager.dispatchtouchevent
之前,调用 ListView.intercepterTouchEvent
.
if dispatchTouchEvent
return false
调用 parent View
的 TouchEvent
但 return true
调用流下降。
if intercepterTouchEvent
return true
不调用 child dispatchTouchEvent
但 return false
调用 child dispatchTouchEvent
。
所以 listview.intercepterTouchEvent
return true
, 调用 onItemClick
.
所以如果 listView.intercepterTouchEvent
return true
,没有刷 viewPager
项。
你可以知道用户的动作滑动或点击 2 方式。
TouchEvent
和 guesturedetector
..
在列表视图中 IntercepterTouchEvent(Event ev);
VelocityTracker mVelocityTracker;
PointF mLastPoint;
public mListView(Context context) {
super(context);
init();
}
public mListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public mListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mLastPoint = new PointF();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(100);
int x = (int)Math.abs(mVelocityTracker.getXVelocity());
int move_x = (int)Math.abs(ev.getX() - mLastPoint.x);
Log.d("ListView","speed : " + x +" move_x : " + move_x);
//here x is drag speed. (pixel/s)
// change value left right both value you want speed and move amount
if(move_x < 100 || x <100) {
mLastPoint.set(ev.getX(), ev.getY());
return true;
}
break;
case MotionEvent.ACTION_DOWN:
mLastPoint.set(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.recycle();mVelocityTracker = null;
break;
}
return super.onInterceptTouchEvent(ev);
}
您可以滑动速度大约 100 或移动量 100 像素。如果不执行点击事件。
希望本文能对您有所帮助......
并添加编辑一些代码。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(10);
int x = (int)Math.abs(mVelocityTracker.getXVelocity());
int move_x = (int)Math.abs(ev.getX() - mLastPoint.x);
int move_y = (int)Math.abs(ev.getY() - mLastPoint.y);
Log.d("ListView","speed : " + x +" move_x : " + move_x + " move_y : "+ move_y);
if(move_x < move_y || x < 10) {
mLastPoint.set(ev.getX(), ev.getY());
return true;
}else if(move_x > move_y){
return false;
}
break;
case MotionEvent.ACTION_DOWN:
mLastPoint.set(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("ListView", "dispatch");
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
if(mVelocityTracker != null){mVelocityTracker.recycle();mVelocityTracker = null;}
break;
}
return super.dispatchTouchEvent(ev);;
}
我认为这应该可以解决问题。
viewPager.getParent().requestDisallowInterceptTouchEvent(false);
希望对您有所帮助。
由于您的 viewpaper 是您的列表视图 child,它会消耗触摸事件。您可以通过使您的 viewpager child 不可点击来防止这种情况,即
TextView textView =(TextView)inflater.inflate(R.layout.page_viewpager,container, false);
textView.setText("Page " + position);
textView.setClickable(false);
我们的想法是在视图寻呼机而不是 listView 上执行点击侦听器
在getView方法中添加viewPager时,将其标签设置为行的位置
holder.viewPager.setTag(position);
然后给viewPager添加点击监听器(getView也一样)
viewPager.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int position = v.getTag();
Log.d(TAG, "onItemClick: " + position);
//Fire a delegate or notification of whatever you want to do on the item click. You now have the position
myClickListener.onItemClicked(position);
}
});
问题出在列表适配器查看器上。当我尝试实现相同的功能(列表视图行内的 viewpager)时,我遇到了类似的问题。我通过这样做解决了它---- 在 viewholder 对象上设置 onclicklistener,而不是直接在 listview 上设置。为此,您必须在适配器 class.
上实施 onitemclicklistener我建议在每个 ViewPager
实例本身上设置 OnClickListener
并避免使用 ListView 的 onItemClickListener
。然后,您也可以完全删除 onInterceptTouchEvent()
。那将是最简单和稳定的解决方案。更少的代码 - 更少的错误 ;)
尽管列表选择器不起作用,但这是我最终为解决此问题所做的工作。这可以改进,但就目前而言,这是我喜欢它工作方式的唯一解决方法。
public class CustomListView extends ListView implements AbsListView.OnScrollListener {
/*
* Used for detect taps
*/
private GestureDetector tapDetector;
/*
* As we need to set our own OnScrollListener, this stores the one
* used outside, if any
*/
private OnScrollListener onScrollListener;
private boolean isScrolling = false;
public CustomListView(Context context) {
super(context);
initView(context);
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
private void initView(Context context) {
tapDetector = new GestureDetector(context, new TapListener());
super.setOnScrollListener(this);
}
@Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isTap = tapDetector.onTouchEvent(ev);
if(ev.getActionMasked() == MotionEvent.ACTION_UP) {
// Don't perform the click if the ListView is scrolling
// so it is able to stop the scroll
if(isTap && !isScrolling && hasOnItemClickListener()) {
int itemPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
performItemClick(this, itemPosition, getItemIdAtPosition(itemPosition));
}
}
return super.dispatchTouchEvent(ev);
}
public boolean hasOnItemClickListener() {
return getOnItemClickListener() != null;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
isScrolling = scrollState != OnScrollListener.SCROLL_STATE_IDLE;
if(this.onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(this.onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
对 Adrian 的回答的改进:
public class CustomListView extends ListView {
private GestureDetector gestureDetector;
public CustomListView(Context context) {
super(context);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
gestureDetector = new GestureDetector(context, new CustomListViewOnGestureListener());
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
private class CustomListViewOnGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent ev) {
int firstVisiblePosition = getFirstVisiblePosition();
int itemPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
int childIndex = itemPosition - firstVisiblePosition;
View view = getChildAt(childIndex);
performItemClick(view, itemPosition, getItemIdAtPosition(itemPosition));
return true;
}
}
}