Оформлението не се опреснява след промяна на ориентацията

Имам следната дефиниция на дейност:

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

Сега в моя код добавям бутони динамично в 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);
            }
        }
    }       
    ...
}

Този код работи добре, докато не променя ориентацията на устройството и активността ми се пресъздаде. Въпреки че целият код се изпълнява правилно, екранът изглежда не се актуализира. След като възстановя предишната ориентация, всички елементи изведнъж се появяват. Някаква идея защо и как да го поправя?

Благодаря

РЕДАКТИРАНЕ:

Мисля, че се натъквам на същия проблем, както е описано в тази публикация: Android: findViewById ми дава грешен указател?

Някаква идея как да се реши това?

Както поиска моят onRestoreInstanceState:

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

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

и на onSaveInstanceState:

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

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

сега и двата ми класа изпълняват Parcelable интерфейс. Те се поддържат и възстановяват правилно.

Все пак за някои причини извикването на findViewById get ме насочва към грешен обект (не този, който е пресъздаден).


person MaiOM    schedule 12.06.2014    source източник
comment
Можете ли да публикувате екранната снимка, за да можем да разберем какви промени получавате, може би има нещо общо с android:layout_height="50dp", защото с промяната на ориентацията височината също се променя   -  person Girish Nair    schedule 12.06.2014
comment
Не мога да публикувам екранната снимка, тъй като съдържа поверителни данни. Всеки път, когато дейността се създава отново, тя не успява да актуализира потребителския интерфейс. Например, след като той пресъздаде дейността, тя се показва правилно, но ако задам например добавяне на нов бутон вътре в навигационния път, той няма да се покаже на екрана и въпреки това, ако проверя обекта на навигационен път, новодобавеният бутон е там. Опитах се да обезсиля изгледа на навигационния път, но все още нищо. Някаква идея?   -  person MaiOM    schedule 12.06.2014
comment
Така че искате да опресните или да не опресните?   -  person jyoonPro    schedule 12.06.2014
comment
Бих искал да го освежа по някакъв начин. Така че да отразява възгледите, които са вътре.   -  person MaiOM    schedule 12.06.2014


Отговори (2)


Добавяте изгледи динамично (при събитие с щракване на потребителя).
По подразбиране android не „запомня“ да запази тези динамични изгледи, когато пресъздава активността при промени в конфигурацията, вие трябва сами да се справите с този процес.

Някои възможности:

  • Избягвайте повторното създаване на дейност при завъртане на екрана, като декларирате android:configChanges="keyboardHidden|orientation|screenSize" за дейността си в AndroidManifest.xml – това силно не се препоръчва

  • „Запомнете“ какви изгледи бяха динамично добавени при повторно създаване на дейност след завъртане (например използване на допълнителни флагове за откриване, че е добавен нов филтърен бутон и предаването му чрез пакет в onSaveInstanceState и проверете в onCreate дали трябва да създадете отново бутона), или запазете обект на целия изглед, както е обяснено тук

Една допълнителна забележка: може би искате да посочите "вертикална" ориентация за вашето breadCrumb оформление, то е хоризонтално по подразбиране.

person kiruwka    schedule 12.06.2014
comment
Здравейте, запазвам някои от данните, които вече са в метода onSaveInstanceState, и ги възстановявам onRestoreInstanceState. След като данните бъдат възстановени, в метода onResume правя преобвързване на моя обект за навигация, като добавям бутоните, които са били там преди. След като това стане, ако се опитам да добавя нов бутон към този изглед, той просто не се показва. - person MaiOM; 12.06.2014
comment
Това, което открих досега, е, че по някакъв начин се запазва препратка към предишния случай. Това, което имам предвид с това е, че ако извикам LinearLayout breadCrumb = (LinearLayout) findViewById(R.id.breadCrumb); за първи път моята var сочи към X. След това класът се пресъздава поради промяната на ориентацията. Новото ми обаждане на findViewById(R.id.breadCrumb) сочи към различен обект. Въпреки това, след като добавянето се задейства и аз извикам отново findViewById(R.id.breadCrumb), той извлича адреса на предишния екземпляр. Това правдоподобно ли е? Нещо глупаво ли казвам? - person MaiOM; 12.06.2014
comment
можете ли да публикувате кода от onSave/onRestoreInstanceState, моля? - person kiruwka; 12.06.2014
comment
Е, запазвате/възстановявате данните, но не виждам къде пресъздавате обекта Button. Когато дейността ви бъде пресъздадена, цялата й йерархия на изгледа се унищожава и след това я раздувате отново от xml оформление. Ако искате да видите вашия динамичен бутон, трябва да го създадете отново въз основа например на запазен флаг, който показва, че бутонът е бил там преди завъртането. - person kiruwka; 12.06.2014
comment
Открих причината. провери отговора ми. Изглежда, че ако вашият клас имплементира Parcelable, той все още неявно запазва частните променливи, които не са посочени в замяната на writeToParcel. Това се случваше за моите лични ArrayList‹FilterItemListListener› слушатели; веднъж възстановен, той съдържа указателя към предишния екземпляр на изглед. По неизвестна за мен причина този екземпляр все още беше валиден и връщаше валиден обект. - person MaiOM; 12.06.2014

Разбрах защо се случва това.

onSave/onRestoreInstanceState Поддържах класа currentFilter, който има някои персонализирани слушатели.

Като метод onResume правех следното:

@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()));
                }
            });
    }
}

Указателят към предишния екземпляр беше запазен. Ето защо интерфейсът ми не се държеше правилно.

Сега пререгистрирам слушателите дори когато currentFilter не е null (възстановен е), за да могат да сочат към правилния екземпляр.

Има ли някакъв модел за справяне с тези ситуации?

Благодаря

person MaiOM    schedule 12.06.2014
comment
Очевидно класът Android Bundle не се придържа към протокола за парцелиране, който вместо това се следва по време на IPC marshalling. stackoverflow .com/questions/4853952/ - person MaiOM; 19.06.2014