Reputation: 4825
I have an Activity
, inside is ViewPager
and inside is ListFragment
with ListView
items. I want define touch gesture for the items (drag, fling etc.).
I'm able to attach onTouchListener()
to each ListView
item View
by overriding getView()
of adapter.
Adapter adapter = new Adapter(blah, blah, blah) {
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View itemView = super.getView(position, convertView, parent);
itemView.setOnTouchListener(new View.OnTouchListener() {
blah, blah, blah
});
return itemView;
}
};
But I only receive MotionEvent.ACTION_DOWN
, MotionEvent.ACTION_MOVE
and MotionEvent.ACTION_UP
if it occurs inside triggering item View
boundary. For example I'm not able to catch MotionEvent.ACTION_OUTSIDE
.
My guess is, that my custom onTouchListener
compete with listener of ViewPager
and of Activity
. I want to trigger these other listeners in some occasions, i.e. if user move to side, I want to slide the whole View
inside ViewPager
but if he push on the ListView
item and move vertically, I want startDrag()
of the item.
How to implement that?
Edit/ Currently, my custom listener on ListView
items works along with ViewPager
, which is great. I'm able to catch ListView
item events as long as I don't move outside its View
and I'm able to slide the whole View
in ViewPager
as well.
Edit/ I've rewrote my ListView
into RecycledView
and realized that if I attach listener to item View
, the ViewPager
catches almost all move events, while allowing only the click events to go through. I've also realized, that if I attach OnTouchListener
to the RecycledList
it is able to catch move events along with the ViewPager
so it depends on which level I attach the Listener
. Problem is, that in item View
level I have available full reference to item, while when working with the list, I need to guess item position from event coordinates and then gather data from Adapter and RecycledView
, which is extra work.
Edit/ it wasn't that hard after all. Luckily, RecycledView
has findChildViewUnder(int xPos, int yPos)
method, so getting the View
was piece of cake.
Here's my implementation, if someone is interested
mRecycledView.setOnTouchListener(new View.OnTouchListener() {
int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
float mItemViewHeight, mInitialY;
boolean mIsResolved;
final Handler mHandler = new Handler(Looper.getMainLooper());
final Runnable mLongPress = new Runnable() {
@Override
public void run() {
mIsResolved = true;
onLongClick();
}
};
@Override
public boolean onTouch(View v, MotionEvent me) {
switch (MotionEventCompat.getActionMasked(me)) {
case MotionEvent.ACTION_DOWN:
mInitialY = me.getY();
mActiveUserView = mRecycledView.findChildViewUnder(me.getX(), mInitialY);
if (mActiveUserView == null) { // clicked to RecycledView where's no item
mIsResolved = true;
} else {
mIsResolved = false;
mItemViewHeight = (float) mActiveUserView.getHeight();
mHandler.postDelayed(mLongPress, LONG_PRESS_TIMEOUT);
}
break;
case MotionEvent.ACTION_MOVE:
if (!mIsResolved) {
stopLongClickHandler();
// check for vertical upward move
if (mInitialY - me.getY() > mItemViewHeight) {
mIsResolved = true;
onVerticalDrag();
}
}
break;
case MotionEvent.ACTION_UP:
if (!mIsResolved) {
stopLongClickHandler();
mIsResolved = true;
onClick();
}
}
return !mIsResolved;
}
void stopLongClickHandler() {
mHandler.removeCallbacksAndMessages(null);
}
void onVerticalDrag() {
Log.e("DEBUG", "start drag");
}
void onLongClick() {
Log.e("DEBUG", "long click");
}
void onClick() {
Log.e("DEBUG", "short click");
}
});
Upvotes: 0
Views: 111
Reputation: 3794
I can suggest you replacing ListView
with RecyclerView
. Then read this tutorial: it contains almost every drag/swipe/touch implementations on RecyclerView
clearly explained.
Upvotes: 2