As mentioned here, Android's GridView.scrollTo() doesn't work. The method the solution mentioned, setSelectedPosition, doesn't seem to exist in GridView
smoothScrollToPosition does work, but I really don't want the animation.
For context, I have a CursorAdapter-backed GridView, and I want the view to "reset", i.e. scroll to the top, when I change the cursor.
I've been using setSelection(int position) for this, and it seems to be working just fine. To scroll to the top, just use 0 for position.
From the docs:
If in touch mode, the item will not be selected but it will still be positioned appropriately.
Edit:
Added code to post setSelection as a Runnable:
albumsView.post(new Runnable() {
#Override
public void run() {
albumsView.setSelection(0);
}
});
I have found that in order to reliably use gridView.setSelection(index), I have to first call gridView.setAdapter() (even if the adapter has already been set and has not changed). Like so:
gridView.setAdapter(gridAdapter);
gridView.setSelection(index);
Related
I have a listview with 150+ items, I need to make one visible from code. I currently use smoothscrolltoposition but when the desired item is far away from the current visible item it takes several seconds to arrive.
Is there anyway to simply get rid of the smooth scrolling and simply make the item visible directly?
Thanks,
Ignacio
You can use postdelayed for smooth scroll
listview.postDelayed(new Runnable() {
#Override
public void run() {
// smoothscrolltoposition
}
}, 100);
After several testing and reading the thread suggested by audi , I got this solution:
Strangely, the trick is in reassign the adapter to the listview, not even it is needed to recreate it, just reassign.
listView.Adapter = adapter;
listView.FastScrollEnabled = true;
listView.SetSelection(index);
adapter.NotifyDataSetChanged();
I need to set the firstVisibleItem. right now when I use smoothScrollToPositionFromTop there seems to be some visible scrolling (of course). I am drawing a blank on what the alternative method is. I have used it before, but I just can't seem to find it. Basically instead of smooth scrolling, it "instantaneously" sets the position that I select plus whatever offset I pass it. Does anyone remember which method does this? It's something like
listView.setAsTop(position, offset)
Use AbsListView#setSelection(int position):
new Handler().post(new Runnable() {
#Override
public void run() {
listView.setSelection(position);
}
});
Make sure listView and position are member variables or declared as final.
The problem:
(1) add a touch listener to rows in a listview so that on swipe.
(2) swipe animation plays
(3) rows get deleted in the backend, and
(4) the animation plays without any flicker or jerkiness. By "flicker" I mean that the deleted row briefly shows after the animation finished.
I suspect that something funky was happening with the animation listener, so I ended up doing the following (done in the order given):
Do the animation and make it persist by setting setFillAfter and setFillenabled to true
Make the view invisible when the animation ends
Delete the row in the database
Reset the animation
Reload the listview
Make the view visible (but wait an additional 300 ms)
The result deletes the row without jerkiness or flicker BUT it now feels sluggish because of extra 300 ms wait. (I'm also not sure if this delay works across all devices.)
Update: I should point out that the 300 ms delay is what makes it work. That's weird because by that point the animation was reset and the listview has the latest data. There should be no reason why making the view visible makes the old row briefly show, right?
I also tried using a ViewPropertyAnimator (as per Using animation on a ViewPager and setFillAfter) but for some reason the onAnimationEnd listener was called at every step of the animation.
I also read that we should implement a custom view and override its onAnimationEnd listener. (However, I haven't tried that approach yet.)
Update: just tried to add an extra dummy animation at the end (as per Android Animation Flicker). However, that doesn't work
My test phone runs Ice Cream Sandwich. My app is targeting Gingerbread and after.
So what's the proper solution? Am I doing this the wrong way?
Here's the code:
#Override
public boolean onTouch(final View view, MotionEvent event)
{
//...
switch(action) {
//...
case MotionEvent.ACTION_MOVE:
// ...
if (//check for fling
{
view.clearAnimation();
//animation = standard translate animation
animation.setAnimationListener(new AnimationListener() {
//
#Override
public void onAnimationEnd(Animation animation)
{
view.setVisibility(View.INVISIBLE);
flingListener.onFling(cursorPosition, view, velocity);
}
//...
});
view.startAnimation(animation);
}
break;
//
}
The "fling listener":
#Override
public void onFling(int position, final View view, float velocity)
{
//delete row -- its actually a Loader
//the following code runs in the Loader's onLoadFinished
view.clearAnimation();
adapter.notifyDataSetChanged();
adapter.swapCursor(null);
//reload listview -- it's actually a Loader
//the following code runs in the Loader's onLoadFinished
adapter.swapCursor(cursor);
view.postDelayed(new Runnable() {
#Override
public void run()
{
view.setVisibility(View.VISIBLE);
}
}, 300);
}
Update: After comparing Chet Haase's code, we are doing similar things with some important differences: (1) he uses a onPreDraw listener with the ListView tree observer to do the actual deletion, (2) he removes the row not only from the array but also from the listview. After mimicking his code, it still didn't work. The problem is now the Loader---I use a Loader to delete rows asynchronously. The Loader seems to force an additional draw call to the ListView...before the row has been deleted in the backend. And that's (another) cause of the flicker. Still haven't figured out a workaround though.
Chet Haase (Google Engineer) put together a really good DevBytes on this topic I'd suggest watching/taking ideas from his source. If you use NineOldAndroids, I think this'll be backwards compatible to GB.
Check it out here:
http://graphics-geek.blogspot.com/2013/06/devbytes-animating-listview-deletion.html
As I pointed out in the comments, the problem with Chet's code is that its designed for synchronous data access. Once you start asynchronously deleting rows, his code fails.
I found the solution to the flicker problem by combining Chet's code with this answer: CursorAdapter backed ListView delete animation "flickers" on delete
The solution to correctly do a row deletion aynchronously is:
Create a onPreDraw listener for the ListView tree observer to prevent flicker. All code in this listener runs before the list view re-draws itself, preventing flicker.
Find a way to delete the row in the listview (but not yet in the database). There are two approaches (see CursorAdapter backed ListView delete animation "flickers" on delete):
Create a AbstractCursor wrapper that ignores the row to be deleted and swap it in for the real cursor. OR
Mark the row to be deleted as "stained" and act on it appropriately when redrawing the row.
Remove the row for real in the database (asynchronously).
Some pseudo-code using the AbstractCursor wrapper (it's technically called a "proxy"):
//Called when you swipe a row to delete
#Override
public void onFling(final int positionToRemove, final View view)
{
final ViewTreeObserver observer = listView.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
observer.removeOnPreDrawListener(this);
//remove the row from the matrix cursor
CursorProxy newCursor = new CursorProxy(cursor,
positionToRemove);
swapCursor(newCursor);
//delete row in database
}
}
}
I have a ListView that potentially can have hundreds of entries. When a selection is made I've been using a smoothScrollToPosition, thusly:
if (lv != null) { //Are we created yet?
lv.post(new Runnable() {
public void run() {
lv.smoothScrollToPosition(k);
}
});
}
but my users have told me they don't like the scrolling animation and would prefer to just instantly go there. So I replaced my smooth scroll with
lv.setSelection(k);
... and now it does nothing at all. FWIW this is all happening right after a notifyDatasetChanged
In searching for a solution I came across this discussion on http://code.google.com/p/android/issues/detail?id=6741 which implies this is a known problem. Is there a workaround or am I just doing this wrong?
Thanks in advance.
The documentation of setSelection says that it only scrolls to the selected position when the ListView is in touch mode. Maybe ListView is no more in touch mode once the data set has changed or maybe setSelection is simply forgotten for the next UI update cycle.
I guess you could try a workaround by calling setSelection with a delay. You could use the postDelayed method with a delay of 100 milliseconds for example. Or you could extend ListView and override layoutChildren or something related that probably gets called when the data set changes in order to re-calculate the list view item measurements. At that point it should be safe to call setSelection and you don't need to rely on guesstimating a delay.
I have a view (custom drawn) added with getWindowManager().addView() and later I'm modifiying the LayoutParameters of it (changing x & width) and call getWindowManager().updateViewLayout(). This works but I am getting two screen refreshes, first one only moves the whole thing according to the new x and later one scales it according to the new width. Any ideas about why is this happening even though I only call updateViewLayout just one time with the new layout parameters?
FYI: onDraw method of the custom drawn view mentioned here is also called only one time by the system during this process.
Try:
runOnUiThread(new Runnable() {
public void run() {
view.updateViewLayout();
}
});
http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)
If it doesn't work, check this:
How to move a view in Android?
Are you doing this?
try to do :
view.post(new Runnable() {
public void run() {
view.updateViewLayout();
}
});
updateViewLayout is an a method that can be overriden by your custom ViewGroup and in this overrided method you can implement all what your want to change.
Maybe you do something wrong in it?
Or also maybe you have to implement this code in UiThread like in other questions. - In this case when you change your parameters asynchronously with first call of drawing function by system you method maybe can change only one parameter and on second call the second parameter will be also changed.