I have implemented some gesture detection code so that I can detect when a row in my listview (which is situated in a FrameLayout) has been swiped.
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
int itemId = MainActivity.this.getListView().pointToPosition((int) e1.getX(),(int) e1.getY());
Offer order = (Offer)MainActivity.this.getListView().getAdapter().getItem(itemId);
View v = MainActivity.this.getListView().getChildAt(itemId);
}
}
I want to display a view over the top of the swiped row with a set of context sensitive options for that row.
My problems is that the following methods:
v.getTop()
v.getBottom()
return correct values only before the view has been scrolled. I could probably do some calculation to work out offsets using scroll positions etc but I also noticed that I only get values when I swipe a row that is visible on screen to begin with. If I scroll down the list and then swipe a row (that was not originally off-screen) then these methods return null values.
The methods below seem to suffer from the same problem...
v.getLocationOnScreen(loc)
v.getLocationInWindow(loc)
Ultimately, I am looking to find the distance between the top of the visible listView and the row that has been swiped. I will then use this distance to add a view to the parent FrameLayout with the appropriate height padding (so that it covers the swiped row).
Any help would be much appreciated!
android.widget.ArrayAdapter<T> You can use this adapter to provide views for an AdapterView , Returns a view for each object in a collection of data objects you provide, and can be used with list-based user interface widgets such as ListView or Spinner .
There are two different ways of indexing ListView items, by position in the adapter, or by visible place on the screen. An item will always be at the same position within the adapter (unless you alter the list). The index into view.getChildAt() will vary depending on what is visible on the screen.
The position being returned by pointToPosition is the position within the adapter. If you want to get the physical view, you need to account for if the view is scrolled. Try this:
int adapterIndex = MainActivity.this.getListView().pointToPosition((int) e1.getX(),(int) e1.getY());
int firstViewItemIndex = MainActivity.this.getListView().getFirstVisiblePosition();
int viewIndex = adapterIndex - firstViewItemIndex;
View v = MainActivity.this.getListView().getChildAt(viewIndex);
I've solved this but it feels like a bit of a hack and probably isn't the most efficient implementation. Surely there must be a cleaner way to do this using the Android APIs?
In the code below, I have added a HashMap to my ArrayAdaptor to keep track of which on screen views are holding which rows (since these are recycled as you scroll the list)
All changes that I made to my original adaptor have been commented below.
class RestaurantAdapter extends ArrayAdapter<Offer> {
Map hash = null; //HashMap to keep track of rowId and on screen View
RestaurantAdapter() {
super(MainActivity.this, android.R.layout.simple_list_item_1, model);
hash = new HashMap(); //instantiate HashMap in constructor
}
//method to return correct View on screen for row id
public View getViewOnScreen(int rowId) {
return (View)hash.get(rowId);
}
public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
hash.put(position, convertView); // add/update view for row position as it is recycled
RestaurantWrapper wrapper=null;
if (row==null) {
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.row, parent, false);
wrapper=new RestaurantWrapper(row);
row.setTag(wrapper);
}
else {
wrapper=(RestaurantWrapper)row.getTag();
}
wrapper.populateFrom(model.get(position));
return(row);
}
}
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