13

I have a child Activity that contains a ListView. This Activity is populated asynchronously from a SQLite cursor. The list items contain a TextView, a RadioButton, and a normal Button. The XML is shown below:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/rlCategoryListItemLayout" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content">
 <TextView android:id="@+id/tvCategoryListItemTitle" style="@style/uwLargeListItemLabelStyle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:singleLine="true" android:text="This is a test note title" />
 <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentRight="true" android:gravity="center_vertical">
  <RadioButton android:id="@+id/rdoCategoryListItemSelect" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="10dip" />
  <Button android:id="@+id/btnCategoryListItemDelete" android:background="@drawable/delete_red" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" />
 </LinearLayout>
</RelativeLayout>

I have to do some logic to determine which RadioButton is selected by default, and I can't do it until the ListView is already loaded. The problem is that with all the events I have tried so far (onCreate, onPostCreate, onResume, onWindowFocusChanged), the ListView child count is zero. I've also tried using the getView method in the ArrayAdapter class, but that method is called mutliple times and the ListView child count is potentially different every time, leading to unexpected results. Apparently, these events are firing before the ListView has finished being completely populating with its child items.

Is there an event I can listen for, or some other way to determine when the ListView is finished populating and has all of its children accessible to be modified programmatically?

Thank you!

mahdaeng
  • 791
  • 4
  • 15
  • 25

4 Answers4

6

You can use a Handler to accomplish this task, like this:

In your activity add the Handler as any other property.

private Handler mListViewDidLoadHanlder = new Handler(new Handler.Callback() { 
    @Override
    public boolean handleMessage(Message message) {
        //Do whatever you need here the listview is loaded
        return false;
    }
});

And inside the getView method of your listview adapter you do the comparison to see if the current position is the last one , so , it will finish (just put it before the return):

public View getView(int position, View convertView, ViewGroup parent) {
         //Your views logic here

         if (position == mObjects.size() - 1) {
                mViewDidLoadHanlder.sendEmptyMessage(0);
            }

         return convertView;
     }
Renan Grativol
  • 1,062
  • 15
  • 20
  • 4
    The size of your data (mObjects) doesn't correlate to the actual number being displayed in getView. – Johann Nov 27 '14 at 19:59
  • There is no `getView(` in my `ListView.java`; where exactly is that method? – msysmilu Jan 22 '15 at 23:47
  • You are right , this is in your adapter. http://developer.android.com/reference/android/widget/ArrayAdapter.html#getView(int, android.view.View, android.view.ViewGroup) – Renan Grativol Jan 23 '15 at 13:02
  • For future readers, what the comments are trying to say is that this code will only fire the message when the user has scrolled enough that the final item is loaded. In a "proper" adapter with more items than can fit on the screen, this is around the same time that the item becomes visible. – Abandoned Cart Apr 28 '19 at 11:59
  • If you have observed that say there are 4 items that can fit into your screen at a time, then you can check if position == 4 instead of mObjects.size(). This will trigger your method almost immediately any item would get displayed. – Show Young Soyinka May 13 '20 at 16:49
2

At the end of your asynchronous work on the database, you can use Handler.post(Runnable) to run code back on the main thread.

It's almost like triggering a callback that you can guarantee will run after the list is populated. You can schedule the Runnable to occur at a certain time, if you need to time everything very carefully.

I hope this was helpful.

Abandoned Cart
  • 4,512
  • 1
  • 34
  • 41
mtmurdock
  • 12,756
  • 21
  • 65
  • 108
  • Thank you, mtmurdock. I'll try some things there and see what I can come up with. I'll report back on my results. – mahdaeng Oct 28 '10 at 14:31
  • Another option along those lines is to use AsyncTask: http://developer.android.com/reference/android/os/AsyncTask.html – Eric Levine Oct 28 '10 at 20:36
  • As it turned out, I had a subtle bug elsewhere in my code. Apparently, I was firing off my Runnable exactly when I should have been, but the other bug was making it appear that certain controls were not ready yet. Thank you, mtmurdock and elevine, for your willingness to help me out with this. – mahdaeng Oct 28 '10 at 21:59
  • Actually, I was wrong. It seems I fixed the problem when I run the code in the emulator, but when I run it on a device, the problem still exists. – mahdaeng Nov 05 '10 at 01:41
  • What is the bug? if it it unrelated to your first question you should post a new question and accept an answer to this one. If it is related to this problem, please investigate the issue and edit your post to include some of the specific details. As of now it would be difficult to say what your problem is. Look for clues in the stack trace, log cat, debugger, or where ever. More information is always better. Hope we can help! – mtmurdock Nov 05 '10 at 14:10
1

A ListView does not actually create a View for each element in the list of data it is rendering. Instead, it recycles views in order to be responsive and not hog memory. You might rethink what you really need when you say the ListView needs to be populated.

It might help if you go into a little more detail about your app and post some code.

Eric Levine
  • 13,536
  • 5
  • 49
  • 49
  • 1
    Thanks, elevine. I just have to select one of the RadioButtons by default. When I try to do that, though, the ListView doesn't seem to have child items yet. So, I need to know when the ListView is fully populated so I can go in and select one of the list items. – mahdaeng Oct 28 '10 at 14:30
  • My point is what you mean by "ListView is fully populated" is not clear. If by that you mean that all of the items in the list are visible, then that condition might never happen given the way ListView renders rows. – Eric Levine Oct 28 '10 at 20:38
  • Yes - that's exactly what I mean. :^( – mahdaeng Oct 28 '10 at 21:58
0

You can add a string variable in your activity (e.g. public static String END_LOAD) and set the value in your getView method (in the adapter), then in your activity, after

ListView listView = (ListView) findViewById(R.id.recordListView);
        listView.setAdapter(adapter);

You can add a simple thread that will be check the END_LOAD variable:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        while (END_LOAD == null || END_LOAD .equals("") ) { // your conditions
        }
        createReadRequest(); // your task to execute
    }
};

new Thread(runnable).start();

Add a simple condition that can be interesting for you, then when the condition will be false is because the getView method is done, now you can do your specific process.

msysmilu
  • 2,015
  • 23
  • 24
JJAV
  • 1
  • 2