OnItemClickListener and Custom OnTouchListener - android

I have a list of views on a list view that I would like to click on and have the OnItemClickListener get triggered. Though at the same time I want to be able to swipe each view and have a custom action occur. This means that I had to create our own OnTouchEvent for each view when it is made in the ArrayAdapter.
Is there a way to have both of those working together, so that I can have a custom action such as swiping an item and clicking on the item occur easily

This is very similar to how android Recent Activities are handled - you know, they show a list of all recently opened apps, they can be swiped to remove, or clicked to open. Check out their code, I think you'll get a pretty good idea: https://github.com/android/platform_frameworks_base/tree/master/packages/SystemUI/src/com/android/systemui/recent

You class can implement both View.OnTouchListener, AdapterView.OnItemClickListener
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_UP){
Log.d(TAG, "ontouch: UP");
**// Here you can figure if it was simple item click event.
// We return false only when user touched once on the view.
// this will be handled by onItemClick listener.**
if(lastAction == -1){
lastAction = MotionEvent.ACTION_UP;
view.clearFocus();
return true;
}
return false;
}
else if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
Log.d(TAG, "ontouch: DOWN");
return false;
}
else {
// This is all action events.
lastAction = -1;
return true;
}
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// We come here after onTouch event figured out that its a simple touch event and needs to be handled here.
}

Related

Android touch event stucked in ListView

I have a container View that should process touches. It has Listview with overrided onTouch(), which returns false (it will return true, when container is expanded by swipe). When Listview touch occurs it passes to container View and everything is ok. But I need to add click handling for items of Listview.
When I set onClickListener for item, I see in logs it is no MotionEvent passed to container, it is stucked in ListView onTouch() (which return false and does nothing). I'm trying to set onTouchListener for item like this:
viewHolder.itemLayout.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
return false;
case MotionEvent.ACTION_UP:
Log.d(TAG, "something like click detected");
return true;
}
return false;
}
});
But result is the same. Can someone explain why my container does not receive touch event even ListView returns false for its overrided onTouch()?
If you wish to handle the clicks of ListView items : use OnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//parent.getItemAtPosition(position) returns the value of item clicked.. use it to do whatever you wish to do
}
});
I had the same problem about 2 weeks ago. Returning false from listview's onTouch method did not pass event to parent container. I ended up doing this:
1. You need custom layout for your container View. Then in that view, override onInterceptTouchEvent, for example like this:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
previousX_actionDOWN = ev.getRawX();
isOnClick = true;
timePressed = System.currentTimeMillis();
return false;
case MotionEvent.ACTION_MOVE:
if (isOnClick && (Math.abs(previousX_actionDOWN - ev.getRawX()) > SCROLL_THRESHOLD)) {
return true;
} else {
return false;
}
case MotionEvent.ACTION_UP:
if (!wasDragged) {
return false;
}
else {
return true;
}
}
return false;
}
I also have complicated onTouch method in that view but it's not worth pasting. Point is that your parent view should only intercept touch event if some threshold was passed while ACTION_MOVE - well at least that's what I wanted to achieve. Then in your listview you just implement standard onItemClickListener instead of onTouch.

Android - preview by press and hold

I have a listview that contains some items, i want to allow the user to preview the data of a specific item by pressing and holding on the item. i want the preview window/popup to stay showed as long as the user is pressing.
I am trying to achivie the same preview functionality in IOS and instagram
i already implemented on longpress but not sure what to is the best thing to show to get a the desired result
lv.setAdapter(arrayAdapter);
lv.setLongClickable(true);
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int pos, long id) {
Log.v("long clicked", "pos: " +pos);
return true;
}
});
any hints on how to implement that or best way to implement it ?
Well I'm using recycler view with images.
To show the image I use the long press listener calling this method:
public void publicationQuickView(Post post){
View view = getLayoutInflater().inflate( R.layout.quick_view, null);
ImageView postImage = (ImageView) view.findViewById(R.id.ivFeedCenter);
ImageView profileImage = (ImageView) view.findViewById(R.id.ivUserProfile);
TextView tvUsername = (TextView) view.findViewById(R.id.txtUsername);
tvUsername.setText(post.user.name);
Picasso.with(this).load(post.picture).priority(Picasso.Priority.HIGH).noPlaceholder().into(postImage);
Picasso.with(this).load(post.user.picture).noPlaceholder().into(profileImage);
builder = new Dialog(this);
builder.requestWindowFeature(Window.FEATURE_NO_TITLE);
builder.getWindow().setBackgroundDrawable(
new ColorDrawable(Color.TRANSPARENT));
builder.setContentView(view);
builder.show();
}
I inflate the layout and inject into a Dialog.
To dismiss the dialog I'm using the RecyclerView.OnItemTouchListener() like this:
rvUserProfile.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if(e.getAction() == MotionEvent.ACTION_UP)
hideQuickView();
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent event) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}});
And finally:
public void hideQuickView(){
if(builder != null) builder.dismiss();
}
You could add a custom OnTouchListener to the view that represents a given item in you ListView (or RecyclerView or whatever). This allows you to detect when a gesture starts (i.e. first finger down) end ends (i.e. last finger up) or is canceled (e.g. gesture was actually a scroll and has been intercepted by the ListView).
The code you need to do that would look something like that:
itemView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int actionMasked = event.getActionMasked();
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
// show preview
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// hide preview
break;
default:
}
return true;
}
});
Edit: You may need to include some logic to detect a simple tap (e.g. measure if the whole gesture lasted not longer than ViewConfiguration.getTapTimeout()) and call v.performClick().
See the following answer on how to handle long press with recycler view: RecyclerView onClick
Once you have that, you can show a layout to display your data.
Check this out...
Implement a listener on long click listener
how to implement a long click listener on a listview
and then enable the visibiity of a hidden view. While doing that of course check this link out on how to enable dynamic position of the preview pane
How can I dynamically set the position of view in Android?

Perform something onTouch if parent ScrollView doesn't handle it

OK, this might be trivial, but I can't figure out how to do it.
We have a container scrolling view, a subclass of ScrollView, and this view has a child view (among others) which should listen to touch events.
In particular, what I'd like to do is:
childView is clicked and MotionEvent.ACTION_DOWN comes;
check if that event is a scrolling event, i.e., check if that action would start a scroll on the scrollView;
if so, let the scrollView handle it and do nothing; if not, i.e. if the click upon childView did not start a scroll onto the parent view, do something.
Any ideas?
Pseudocoding, it should be something like:
ScrollView scrollView = ...;
View childView = scrollView.findViewById(...):
childView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
if (scrollView.onTouchEvent(event)) {
// event was handled by the view
return false;
} else {
// do something
return true;
}
}
return false;
}
});
As you can see I have not much insight on how motion events work.

How to select GridView item and use onTouchListener in getView?(they seem to cancel each other out)

When i use an onTouchListener in the getView of my adapter the line
android:listSelector="#drawable/circle"
immediately stops working, if I set onTouch to return false it works again, however then the ACTION_DOWN ACTION_UP dosent work properly.
Heres what i have in onTouch
image.setOnTouchListener(new View.OnTouchListener() {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Assets.playMusic(songID, false);
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Assets.mediaPlayer.stop();
Assets.mediaPlayer = null;
}
return true;
}
});
Its suppose to play music for as long as you have a finger held on the item and when you release it should stop the music. And it works well when returning true. However for some reason the circle stops appearing behind the tapped items. If it is set to false the circle appears, but then action_up dosent stop the music
ive tried using .setSelected .setActivated .setEnabled and none of them work
please help
Also i want it to work kinda like snapchats camera button, tap it and it does one thing, hold it and it does something for duration of your hold. I was going to use time variables in the Action up and down. but if anyone knows another way to do this id appreciate info about that too
In this situation is not encouraged to attach an OnTouchListener to the image, but to the items of the GridView instead.
You should have something like this:
GridView gridview = (GridView) findViewById(R.id.the_gridview_id);
gridview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, final View v, int position, long id) {
doSomething();
}
});
EDIT:
In order to know how much time a view is pressed, you can do something like this:
// this goes somewhere in your class:
long lastDown;
long lastDuration;
...
// this goes wherever you setup your button listener:
gridview.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
lastDown = System.currentTimeMillis();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
lastDuration = System.currentTimeMillis() - lastDown;
}
}
};

Simultaneous GridView button events/determining which button has been touched

I'm having a hard time trying to figure this out. I have a gridview of 8 buttons. At the moment I'm using an onItemClickListener to trigger the buttons actions, however this produces two problems for me.
1) The buttons action happens after the button has been unpressed.
2) Two buttons cannot the pressed at the same time, you must release the first button.
As I have learnt, an onTouchListener should resolve my first issue, though I'm not sure how to determine which button has been pressed. My code for the onItemClickListener is as follows
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(Activity.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
Now with the above, I know exactly which button has been pushed. I believe the code for implementing as an onTouchListener is as follows
gridview.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return false;
}
}) {
How am I supposed to determine which button has been pressed using MotionEvent? Before I was getting passed 'position' and it made this fairly easy. I also need to account for if two or more buttons have been pushed simultaneously/without letting another one go.
Does anyone know how to achieve this?
Having hit this very issue recently and coming across this post in my quest for help, I wanted to add two things from what I did which seem to have worked:
1) I added the onTouchListener to the object in the adapter rather than the activity or gridview.
2) In the OnTouchListener, I looked for MotionEvent.ACTION_DOWN (first finger touch) and MotionEvent.ACTION_POINTER_DOWN (subsequent finger touches), this way I can get multitouches and process them immediately without waiting for the user to lift their finger(s).
Note that I called it ImageAdapter, even though I've added a TextView to each as that way I can use the TextView background for the image, but add invisible text to the TextView so it works with Talkback):
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return numCols * numRows;
}
public Object getItem(int position) {
return this;
}
public long getItemId(int position) {
return position;
}
// create a new TextView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView;
if (convertView == null) { // if it's not recycled, initialize some attributes
textView = new TextView(mContext);
} else {
textView = (TextView) convertView;
}
// place any other initial setup needed for the TextView here
// here's our onTouchListener
textView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
boolean returnValue;
int thePosition = v.getId();
// MotionEvent.ACTION_DOWN gets the first touch
// MotionEvent.ACTION_POINTER_DOWN gets any subsequent touches (if you place a second finger on the screen)
// Between these I can get touches as soon as they happen, including multitouch support, without needing to wait until the user lifts their finger.
if ((event.getAction() == MotionEvent.ACTION_DOWN) || (event.getAction() == MotionEvent.ACTION_POINTER_DOWN)) {
TextView textView;
if (v == null) { // if it's not recycled, initialize some attributes
textView = new TextView(mContext);
} else {
textView = (TextView) v;
}
// Do any processing based on the touch - I call a function and pass the position (number of cell, 1..n) and textview so can make changes to it as needed
ScreenTapped(thePosition, textView);
// I used a returnValue
returnValue = true;
} else returnValue = false;
return returnValue;
});
return textView;
} // getView
} //imageadapter
I am infact trying to figure the same thing out. I got as far as figuring out which gridcell has been clicked using the following code
public boolean onTouch(View v, MotionEvent me) {
float currentXPosition = me.getX();
float currentYPosition = me.getY();
int position = gridView.pointToPosition((int) currentXPosition, (int) currentYPosition);
Position gives you the number on the gridView, and you can supposedly retrieve that particular item as following
gridView.getItemAtPosition(position)
But that is where I am stuck. My gridView has Textview items in it, and I am having trouble converting the item to a textview and then performing operations on it.
Hope this helps!
When using gridView the philosophy is:
the grid view implements the onTouchLister
when touch happens onTouchLister gathers the coordinates (a lot :) )
for all ACTION_MOVE events
when the touch event is MOVE_UP, calculate the real positions under
the coordinates and return the item in the grid
So the solution would be:
In your activity where you have findViewById(some_grid_view)
//Register handler for the onTouch event of gridView in your activity
gridView.setOnTouchListener(new MyActivityOnTouchListener(this));
NOTE: my onTouch listener is implemented in another class (MyActivityOnTouchListener) instead of inside the activity
...then in the MyActivityOnTouchListener class you implement the onTouch method:
public class CalendarActivityOnTouchListener implements View.OnTouchListener {
private MyActivity myActivityContext;
private GridView mGridView;
private HashSet<Point> movementCoordinates = new HashSet<Point>;
//Constructor
public MyActivityOnTouchListener (MyActivity context){
this.myActivityContext= context;
mGridView= myActivityContext.getGridView(); //assign touched gridView into a local variable
}
#Override
public boolean onTouch(View v, MotionEvent event) {
/*
* NOTE:
* ACTION_MOVE fires events until you release it
* ACTION_UP once you release it fires it
*/
//while touching the grid a bunch of ACTION_MOVE events are dispatched
if (event.getAction() == MotionEvent.ACTION_MOVE) {
//gather all coordinates touched (in a set to avoid duplicates)
movementCoordinates.add(new Point((int)event.getX(), (int)event.getY()));
return true;
}
//Finally the finger is lifted
if (event.getAction() == MotionEvent.ACTION_UP) {
//convert all movementCoordinates gathered in the previous block into real grid positions
int position;
for(Point p : movementCoordinates){
Log.d("Luka", p.x +" / "+p.y);
position = calendarGridView.pointToPosition(p.x, p.y);
//...Do whatever with the position
}
}
}
}
Be careful about the pointToPosition() method because in some cases it can return -1 instead of the position behind the coordinates. For example, if you have a margin between items in the grid those coordinates cannot return a position, hence the -1
hope it helps...

Categories

Resources