Layout not refreshing after orientation change

2019-08-29 11:11发布

问题:

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).

回答1:

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.



回答2:

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