I have a spinner and I want to detect when the user clicked on it.
When the user clicks it open, I want to call an endpoint and then update the spinner with new data.
But I couldn't figure out how to do this and results in infinite loop:
I call the API in here:
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// Call API here
// Do render with old data.
}
when the API successes, I call the to update:
#Override
public void onSuccess(final Response response) {
Helper.update(...);
}
public void update(...) {
...
adapter.setItems(newData);
adapter.notifyDataSetChanged();
}
and then this triggers getDropDownView() again. I couldn't figure out what else is called after the user clicks open the spinner besides getDropDownView(). How can I solve this problem?
You can set Touch-listener on Spinner to detect click.
findViewById(R.id.spinner).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Toast.makeText(MainActivity.this,"CallAPI",Toast.LENGTH_SHORT).show();
return false;
}
});
And return False from onTouch to behave normally.I hope It will work for you.
Related
Briefly to say, I have a RecyclerView (a ViewGroup) which should response to TouchEvent, while I also suppose that its item can response individually to TouchEvent.
However, that seems to be impossible for me. For example, if I make both in onTouchEvent() method, then if the item(child view) return a true when ACTION_DOWN, then ViewGroup cannot catch it, whereas if false, the child self cannot response to ACTION_MOVE or ACTION_UP.
I also turned to addOnItemTouchListener() onInterceptTouchEvent(), but none works.
The question is not clear. But based on my understanding, you want to get the click event for entire view and also for each item at the same time.
Get click event for entire view
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
// this will be called multiple times for single click - for MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP, and MotionEvent.ACTION_MOVE
// So restricting the call only for ACTION_DOWN,
if(e.getAction() == MotionEvent.ACTION_DOWN) {
Toast.makeText(mContext, "Clicked view", Toast.LENGTH_LONG).show();
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
});
and to get the click event for each item, add click listener in onBindViewHolder function
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
holder.item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext, "Clicked Item", Toast.LENGTH_LONG).show();
}
});
}
Instead of touch listener you can also use click listener on any element of recycler view row...
I have a ListView displayed on my screen.
When I click on an item (using setOnItemClickListener), you can rename it.
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick() {}
When I hold on an item (using setOnItemLongClickListener), you can delete it.
listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick() {}
However, when i hold on an item, it displays the "rename" window and the "delete" window, as if both were triggered.
Any idea on how to prevent this?
Thanks a lot
Try this
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
return true; //<-- this should be TRUE, not FALSE
}
Input Events This returns a boolean to indicate whether you have consumed the event and it should not be carried further.
If it return true to indicate that you have handled the event and it should stop here.
If it return false if you have not handled it and the event should continue to any other on-click listeners.
You can use setTimeout and .bind to fix your problem with the shortest amount of logic:
var timeoutId = 0;
$('#myElement').mousedown(function() {
timeoutId = setTimeout(myFunction, 1000);
}).bind('mouseup mouseleave', function() {
clearTimeout(timeoutId);
});
In my onBindViewHolder of my RecyclerView.Adapter<SearchAdapter.ViewHolder> when user clicks on cardview a button becomes visible. But when I'm scrolling recyclerview some other items buttons are shown as visible too. Why is this happening?
this is my code:
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
viewHolder.card.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (viewHolder.content_layout.getVisibility() == View.VISIBLE) {
viewHolder.content_layout.setVisibility(View.GONE);
viewHolder.address.setMaxLines(2);
viewHolder.attribute.setMaxLines(2);
} else {
viewHolder.content_layout.setVisibility(View.VISIBLE);
viewHolder.address.setMaxLines(8);
viewHolder.attribute.setMaxLines(8);
}
}
});
...
}
Once you start scrolling down the list your views get recycled. This means a previously inflated ViewHolder (some that gets created in onCreateViewHolder) is reused.
So what you have to do is to remember the clicked positions (e.g. via a SparseBooleanArray) and check in onBindViewHolder whether the view should be visible (previously clicked) or not.
You can find a basic usage example of the SparseBooleanArray in this StackOverflow post
The 'other' visible items buttons are the ones using the same viewholder that was modified in the callback. So because viewholders (and views) are recycled :
They should only store information that can be retrieved each time the viewholder is bound to a position.
Anything that may be changed in the views state should be refreshed in onBindViewHolder()
In your case you should store the 'is selected' somewhere else and reset the visibility and maxlines in onBindViewHolder() (not only in the callback)
Good idea is to make a class object with all data you need for one item in recycler view, also add there one boolean isItemWasClicked and inside onBindViewHolder() check this boolean and make buttons visible or not.
For example:
public class OneItemOfList{
int priceToDisplay;
String name;
String date;
boolean wasClicked;
}
public class YourAdapter extends RecyclerView.Adapter<OneItemOfList.ViewHolder> {
ArrayList<OneItemOfList> items;
...
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.view.setText(items.get(position).name);
if (items.get(position).wasClicked)
viewHolder.button.setVisible(View.VISIBLE);
else
viewHolder.button.setVisible(View.GONE);
viewHolder.view2.setOnClickListener(...
OnClick(...){
items.get(position).wasClicked = !items.get(position).wasClicked;
});
}
...
}
create an array for example Boolean array, and when each position clicked, set true in same position of array. and in onBindViewHolder check if that array[position] is true set that item visible if.
I am going nuts over this.
I did not find any working solution (tried a few from stackoverflow)
Scenario (this is an actual screenshot what is already done):
I have a Activity that has a View as his Attribute.
This view adds another view via View.addView(myView).
I now want to add a Button to myView (to be specific: after MotionEvent.ACTION_UP the button should appear in the right lower corner (this will start the robot to drive the track))
Here is a shortcut of my code:
public class ModeRouting extends View {
public ModeRouting(Context context) {
super(context);
Button asuroStartButton = new Button(context) //does not work
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int actionevent = event.getAction();
if (actionevent == MotionEvent.ACTION_UP
|| actionevent == MotionEvent.ACTION_CANCEL) {
asuroStartButton.visible=true;
view.add(asuroStartButton);
}
return true;
}
}
and my Activity:
//in constructor
contentView = (FrameLayout) findViewById(R.id.content);
onClickListenerFacade(routingMode, route);
//this removes all views from stack and places the new one on the view
private void onClickListenerFacade(View v, final View target) {
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
contentView.removeAllViews();
contentView.setBackgroundColor(0xff000000);
contentView.addView(target);
modeSelectorAnimation();
}
});
}
I tried to create a button in my mainactivity.xml and instantiate in my mainactivity.
I am missing some point in here but i am not sure which.
Since my view is purely dynamic (no layout.xml) i dont think i should use a layout.xml (maybe thats my mind-blockage) but instead set the button attributes dynamically too.
Any hint is appreciated!
You want to extend ViewGroup rather than just a View (LinearLayout, RelativeLayout, FrameLayout, etc) - they handle child views for you.
I think maybe you need to refresh the whole view/activity. Try to do this in the onResume methode, maybe this helps. But as you don't use a layout.xml, I'm not sure if this helps you much..
#Override
protected void onResume(){
super.onResume();
setContentView(R.layout.activity_main);
}
I am trying to implement a drag and drop in a ListView in android(Ice Cream Sandwich). So when the dragged object reaches the edge of the ListView, I am scrolling the ListView in the relevant direction. The problem is that when we scroll, sometimes the adapter creates new Views as necessary and these 'new' Views did not receive the ACTION_DRAG_STARTED event earlier and hence do not receive the DragEvent updates. Is there any way I can send the events to these views as well?
An easiest way to implement drag and drop in listview is you use this great library.
https://github.com/commonsguy/cwac-touchlist
it's worth trying.
Looking at the source for View, I see:
static final int DRAG_CAN_ACCEPT = 0x00000001;
int mPrivateFlags2;
boolean canAcceptDrag() {
return (mPrivateFlags2 & DRAG_CAN_ACCEPT) != 0;
}
mPrivateFlags2 is package-private and not exposed by the SDK. However, you should be able to change it in a subclass by doing:
try {
Field mPrivateFlags2 = this.getClass().getField("mPrivateFlags2");
int currentValue = mPrivateFlags2.getInt(this);
mPrivateFlags2.setInt(this, currentValue | 0x00000001);
} catch (Exception e) {
}
I have the same problem. I did not solved this recycling problem, but I found a possible workaround still using the Drag & Drop framework. The idea is to change of perspective: instead of using a OnDragListener on each View in the list, it can be used on the ListView directly.
Then the idea is to find on top of which item the finger is while doing the Drag & Drop, and to write the related display code in the ListAdapter of the ListView. The trick is then to find on top of which item view we are, and where the drop is done.
In order to do that, I set as an id to each view created by the adapter its ListView position - with View.setId(), so I can find it later using a combination of ListView.pointToPosition() and ListView.findViewById().
As a drag listener example (which is, I remind you, applied on the ListView), it can be something like that:
// Initalize your ListView
private ListView _myListView = new ListView(getContext());
// Start drag when long click on a ListView item
_myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(null, shadowBuilder, _myListView.getItemAtPosition(position), 0);
return true;
}
});
// Set the adapter and drag listener
_myListView.setOnDragListener(new MyListViewDragListener());
_myListView.setAdapter(new MyViewAdapter(getActivity()));
// Classes used above
private class MyViewAdapter extends ArrayAdapter<Object> {
public MyViewAdapter (Context context, List<TimedElement> objects) {
super(context, 0, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View myView = convertView;
if (myView == null) {
// Instanciate your view
}
// Associates view and position in ListAdapter, needed for drag and drop
myView.setId(position);
return myView;
}
}
private class MyListViewDragListener implements View.OnDragListener {
#Override
public boolean onDrag(View v, DragEvent event) {
final int action = event.getAction();
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DRAG_DROP:
// We drag the item on top of the one which is at itemPosition
int itemPosition = _myListView.pointToPosition((int)event.getX(), (int)event.getY());
// We can even get the view at itemPosition thanks to get/setid
View itemView = _myListView.findViewById(itemPosition );
/* If you try the same thing in ACTION_DRAG_LOCATION, itemView
* is sometimes null; if you need this view, just return if null.
* As the same event is then fired later, only process the event
* when itemView is not null.
* It can be more problematic in ACTION_DRAG_DROP but for now
* I never had itemView null in this event. */
// Handle the drop as you like
return true;
}
}
}
Now if you need to have a visual feedback when doing a drag and drop, there are several strategies. You can for instance have 2 instance variables in your activity named:
private boolean ongoingDrag = false; // To know if we are in a drag&drop state
private int dragPosition = 0; // You put the itemPosition variable here
When doing the drag and drop in MyListViewDragListener you modify these variables, and you use their state in MyViewAdapter. Of course do not forget to update the UI (in the event thread of course, use a Handler) with something like _myListView.getAdapter()).notifyDataSetChanged() or maybe _myListView.invalidate() method.
The problem is because listView.getPositionForView(view) returns -1 if the view is not visible when it is called. So relying on that will fail when you scroll the list. So, instead of setting a view.setOnLongClickListener() you can set a listView.setOnItemLongClickListener() on the list item which calls startDrag() on the item. onItemLongClick() gives you the position which you can pass to in the myLocalState parameter of startDrag(). Then you recover that in onDrag() using event.getLocalState() and casting it to an Integer. Like this...
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
position -= listView.getHeaderViewsCount();
DragShadowBuilder dragShadow = new View.DragShadowBuilder(view);
view.startDrag(null, dragShadow, position, 0);
return true;
}
});
Then in your OnDragListener...
#Override
public boolean onDrag(View eventView, DragEvent event) {
Integer dragViewPos = ((Integer) event.getLocalState());
int eventViewPos = listView.getPositionForView(eventView) - listView.getHeaderViewsCount();
...
}