I have a ViewPager inside every row of a ListView. It works fine, it changes the views inside it when the user use the swipe gesture, but it prevents the ListView's onItemClick method to be called. I know that the ViewPager is the culprit because when I hide it, the onItemClick is called, so this is what I am trying:
I have created a ViewGroup to be the row (RowView). This ViewGroup has onInterceptTouchEvent
overriden to avoid the ViewPager to handle further touch events when I detect a click. But the onItemClick callback is still not being called. And the list selector doesn't show either on click. I want this two features to work.
This is how the onInterceptTouchEvent from RowView looks like:
@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);
}
Any suggestions to solve this?
EDIT: Not working example about how the onItemClick in MainActivity is not called when the ViewPager is active (Lollipop list selector doesn't appear either)
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);
}
}
I think better override listview onintercepter than viewgroup.
TouchEvent Flow simply :
Acitivity touch event - > viewgroup.dispatchtouchevent -> viewgroup.intercepter..-> view.dispatchtouch... -> .....
in this case list.dispatch call. toss event to ViewPager.dispatch
. but before ViewPager.dispatchtouchevent
, call ListView.intercepterTouchEvent
.
if dispatchTouchEvent
return false
call parent View
's TouchEvent
but return true
call flow descent.
if intercepterTouchEvent
return true
don't calling child dispatchTouchEvent
but return false
calling child dispatchTouchEvent
.
so listview.intercepterTouchEvent
return true
, calling onItemClick
.
so if listView.intercepterTouchEvent
return true
, not swiped viewPager
items.
you can know user's action swipe or click 2 way.
TouchEvent
and guesturedetector
..
in listview's 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);
}
you can swipe speed about 100 or move amount 100 pixel. if not perform click event.
i hope this text can help you......
and add edit some code blow.
@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);;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With