I'm using a cursor loader with the RecyclerView, and everything else works just fine except every time I remove an item from the RecyclerView, the last item blinks, like this
last item blinks
my code for deletion is
public void deleteData(long id){
Uri uri = ContentUris.withAppendedId(URI, id);
getContentResolver().delete(uri, null, null);
}
code on swipe:
deleteData(viewHolder.getItemId());
note that I have another activity that's using the same RecyclerAdapter, ContentProvider and layout code, even most of the implementations and method approaches are the same, but that one works perfectly without any blinks, so it is a pretty weird situation for me.
Is there a specific reason that can cause this problem? I already tried disabling the animation like
recyclerView.getItemAnimator().setChangeDuration(0);
or
((DefaultItemAnimator) recyclerViewObject.getItemAnimator()).setSupportsChangeAnimations(false);
But none of that worked, not to mention that I do want the animations to work.
Edit: Break points upload:
onCreate:
"main#4668" prio=5 tid=0x2 nid=NA runnable
java.lang.Thread.State: RUNNABLE at com.jackz314.todo.HistoryActivity.onCreateLoader(HistoryActivity.java:803)
at
android.support.v4.app.LoaderManagerImpl.createLoader(LoaderManager.java:539)
at
android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManager.java:548) at
android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:603)
at
com.jackz314.todo.HistoryActivity.displayAllNotes(HistoryActivity.java:394)
at
com.jackz314.todo.HistoryActivity.deleteExpiredNotes(HistoryActivity.java:767)
at
com.jackz314.todo.HistoryActivity.onCreate(HistoryActivity.java:129)
at android.app.Activity.performCreate(Activity.java:6975) at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(ActivityThread.java:-1) at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105) at
android.os.Looper.loop(Looper.java:164) at
android.app.ActivityThread.main(ActivityThread.java:6541) at
java.lang.reflect.Method.invoke(Method.java:-1) at
com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
onDelete(remove item):
"main#4668" prio=5 tid=0x2 nid=NA runnable java.lang.Thread.State:
RUNNABLE at
com.jackz314.todo.HistoryActivity.deleteData(HistoryActivity.java:791)
at
com.jackz314.todo.HistoryActivity$4.onSwiped(HistoryActivity.java:434)
at
android.support.v7.widget.helper.ItemTouchHelper$4.run(ItemTouchHelper.java:686)
at android.os.Handler.handleCallback(Handler.java:789) at
android.os.Handler.dispatchMessage(Handler.java:98) at
android.os.Looper.loop(Looper.java:164) at
android.app.ActivityThread.main(ActivityThread.java:6541) at
java.lang.reflect.Method.invoke(Method.java:-1) at
com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Hope those helps
Edit: After a little investigation, I found out that the last item didn't actually blink, it seems that the RecyclerView first changed it's length/size, cuts off a section that has the equal size as the deleted item(deleting), so that the last item(depending on the size of the deleted item) is not visible for a while, and then the RecyclerView deletes the item that's supposed to be deleted at the beginning. I'm still working on it, trying to figure out whether it's because I messed up the order somewhere or it's some other reason.
Finally got it!
Yet another stupid mistake, it's not complicated at all, I kept thinking, why I'm having this problem only on this activity, but not my previous one, and I suddenly realized that the layout in the two activities are different, turns out I set the RecyclerView's height to wrap_content, so I suppose what happened is that, the layout told the system to wrap the content in the RecyclerView, and when you delete an item, it's technically not there anymore, so, even though I have animations for the deleted item, the item is removed immediately from the layout's level, that's why it seems like Android adjusted the RecyclerView's height first, and then delete the item, what actually happened is, since RecyclerView is set to wrap_content, it removes the position of the view(item) immediately after delete, not after the animation, and that's why it looks like a blink.
Anyways, the solution is just simply to change the RecyclerView's height from wrap_content to any other value, if you are using ConstraintLayout like I did, just change the height to 0dp, then constraint the RecyclerView to something else, or the edge.
Thanks to everyone who tried to help me solve my problem though, I admit this is one of the stupidest yet frustrating problems I ever had.
I believe there're others who also encountered the same problem, so just go and check if the height is set to wrap_content.
I'm stuck with the same thing as you. I believe the problem is that CursorLoader restarts every time we delete something from the database, so onLoadFinished is triggering every time and swapping cursors.
The problem can be "solved" if we load our data from the cursor into the List and pass that to the adapter. Still, i find this as a workaround, not a real solution.
Let place the break point at the data object, which bind with the RecyclerViewAdapter and check if the data is blinked or not. In the most of case like this situation, I realize the data maybe is effected and make view change uncontrollable. Because I don't know how you handle the data, so I can't give more detail answers for you.
Have you tried that, to disable animation?
adapter.setHasStableIds(true);
recyclerView.setItemAnimator(null);
Jackz this code below is in the DetailsActivity that has a btnDelete it fires and goes over to DBHelper and deletes the record and returns to DetailsActivity then makes a call to ListActivity to remove the item from the list based on position NOT record ID
private void addListenerOnButtonDelete() {
btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Calls the Method deleteDBRow in DatabaseHelper
// which acts on the TABLE_INFO to remove a record by getting the record ID
helper.deleteDBRow(String.valueOf(dbList.get(position).getID()));
ListActivity.removeListRow(position);
// Code line above calls Method in ListActivity to notify recycler view of changes
// NOTICE the List keeps items by position not ID <== READ
etWebSite.setText("");
etUN.setText("");
etPW.setText("");
etSecQuestion.setText("");
etSecAnswer.setText("");
etNotes.setText("");
Intent intent = new Intent(DetailsActivity.this, ListActivity.class);
startActivity(intent);
}
});
}
Here is the code that lives in the ListActivity that talks to the Adapter
// This method is called from DetailsActivity and notifies Recycler View that the DB was changed
// and the method makes the same changes to the Recycler View kind of a sync of DB and Recycler View
public static void removeListRow(int position) {
dbList.remove(position);
mAdapter.notifyItemRemoved(position);
mAdapter.notifyItemRangeChanged(position, dbList.size());
}
I exactly have the same issue and it makes me crazy.
Your solution does not work in my case, probably because my RecyclerViewer is inside a LinearLayout?
The only workaround I found so far is to disable animation like that
cardRecyclerView.itemAnimator = null
But I really want to re-enable animation since without it is a little bit choppy.
Here is my layout, has someone any idea to avoid the last item blinking when a card is removed? thank you in advance
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="wrap_content">
<fragment
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bendk97.platenumber.activities.main.Toolbar"/>
<fragment
android:id="#+id/search_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bendk97.platenumber.activities.main.SearchBar"/>
<ProgressBar
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminateTint="#android:color/white"
android:id="#+id/progressBar" android:visibility="invisible"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Related
Hi I'm implementing click listeners in the following way but after some time the methods and variables inside the listener's closure get the wrong values or something. Let me explain the implementation of the listener a little better a for loop creates the listener for a set of image views then later in the program the for loop is called a second time and it resets the listener methods and variables to different values. Everything works great for about 30 minutes but then for some reason, the listener's methods and variables start having the wrong values. Has anybody ever heard of this behavior or can tell me where I've gone wrong with the code? Keep in mind that the listener I'm about to paste here is just a small piece of a 1014 line class. I'm hoping somebody can spot How I'm implementing the listener wrongly and can give me some advice on how to "reset" the listener so that it's variables and values stay over time. Hopefully you can read the code without putting it in an editor but feel free to copy it for readability's sake Here is the code for the image view listener with comments.
//image views are held in an array
//set an image view in its imageview container
imgArr0[slotId1].invalidate()
imgArr0[slotId1].setImageDrawable(null)
//drw is not defined in this example
imgArr0[slotId1].setImageDrawable(drw)
/*if video or image id is set to image then set a listener for the image
*/
/*slotId1 is not defined in this example but it is simply a counter to iterate over the ImageView array
*/
if (videoOrImageId0[slotId1] == "image") {
//null any listeners that might be attached to the image view
imgArr0[slotId1].setOnClickListener(null)
//set or reset the listener
imgArr0[slotId1].setOnClickListener() {
`enter code here`//if the current config is portrait then set a new image image
if (currentConfig0 == "portrait0") {
act0.lrgImage0.invalidate()
act0.lrgImage0.setImageDrawable(null)
/*drw is not defined in this example but works fine in the production script
*/
act0.lrgImage0.setImageDrawable(drw)
}
--calmchess
ccc tv application with problem.
(https://i.stack.imgur.com/PjdbN.jpg)![enter image description here](https://i.stack.imgur.com/FaMnc.
I was able to partially solve this question by destroying all the image views and their associated click listeners then rebuilding those... However I don't consider this issue completely solved so if anybody can provide a better solution I'd love to hear it because rebuilding the images every few minutes has to be using a lot of unnecessary hardware resources.
--calmchess
My outer RecyclerView crashes either with
IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true...
or
IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
Like the title suggests I have an RecyclerView in the list item layout of the first RecyclerView. This layout is used to display messages and the
inner RecyclerView to display attachments that come with the message. The inner RecyclerViews visibility is set to either GONE or VISIBLE depending whether the message has any attachments or not. The simplified outer list item layout looks like this
ConstraintLayout
TextView
TextView
TextView
RecyclerView
And the part of the adapter that handles the inner RecyclerView looks like this
private fun bindFiles(message: Message?) = with(itemView) {
if (message != null && message.attachments.isNotEmpty())
{
sent_message_attachments.setAsVisible()
sent_message_attachments.layoutManager = GridLayoutManager(this.context,Math.min(message.attachments.size,3))
sent_message_attachments.adapter = AttachmentAdapter(message.attachments)
sent_message_attachments.itemAnimator = null
sent_message_attachments.setHasFixedSize(true)
}
else{
sent_message_attachments.setAsGone()
sent_message_attachments.adapter = null
sent_message_attachments.layoutManager = null
}
}
The bug has something to do with the way I fetch the attachments in the inner adapter since once I disable the part that start the download process, everything is fine. There's no problem when loading images from the device, but once I start the download process, everything goes to hell. This is the part that handles images and kicks off the download process in the inner adapter. I have functions for videos and for other file types that are pretty much the same exact thing but use slightly different layout.
private fun bindImage(item: HFile?) = with(itemView) {
if (item != null)
{
if (item.isOnDevice && !item.path.isNullOrEmpty())
{
if (item.isGif)
{
attachment_image.displayGif(File(item.path))
}
else
{
attachment_image.displayImage(File(item.path))
}
}
else
{
//TODO: Add option to load images manually
FileHandler(item.id).downloadFileAsObservable(false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ progress ->
//TODO: Show download process
},
{ error ->
error.printStackTrace()
//TODO: Enable manual retry
},
{ notifyItemChanged(adapterPosition)} //onComplete
)
}
}
}
I use the same structure as above in my DiscussionListAdapter to load discussion portraits (profile pictures etc.) and it does not have the same issue.
These are the extensions functions used to inflate the viewHolders and to display the images
fun ViewGroup.inflate(layoutRes: Int): View
{
return LayoutInflater.from(context).inflate(layoutRes, this, false)
}
fun ImageView.displayGif(file:File){
GlideApp.with(context).asGif().load(file).transforms(CenterCrop(), RoundedCorners(30)).into(this)
}
fun ImageView.displayImage(file:File){
GlideApp.with(context).load(file).transforms(CenterCrop(), RoundedCorners(30)).into(this)
}
I've been on this for the past couple of days and just can't get my head around it. Any help in any direction is greatly appreciated. I know my explanations can be a bit all over the place so just ask for clarification when needed :)
UPDATE
I have now been able to produce this with a GridLayout as well as with RecyclerView. It's safe to assume that the nested RecyclerViews were not the culprit here. I even tried to ditch the Rx-piece that handled loading the images and created an IntentService for the process, but the same crashes still occur.
With GridLayout I mean that instead of having another adapter to populate the nested RecyclerView I use only one adapter to populate the message and to inflate and populate views for the attachments as well and to attach those views to the nested GridLayout.
The crash happens when I start to download a file and then scroll the view, that is supposed to show the downloaded file, out of the screen. That view should get recycled but for some reason the download process (which in my test cases only takes around 100ms-400ms) causes the app to throw one of the two errors mentioned in the original question. It might be worth noting that I'm using Realm and the adapter takes in a RealmResults<Message> list as it's dataset. My presenter looks for changes in the list and then notifies the adapter when needed (changed due to the implementation of IntentService).
This is how I'm capable to reproduce this time and time again:
Open a discussion that has messages with attachments
Start to scroll upwards for more messages
Pass a message with an attachment and scroll it off screen while it's still loading
Crash
There is no crash if I stop and wait for the download to complete and everything works as intended. The image/video/file gets updated with a proper thumbnail and the app wont crash if I scroll that out of view.
UPDATE 2
I tried swapping the nested ViewGroup for a single ImageView just to see is the problem within the nestedness. Lo and behold! It still crashes. Now I'm really confused, since the DiscussionListAdapter I mentioned before has the same exact thing in it and that one works like a charm... My search continues. I hope someone, some day will benefit from my agony.
UPDATE 3
I started to log the parent of every ViewHolder in the onBindViewHolder() function. Like expected I got nulls after nulls after nulls, before the app crashed and spew this out.
04-26 21:54:50.718 27075-27075/com.hailer.hailer.dev D/MsgAdapter: Parent of ViewHolder: android.view.ViewOverlay$OverlayViewGroup{82a9fbc V.E...... .......D 0,0-1440,2168}
There's a method to my madness after all! But this just poses more questions. Why is ViewOverlay used here? As a part of RecyclerView or as a part of the dark magicians plans to deprive me of my sanity?
Sidenote
I went digging into RecyclerViews code to check if I could find a reason for the ViewOverlaymystery. I found out that RecyclerView calls the adapters onCreateViewHolder() function only twice. Both times providing itself as the parent argument for the function. So no luck there... What the hell can cause the item view to have the ViewOverlay as it's parent? The parent is an immutable value, so the only way for the ViewOverlay to be set as the parent, is for something to construct a new ViewHolder and supply the ViewOverlay as the parent object.
UPDATE 4
Sometimes I amaze myself with my own stupidity. The ViewOverlay is used because the items are being animated. I didn't even consider this to be an option since I've set the itemAnimator for the RecyclerView as null, but for some odd reason that does not work. The items are still being animated and that is causing this whole charade. So what could be the cause of this? (How I chose to ignore the moving items, I do not know, but the animations became very clear when I forced the app to download same picture over and over again and the whole list went haywire.)
My DiscussionInstanceFragment contains the RecyclerView in question and a nested ConstraintLayout that in turn contains an EditText for user input and a send button.
val v = inflater.inflate(R.layout.fragment_discussion_instance, container, false)
val lm = LinearLayoutManager(context)
lm.reverseLayout = true
v.disc_instance_messages_list.layoutManager = lm
v.disc_instance_messages_list.itemAnimator = null
v.disc_instance_messages_list.adapter = mPresenter.messageAdapter
This is the piece that handles the initialization of the RecyclerView. I'm most definitely setting the itemAnimator as null, but the animations just wont stop! I've tried setting the animateLayoutChanges xml attribute on the root ConstraintLayout and on the RecyclerView but neither of them worked. It's worth mentioning that I also checked whether the RecyclerView had an itemAnimator in different states of the program, and every time I check the animator, it is null. So what is animating my RecyclerView?!
I have faced the same issue
Try this in your child RecyclerView it works for me
RecyclerView childRC = itemView.findViewById(R.id.cmol_childRC);
layoutManager = new LinearLayoutManager(context);
childRC.setItemAnimator(null);
childRC.setLayoutManager(layoutManager);
childRC.setNestedScrollingEnabled(false);
childRC.setHasFixedSize(true);
now set your Adapter like this
ArrayList<Model> childArryList = new ArrayList<>();
childArryList.addAll(arrayList.get(position).getArrayList());
ChildOrderAdapter adapter = new ChildOrderAdapter(context, childArryList);
holder.childRC.swapAdapter(adapter, true);
hope this helps
I finally figured out what was causing this. In my DiscussionInstanceView I have a small view that is animated into and out of view with ConstraintLayout keyframe animations. This view only shows the download progress of the chat history and is used only once, when the discussion is first opened. BUT since I had a call to hiding that view every time my dataset got updated, I was forcing the ConstraintLayout to fire of an animation sequence thus making everything animate during the dataset update. I just added a simple check whether I was downloading the history or not and this problem got fixed.
So I recently started messing around with Espresso in one of my existing Android projects.
Everything went pretty decently, until I came to find AutoCompleteTextView in my program. I don't seem to understand how to properly click the first thing in the autocomplete list. I'm actually not even sure which to use, onView() or onData() in this instance.
By some reasons which I don't know, AStupidNoob's solution doesn't work. So I found another one:
onView(withText("Spinner Item"))
.inRoot(RootMatchers.isPlatformPopup())
.perform(click());
The AutoCompleteTextView itself
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/textInputLayout2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<AutoCompleteTextView
android:id="#+id/product"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:hint="#string/product"
android:singleLine="true"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
I think I found a bit of a cleaner method than the accepted answer!
onData(equalTo("ITEM")).inRoot(RootMatchers.isPlatformPopup()).perform(click());
The breakdown:
onData(x) This will find the view rendering the data object matching x in the drop down. The data is provided by the Adaptor given to the AutoCompleteTextView, so it can be an object of any type that Adaptor provides, it probably won't be a View. You'll want to use the standard hamcrest core matchers for this (equalTo, instanceOf, etc...) rather than (withText, withId, etc...). It might be a pain to try and find what object this is and how to match it, but there isn't a neater way: with a lot of items in your adapter some of the views won't even be in the hierarchy yet, so onView can't work! onData will make sure to load the views that match your data. Checkout here (this what onData returns) and here (this loads the matching data)
inRoot(RootMatchers.isPlatformPopup()) So it turns out the dropdown menu is on another window than the default window your activity runs in. So we have to specify that we want to search that window. The accepted answer uses RootMatchers.withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView()))) which seems to match any window that is not the default one.
Anyways HTH someone else.
For anyone still running into this issue, despite trying out the accepted solution above, I managed to get it working with help from a github issue I uncovered. For reference, I am using Robolectric 4.6, which I believe may be the reason why I am requiring a different solution from non-instrumented tests.
The solution I came up with (to verify an item is appearing in an AutoCompleteTextView popup is:
fun testAutoCompleteTextViewEntry() {
onView(withId(R.id.editText_search))
.perform(typeTextIntoFocusedView("x"), showDropDown())
onView(withText("xyz"))
.inRoot(RootMatchers.isPlatformPopup())
.check(matches(isDisplayed()))
}
// Somewhere else in your code
fun showDropDown(): ViewAction =
object : ViewAction {
override fun getDescription(): String = "Shows the dropdown menu of an AutoCompleteTextView"
override fun getConstraints(): Matcher<View> = allOf(
isEnabled(), isAssignableFrom(AutoCompleteTextView::class.java)
)
override fun perform(uiController: UiController, view: View) {
val autoCompleteTextView = view as AutoCompleteTextView
autoCompleteTextView.showDropDown()
uiController.loopMainThreadUntilIdle()
}
}
So i finally figured it out, thanks to this previous question:
Testing autocomplete textview using espresso tool
Ill just post my version of it for people who might use it in future.
onData(instanceOf("Whatever your arrayadapter contains".class)).inRoot(RootMatchers.withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView())))).perform(ViewActions.click());
Following on from the answer from AStupidNoob on 28 July 2017...
To click on a specific row number of the drop-down list, you can use this:
onData(anything())
.atPosition(2)
.inRoot(RootMatchers.isPlatformPopup())
.perform(click());
To click on a specific item in a specific row number of the drop-down list, you can use this:
onData(anything())
.atPosition(2)
.inRoot(RootMatchers.isPlatformPopup())
.onChildView(withId(R.id.button_on_layout))
.perform(click());
Since the place predictions are inside a recyclerview, after typing the name of the placement, if you want to click on the first option in the predicted list, you can go with one of the RecyclerViewActions methods (actionOnItemAtPosition).
The key is to find out the id of the autocompleted places. It is set up by Google Place SDK but not yourself so might not be that straightforward to find out.
This works for my project, see if it can help:
onView(ViewMatchers.withId(com.google.android.libraries.places.R.id.places_autocomplete_list)).perform(RecyclerViewActions.actionOnItemAtPosition(0,click()));
Question 1
I was looking at the example code on this page that uses SortedList with RecyclerView.
At line 127, after the CheckBox status changed, recalculatePositionOfItemAt() method was used. The javadocs for SortedList<T> says that recalculatePositionOfItemAt() is for adjusting item positions without triggering onChanged() callback. And updateItemAt() will call onChanged() and/or onMoved() if necessary.
In the case of the example code, the item's field boolean mIsDone changed. I thought updateItemAt() would be more appropriate here?
Question 2 (related)
I tried to play around to use updateItemAt() with a sorted list, but some times java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling was triggered and I found lines 122-125, 152-154 in the example code helps to avoid the exception. I thought the checkbox checked status changing event can only happen when the user checks/unchecks the checkbox. Why are these lines necessary? Some times random tapping or scrolling events far away from the checkboxes can trigger the event?
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.