0

I have the following Activity definition:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/inspectionMainLayout"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:singleLine="false"
        android:id="@+id/breadCrumb"
        android:visibility="gone">
    </LinearLayout>

    <ExpandableListView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/expandableListView" />

</LinearLayout>

Now in my code I do add buttons dynamically in breadCrumb LinearLayout:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_inspection);
    LinearLayout mainLayout = (LinearLayout) findViewById(R.id.inspectionMainLayout);

    if (mainLayout != null) {
        ExpandableListView list = (ExpandableListView) findViewById(R.id.expandableListView);

        list.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i2, long l) {
                LinearLayout breadCrumb = (LinearLayout) findViewById(R.id.breadCrumb);

                Button filterButton = new Button(InspectionActivity.this);
                filterButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        onFilterButtonClick((Button) view);
                    }
                });

                filterButton.setText(item.getFormattedFilter());

                breadCrumb.addView(filterButton);
            }
        }
    }       
    ...
}

This code works well, until I do not change the device orientation and my Activity is recreated. Although all the code is executing correctly, screen seems not being updated. Once I restore the previous orientation, all the items suddenly appear. Any idea why and how to fix it?

Thanks

EDIT:

I do think that I'm running into the same problem as describe in this post: Android: findViewById gives me wrong pointer?

Any idea on how to solve this?

As requested my onRestoreInstanceState:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    baseCategories = savedInstanceState.getParcelable(BASE_CATEGORIES_STATE);
    currentFilter = savedInstanceState.getParcelable(FILTERS_STATE);
}

and on onSaveInstanceState:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    outState.putParcelable(BASE_CATEGORIES_STATE, baseCategories);
    outState.putParcelable(FILTERS_STATE, currentFilter);
}

now both of my classes do implement Parcelable interface. They are persisted and restored correctly.

Still for some resaon the call to the findViewById get's me pointed to the wrong object (not the one that is recreated).

Community
  • 1
  • 1
MaiOM
  • 916
  • 2
  • 13
  • 28
  • Can you post the screenshot so that we can get what changes are you getting, perhaps it has something to do with `android:layout_height="50dp"`, because as orientation changes the height also changes – Girish Nair Jun 12 '14 at 08:59
  • I can't post the screenshot as it contains sensitive data. Each time the activity is recreated, it fails to update the UI. At example, after he recreates the activity, it's showing correctly, but if I set at example add a new button inside the breadcrumb, it will not show on screen, and still if I verify the breadcrumb object, the newly added button is there. I tried to invalidate the breadcrumb view but still nothing. Any idea? – MaiOM Jun 12 '14 at 09:06
  • So you want to refresh, or not refresh? – jyoonPro Jun 12 '14 at 09:34
  • I would like to refresh it somehow. So that it reflects the views that are inside. – MaiOM Jun 12 '14 at 09:54

2 Answers2

0

You add views dynamically (on user click event).
By default, android does not "remember" to keep these dynamic views when re-creating the activity on configuration changes, you have to handle this process yourself.

Some possibilities :

  • Avoid recreating activity on screen rotation by declaring android:configChanges="keyboardHidden|orientation|screenSize" for your activity in AndroidManifest.xml - This is highly not recommended

  • "Remember" what views were dynamically added when re-creating activity after rotation (for example using extra flags to detect that new filter button was added and pass it via bundle in onSaveInstanceState, and check in onCreate whether you need to re-create the button), or retain the whole view object as explained here

One extra note : you perhaps want to specify "vertical" orientation for your breadCrumb layout, it is horizontal by default.

kiruwka
  • 9,250
  • 4
  • 30
  • 41
  • Hi, I am persisting some of the data already on onSaveInstanceState method and restoring them onRestoreInstanceState. After the data is restored, in onResume method i do "rebind" my breadcrumb object, by adding the buttons that where previously there. Once this is done, if I try to add a new button to that view, it just doesn't show. – MaiOM Jun 12 '14 at 09:53
  • What I discovered till now is that somehow a reference to the previous instance is persisted. What I do mean with that is that if I call the LinearLayout breadCrumb = (LinearLayout) findViewById(R.id.breadCrumb); for the first time, my var is pointing to X. Then the class is recreated because of the orientation change. My new call on findViewById(R.id.breadCrumb) is poininting to a different object. However, once the adding is triggered and I do call again findViewById(R.id.breadCrumb) it retrieves the previous instance address. Is this plausible? Am I saying something stupid? – MaiOM Jun 12 '14 at 10:16
  • can you post the code from onSave/onRestoreInstanceState please ? – kiruwka Jun 12 '14 at 10:22
  • Well, you save/restore the data, but I don't see where you recreate the Button object. When your activity is recreated, its whole view hierarchy is destroyed and then you re-inflate it again from xml layout. If you want to see your dynamic button, you have to create it again, based on, for example, a saved flag that indicates that the button was there before the rotation. – kiruwka Jun 12 '14 at 11:08
  • I found out the reason. check my answer. It seems that if your class implements Parcelable, he still implicitly persists also the private variables that are not indicated in writeToParcel override. This was happening for my private ArrayList listeners; once restored it was containg the pointer to the previous instance of view. For to me unknown reason that instance was still valid and returning a valid object. – MaiOM Jun 12 '14 at 11:21
0

I found out why this is happening.

onSave/onRestoreInstanceState I was persisting the currentFilter class which has some custom listeners on it.

As onResume method I was doing the following:

@Override
protected void onResume() {
    if (currentFilter == null) {
            currentFilter = new FilterItemList();

            currentFilter.addListener(new FilterItemListListener() {
                @Override
                public void filterChanged(FilterChangedEvent e) {
                    filterCategories(categoryRepository);
                }

                @Override
                public void filterAdded(FilterAddedEvent e) {
                    FilterItem addedItem = e.getAddedItem();
                    baseCategories.remove(new BaseCategory("", addedItem.getSectionName()));
                }

                @Override
                public void filterRemoved(FilterRemovedEvent e) {
                    FilterItem removedItem = e.getRemovedItem();
                    baseCategories.add(new BaseCategory("", removedItem.getSectionName()));
                }
            });
    }
}

The pointer to the previous instance was persisted. That's why my interface was not behaving correctly.

Now I do re-register listeners even when currentFilter is not null (it is restored) so they can point to the right instance.

Is there any pattern in handling this situations?

Thanks

MaiOM
  • 916
  • 2
  • 13
  • 28
  • Apparently the Android Bundle class does not adhere to the parcelable protocol that instead is followed during IPC marshalling. http://stackoverflow.com/questions/4853952/android-parcelable-writetoparcel-and-parcelable-creator-createfromparcel-are-ne – MaiOM Jun 19 '14 at 06:52