0

View is not using automatically generated Bundle information.

Searching Stack Overflow yielded no results about it. View is not custom, has unique name id. Is not a duplicated of:

findViewById not working for an include?

FindViewById() not finding View

Android: findviewbyid: finding view by id when view is not on the same layout invoked by setContentView

Other View(s) are working as expected. The only one that is not is added as an include, that is the only difference I could find.

Problem description: View is hidden, Activity is recreated, View is shown.

Details: I have a Button, that uses the default android:onClickatribute, that invokes a method. This method is working as expected. Then, I "flip" the Android Device, the orientation changes, that causes it to call onSaveInstanceState(Bundle b). Information about the Activity is bundled, Activity is destroyed and recreated, then the saved Bundle is sent to onCreate(Bundle b) as expected.

Then, as per Android Activity, the Bundle restores View(s) states.

Except for a single View

class ExampleActivity extends AppCompatActivity {
    public void hide(View view){
        view.setVisibility(View.GONE);
    }
}

Activity XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    style="@style/linear_layout_style"
    tools:context=".ExampleActivity">
        <View style="@style/another_style" />
        <include layout="@layout/included_xml" />
        <View style="@style/another_style" />
</LinearLayout>

Included XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/linear_layout_style">

    <!-- This was supposed to stay hidden after pressed, but it restores in a VISIBLE state -->
    <Button
        style="@style/the_style"
        android:id="@+id/an_unique_id"
        android:onClick="hide"
        android:text="EXAMPLE: Hide" />

    <!-- This is supposed to always be visibile, and it does -->
    <Button
        style="@style/the_style"
        android:onClick="doOtherStuff"
        android:text="Example: Always Available">

        <requestFocus />
    </Button>
</LinearLayout>
Bonatti
  • 2,778
  • 5
  • 23
  • 42
  • PS I tested the include theory and found the saved state issue the same without it. – Jeffrey Blattman Nov 07 '18 at 19:42
  • @JeffreyBlattman I am using `AppCompatActivity`, I have several `View`(s) that are behaving as expected, several `Button`(s) as well (that receive `bt.setVisibility(View.GONE)` programmatically, on server results. I can ensure that the results and the AsynkTask are not re-called, nor buffered or registered. Everything is working except for the `R.id.an_unique_id`, findViewByID is returning the View, as expected... I ran out of places to search for information... – Bonatti Nov 07 '18 at 19:47
  • Regardless of the activity impl, it relies on the views to do the saving state. The view impls come from the platform not from app compat. Like I said, I created a sample app same as your and was able to reproduce it and problems like it simply, without includes. The truth is in the code. If you don't believe me check out AOSP and see for yourself. – Jeffrey Blattman Nov 07 '18 at 20:01

1 Answers1

1

Saved instance doesn't save everything. I looked at the source for TextView (Button extends and is not much more than a TextView) and it does not save the visibility. In fact that only thing it saves is the text and text selection, but then only if you call setFreezesText(true) on it. By the same token, View.onSaveInstanceState() does not do anything (just returns an empty saved state).

So, you need to manually save and restore them yourself. This is an area where I think Android did a very poor job. The docs say:

The default implementation takes care of most of the UI per-instance state for you by calling View.onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)).

which sure seems misleading after looking at the platform source. I mean, here's the implementation of View.onSaveInstanceState() from Nougat:

protected Parcelable onSaveInstanceState() {
    mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
    if (mStartActivityRequestWho != null) {
        BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
        state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
        return state;
    }
    return BaseSavedState.EMPTY_STATE;
}

I'd tend to disagree that this is "takes care of most" of the work.

Jeffrey Blattman
  • 22,176
  • 9
  • 79
  • 134