listview.pointToPosition providing INVALID value when should not - android

I am getting information from my application in production, but when I am trying to reproduce the problem I am not able to do it. What I am looking for is any idea (as I am blocked) of how can I try to reproduce the error.
Basically I am having an activities with a listview, toolbar, edittext and admob advertisement.
My listview is composed of a relativealayout containing a textview and one imageview.
With the adapter I am attaching to the textview an OnLongClickListener that starts a drag operation.
To the Listview itself, I am adding an OnDragListener.
#TargetApi(11)
public class myDragEventListener implements View.OnDragListener {
// This is the method that the system calls when it dispatches a drag event to the
// listener.
public boolean onDrag(View v, DragEvent event) {
// Defines a variable to store the action type for the incoming event
final int action = event.getAction();
// Handles each of the expected events
switch(action) {
case DragEvent.ACTION_DRAG_STARTED:
return true;
case DragEvent.ACTION_DROP:
//We detect which is the item where the drop happened on.
int itemPosition = listShop.pointToPosition((int)event.getX(), (int)event.getY());
// An unknown action type was received.
default:
return true;
}
}
}
So, basically we initiate the drag on the textview from the listview item and the drop ends on the listview itself.
This code works properly, but I received from production reports that sometimes the value of itemPosition to be -1. To avoid an exception I can add a simple check, but I am worried about the user experience.
So, what I would like to avoid is a bad user experience with the application not responding properly, and we know is happening due to reports. The problem is that we are not able to reproduce.
Trying to reproduce this error we tried:
Longclick on textview and drop on imageview. (provides correct itemPosition)
Longclick on textivew and drop outside the listview (drag listener not called)
Longclick on textivew and drop at the edge of listview (works ok).
Does somebody has a suggestion of how this "-1" could be reproduced? Theoretically it should not happen.... is the drop (whatever the drag that has been initiated) activates the OnDragListener, that means that the position where the drop occurs is a position of the listview. How can the DragListener be called in a listview in an incorrect position?
Any idea what could be happening?

Ok, I found the problem. My listview covers almost all the screen, and sometimes is feed with items in the list that are not covering the complete list up to the end. When an item is dragged and dropped in the space where there is no item in the list, provide that value.
The problem is that my testing mobile is very small compared to other devices, and that mobile was always having the screen fully covered by items of the list, so any drag/drop operation was always captured.

Related

How can I prevent a race condition in Android RecyclerView's Adapter?

I have a classic implementation of a recycler view that, when I click on an item inside the recycler view, that item gets deleted.
The problem is that, when I successively click twice one after another (without any noticeable delay between the clicks) on an item in that recycler view, then the second click on that same item is registered at a different position.
The way I identify the item that received the click is by holder.adapterPosition (where holder is an instantiation of ViewHolder class). I wonder if I'm doing wrong by relying on this.
To further troubleshoot, I added the following println statement to troubleshoot:
println("layoutpos ${holder.layoutPosition} adapterpos ${holder.adapterPosition} oldpos ${holder.oldPosition}")
Then, as I repeated those successive clicks, I got the following output in Android Studio's Run tab:
[Galaxy_Nexus_API_22 [emulator-5554]]: I/System.out: layoutpos 1 adapterpos 1 oldpos -1
[Galaxy_Nexus_API_22 [emulator-5554]]: I/System.out: layoutpos 0 adapterpos -1 oldpos -1
Right now, my thoughts are: use adapterPosition, and ignore it when its value is -1 (assume that -1 means a declaration of a racing condition). But I feel that I might be missing something deeper.
How should I handle this situation?
Show the user that the system is refreshing while you're disabling the user from deleting a new object until the previous transaction is completed.
I found two solutions:
if (holder.adapterPosition == -1) return // Race condition; do nothing
// else, do stuff
This does the trick. However, it is not elegant in my view, as: why receive clicking events to begin with if we are not supposed to? It doesn't seem to be solving the problem from its roots.
To solve it more elegantly (from its roots), I did this in the setOnClickListener:
holder.item.setOnClickListener {
// we don't want 2nd click right? so let's delete the listener
holder.item.setOnClickListener{}
/* then, do the stuff for this listener. this stuff
will be done once, as we deleted its listener earlier,
so, subsequent clicks are not possible. */
}
This way, the item with that listener is clicked on once, and a second click does not happen to begin with, hence a racing condition is not possible from its roots. Because the clicking listener is deleted right when the first click is received. Should I want to allow the item to get clicks again, I can redefine a listener for it again.

Android talkback; how to disable announcing cell position and cell count?

When I have a recycler view with 12 items, when accessibility focus goes onto cell number 3, for example, talkback announces "Double tap to activate. Item 3 of 12".
I want to keep the action but stop it from announcing the item position and item count. How can I do this? I have tried to assign a delegate to the recycler view but not sure what to override in the delegate.
How can I do this?
So I figured it out. An AccessibilityNodeInfo has a method called SetCollectionInfo(). CollectionInfo has properties like rowCont and columnCount.
I simply set the info to null.
Note, the below is xamarin:
private class TabLayoutTabAccessibilityDelegate: View.AccessibilityDelegate
{
public override void OnInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info)
{
base.OnInitializeAccessibilityNodeInfo(host, info);
info.SetCollectionInfo(null);
}
}
This is a functionality that should not be disabled: it lets people using Talkback know that they are in a list, how long is the list, which element is currently focused (are there more elements above/below).

Slide In button effect of List View item for Android

I'm porting an iPhone app into android app and one of the difficulties is recreating functionalities that are native to iPhone.
I found a native functionality of iPhone:
When user execute slide touch on a listed item in list view, a delete button appears.
Is there a version for this in android?
Can it be used and reused/customized?
This is just a bit more complicated to achieve. This is what I would do talking from a higher level.
Create a custom ViewGroup/Layout to hold a list item. Inside this layout you have your text lines images or what ever you have and also the delete button. You also here listen for gestures to hide or unhide the delete button.
In your list adapter you are going to need to keep track of which item is showing the delete button and which is not. Also, you are going to need to apply a click listener to each of the list item delete buttons. Every time you assign these states on the list item you should setTag(...) and store the list item position so that when it's clicked you can tell which item number must be deleted.
After deleting you must refresh the list in order for it to take effect. Depending on what type of adapter you are using that's going to determine how you refresh the adapter.
Hopefully this makes some sense. But I definitely think this is the easiest way since I've done this a couple times with similar functionality.
I guess you could either try to implement gesture listener over the listview itself but it might be hard to get the correct id. Since I haven't done it myself I can not answer exactly.
Otherwise you might be able to your own view as the item in the listview and the have a gesture listener on all the childs.
Fling gesture detection on grid layout
For some basic reading and code examples
I dont think there is any built-in API function to do this.
However, a workaround would be to use the onFling function on the view in listitem. You might be able to use this to accomplish what you want.
This is how I realize this effect. We have a ListView lvSimple and we add onTouchListener to our lvSimple. This is my working code.
float historicX = Float.NaN, historicY = Float.NaN;
static final int DELTA = 50;
enum Direction {LEFT, RIGHT;}
...
ListView lvSimple = (ListView) findViewById(R.id.linLayout);
...
lvSimple.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event)
{
// TODO Auto-generated method stub
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
historicX = event.getX();
historicY = event.getY();
break;
case MotionEvent.ACTION_UP:
if (event.getX() - historicX < -DELTA)
{
FunctionDeleteRowWhenSlidingLeft();
return true;
}
else if (event.getX() - historicX > DELTA)
{
FunctionDeleteRowWhenSlidingRight();
return true;
} break;
default: return false;
}
return false;
}
});
where function FunctionDeleteRowWhenSlidingLeft() is calling when when we sliding to the left, FunctionDeleteRowWhenSlidingRight - to the right respectively. In this function you need paste code for animation.
ps. I'm sorry for my bad english. Always glad to help.

listView.setSelection(n); m=listView.getSelectedItemPosition(); //m != n. why?

calling ListView's setSelection() seems to have a problem. a lot of people ask about it. there are answers but none is working. ignoring issues of visual affects. here's a basic scenario that results in unexpected results:
listView.setSelection(5); //listView is a ListView. there are >= 6 items in the list
int sel=listView.getSelectedItemPosition();
you would expect sel==5 but actually it's -1 (which method didn't work?)
so is this a bug and if not, what are the rules that govern the setting and retrieval of the selected item?
If you see in the documentation of setSelection you find this:
Sets the currently selected item. If
in touch mode, the item will not be
selected but it will still be
positioned appropriately. If the
specified selection position is less
than 0, then the item at position 0
will be selected.
That way, it makes perfectly sense that it returns -1. The item is not selected even if you run this method when you're in touch mode, as you most likely are.
I agree with Eric's answer. However, if you still want to make it work, here is a work around.
for your onItemClick part use the following (I had multiple listviews...)
public void onItemClick(AdapterView<?> parentView, View v, int chosenPosition, long
myLong) {
switch(parentView.getId()){
case R.id.Hrlist:
parentView.setSelection(chosenPosition);
break;
case R.id.Minlist:
parentView.setSelection(chosenPosition);
break;
case R.id.Seclist:
parentView.setSelection(chosenPosition);
}
}
Then in your other method you can use something along the lines of:
HrList.getFirstVisiblePosition();
Assuming HrList is defined as
ListView HrList = (ListView) findViewById(R.id.Hrlist);
So yes, in touch mode it doesn't register the item as selected. However, it does move it to a dependable location (i.e. at the top) which you can use to get the value.

Android ListView: mouseover action

I am new to Android application Development.
How can I identify the mouseover action for a list box? On mouse over of a particular cell I want to highlight that cell or change the background color.
Please help me regarding this.
There isn't a concept of MouseOver in Android, at least one that I know of - user interaction is done through hardware/virtual keyboard and touchscreen.
ListView automatically highlights the current selection, so when you use the up and down DPad keys, you may get the effect you want.
I know this is old but my queries kept returning to this original post so I wanted to share it here.
For what it is worth, implementing the state_hovered in your selector will NOT work for list views on mouse over events.
You can achieve this event using custom array adapter.
*This assumes you know how to create your own selector and ArrayAdapter. If not, you can find those in other Stack Overflow posts.
Create your own array adapter.
Implement View.OnHoverListener in the adapter
Override the 'onHover'method:
#Override
public boolean onHover(View arg0, MotionEvent arg1) {
int ev = arg1.getActionMasked();
switch (ev) {
case MotionEvent.ACTION_HOVER_ENTER:
arg0.setHovered(true);
arg0.setSelected(true);
return true;
case MotionEvent.ACTION_HOVER_EXIT:
arg0.setHovered(false);
arg0.setSelected(false);
return true;
}
return false;
}

Categories

Resources