Обновление вида внутри фрагмента

Я искал многочисленные вопросы, похожие на этот, но не нашел ответа ни на один из них.

У меня есть активность, в которой есть 3 вкладки, доступные через панель действий. Я добился этого, добавив 3 фрагмента, которые расширяют пользовательское представление, которое я сделал, расширяя класс представления.

В момент изменения базы данных я пытаюсь обновить представление на своей вкладке, вызывая invalidate()/postinvalidate(), но это не работает. То же самое верно для вызова onCreateView фрагмента, как и многие другие варианты, которые я рассматривал.

Однако, когда я перехожу на другую вкладку и возвращаюсь назад, изменение было внесено, и мое представление обновляется, как и должно быть.

Как я могу смоделировать то же самое, что происходит при переходе на другую вкладку? Что происходит. Я попытался посмотреть на жизненный цикл фрагмента (пытался вызвать onCreateView()), чтобы понять это, но он просто не хочет обновлять/перерисовывать, как должен.

Данные загружаются правильно, так как данные меняются, когда я перехожу на другую вкладку.

Я удалил часть кода, так как он больше не актуален. Я реализовал Cursorloaders вместо собственного шаблона Observer, чтобы уведомлять об изменении. Это моя основная деятельность сейчас.

Вопрос в том, что мне теперь делать, если я хочу перерисовать вид внутри этих фрагментов. Если я применяю fragmentObject.getView().invalidate(), это не сработает. У меня та же проблема, что и раньше, но теперь мой Observer для уведомления об изменении в базе данных правильно реализован с помощью загрузчиков.

public class ArchitectureActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);     
    setContentView(R.layout.main);

    ActionBar actionbar = getActionBar();
    actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    ActionBar.Tab EditTab = actionbar.newTab().setText("Edit");
    ActionBar.Tab VisualizeTab = actionbar.newTab().setText("Visualize");
    ActionBar.Tab AnalyseTab = actionbar.newTab().setText("Analyse");

    Fragment editFragment = new EditFragment();
    Fragment visualizeFragment = new VisualizeFragment();
    Fragment analyseFragment = new AnalyseFragment();

    EditTab.setTabListener(new MyTabsListener(editFragment));
    VisualizeTab.setTabListener(new MyTabsListener(visualizeFragment));
    AnalyseTab.setTabListener(new MyTabsListener(analyseFragment));

    actionbar.addTab(EditTab);
    actionbar.addTab(VisualizeTab);
    actionbar.addTab(AnalyseTab);

    ArchitectureApplication architectureApplication = (ArchitectureApplication)getApplicationContext();
    architectureApplication.initialize();

    getLoaderManager().initLoader(0, null, this);
    getLoaderManager().initLoader(1, null, this);
}

public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    if (id == 0){
        return new CursorLoader(this, GraphProvider.NODE_URI , null, null, null, null);
    } else if (id == 1){
        return new CursorLoader(this, GraphProvider.ARC_URI , null, null, null, null);
    }
    return null;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    // Reloading of data, actually happens because when switching to another tab the new data shows up fine
    Log.e("Data", "loaded");
}

public void onLoaderReset(Loader<Cursor> loader) {
}
}

person J. Maes    schedule 03.10.2012    source источник
comment
Может быть, вы могли бы что-то сделать с saveInstanceState? Если вы прочтете руководство по работе с базами данных, предоставленное с Android API, вы увидите часть, которая предотвращает выполнение каких-либо действий, если saveInstanceState изменяется или не изменяется. Точно не помню. Может быть, вы могли бы сбросить saveInstanceState, что привело бы к обновлению базы данных, запрашивающему обновление представления? Просто идея.   -  person thomas.cloud    schedule 07.10.2012
comment
Я заметил, что вы не делаете никаких вызовов Fragment#getView()... разве представление, возвращаемое getView(), не является представлением, которым вы пытаетесь манипулировать? Возможно, причина, по которой вид не перерисовывается, заключается в том, что вы не обновляете правильный вид?   -  person Alex Lockwood    schedule 08.10.2012
comment
В текущей ситуации я этого не называю. Я попытался вызвать editFragment.getView().invalidate(); в наблюдателе он вызывается, но не работает. Я вызвал правильное представление другими способами, передав его в качестве параметра методу, который также вызывает метод addNode() в приведенном выше классе ArchitectureData.   -  person J. Maes    schedule 08.10.2012


Ответы (5)


  1. Не пытайтесь вызывать onCreateView() самостоятельно... это метод жизненного цикла и должен вызываться только фреймворком.

  2. Fragment — это повторно используемые компоненты пользовательского интерфейса. У них есть собственный жизненный цикл, они отображают собственное представление и определяют собственное поведение. Обычно вам не нужно, чтобы ваш Activity возился с внутренней работой Fragment, так как поведение Fragment должно быть автономным и независимым от какого-либо конкретного Activity.

    Тем не менее, я думаю, что лучшим решением будет реализовать каждый из ваших Fragment интерфейсов LoaderManager.LoaderCallbacks<D>. Каждый Fragment будет инициализировать Loader (то есть CursorLoader, если вы используете ContentProvider, поддерживаемый базой данных SQLite), и этот Loader будет отвечать за (1) загрузку данных в фоновом потоке и (2) прослушивание изменений контента. которые вносятся в источник данных, и доставляют новые данные в onLoadFinished() всякий раз, когда происходит изменение содержимого.

    Это решение лучше, чем ваше текущее решение, поскольку оно полностью основано на событиях. Вам нужно только обновить представление, когда данные будут доставлены в onLoadFinished() (в отличие от необходимости вручную проверять, был ли изменен источник данных каждый раз, когда вы нажимаете на новую вкладку).

  3. Если вы ленивы и просто хотите получить быстрое решение, вы можете также обновить представление в методе onResume() вашего Fragment.

person Alex Lockwood    schedule 07.10.2012
comment
Спасибо за ответ и понимание :). На данный момент приоритет быстрый, поэтому я сначала попробовал ваше третье решение, но оно не сработало. На данный момент класс, взаимодействующий с моим Contentprovider, реализует Observable, мое представление внутри моего фрагмента (или моего фрагмента, когда я пробовал onCreateView) реализует Observer. Таким образом, метод обновления был вызван, когда моя база данных была обновлена, и я попытался вызвать invalidate/onCreateView/onResume/... . Я тоже считаю это событием. Завтра я повнимательнее рассмотрю ваше второе решение, так как я не знаком с загрузчиками. - person J. Maes; 07.10.2012
comment
@J.Maes Я много писал о Loaders, начиная с эта запись в блоге... возможно, она поможет. CursorLoader автоматически регистрирует ContentObserver в своем Cursor, поэтому пока ваш ContentProvider вызывает Cursor#setNotificationUri(...) в query() и ContentResolver#notifyChange(...) при внесении изменений в базу данных, CursorLoader загружает новые данные и доставляет их в onLoadFinished(). Реализация собственного ContentObserver, вероятно, все усложняет... - person Alex Lockwood; 07.10.2012
comment
Я понимаю, что эта реализация была бы лучше, но не могли бы вы сказать, как это изменит обновление или отсутствие обновления по сравнению с текущей ситуацией? Мое текущее решение с моими собственными наблюдателями на самом деле не является решением, поскольку мои новые данные не отображаются (фактически рисуются). Я быстро просмотрел ваш блог, и, насколько я понимаю, мне нужно будет что-то сделать в onLoadFinished()/другом методе, когда данные изменятся, но что мне нужно поместить туда, чтобы убедиться, что мои данные отрисовываются . edit/ Структура вашего туториала, между прочим, одна из лучших, которые я когда-либо видел g+1 - person J. Maes; 08.10.2012
comment
@J.Maes Спасибо за +1. :) Хм... ну, все Loader вещи, которые я предложил, были сделаны в предположении, что ваш View был реализован правильно. Похоже, это связано с тем, что изменения обновляются при переключении между вкладками (я предполагаю, что это связано с тем, что Fragments полностью уничтожаются/воссоздаются, когда фрагмент исчезает/на экране). Итак, учитывая все это, я готов поспорить, что проблема в том, что способ, которым вы в настоящее время обновляете представление в Fragment, просто неверен. Это может быть проблема с Fragment или View... - person Alex Lockwood; 08.10.2012
comment
@ J.Maes, не могли бы вы опубликовать часть кода, который у вас есть в настоящее время, иллюстрирующий, как вы уведомляете / устанавливаете новые данные представления? - person Alex Lockwood; 08.10.2012
comment
Я добавил некоторый код, я уверен, что мои данные загружаются правильно, они просто не отображаются правильно при вызове недействительности после загрузки. Теперь, когда я внимательно смотрю на свой код, он становится все грязнее и грязнее... Эти загрузчики сделают его чище. - person J. Maes; 08.10.2012
comment
Я добавил некоторый код после того, как реализовал предложенные вами загрузчики. Данные обновляются должным образом, но они все еще не отображаются сразу, если я вызываю invalidate(). +1 за улучшение моего дизайна :), но исходная проблема все еще существует - person J. Maes; 10.10.2012
comment
@AlexLockwood Не могли бы вы помочь мне в этом :: call" title="странное поведение панели действий sherlock searchview выше уровня API 11 после вызова"> stackoverflow.com/questions/20629665/ stackoverflow.com/questions/20606647/ - person AndroidLearner; 17.12.2013

У меня была похожая (хотя и не идентичная) проблема, которую я мог решить следующим образом:

В фрагменте я добавил

public void invalidate() {
    myView.post(new Runnable() {
        @Override
        public void run() {
            myView.invalidate();
        }
    });
}

и я вызвал этот метод из действия, когда хотел обновить представление myView фрагмента. Использование post() гарантирует, что представление становится недействительным только тогда, когда оно готово к рисованию.

person Jörg Eisfeld    schedule 21.05.2014
comment
Как вы можете вызвать этот метод из активности? Мы упускаем важную часть вашего ответа. - person Sam Joos; 25.05.2016

Я нашел обходной путь, чтобы обновить представление внутри моего фрагмента. Я воссоздавал (новый CustomView) представление каждый раз, когда база данных обновлялась. После этого я вызываю setContentView (представление CustomView). Это больше похоже на хак, но он работает, и ничто другое из того, что я пробовал, не работает.

Хотя моя проблема на самом деле не была решена, я отдал награду Алексу Локвуду. Его совет о загрузчиках сделал мое приложение лучше, и это заставило меня продолжать искать решение, которое я в конце концов нашел.

person J. Maes    schedule 12.10.2012
comment
Извините, я не мог дать много советов... Я никогда раньше не реализовывал свой собственный пользовательский вид, поэтому мне нечего было сказать. Я рад, что вы нашли обходной путь и что я помог сделать ваше приложение лучше. :) - person Alex Lockwood; 13.10.2012
comment
@J.Maes У меня такое же требование, не могли бы вы сказать, что создали CustomView и использовали его для setContentView. - person Lalit Poptani; 21.03.2013

Я была такая же проблема. Моим решением было отсоединить фрагмент и снова прикрепить его.

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        Fragment f = getFragment(action);
        if(forceUpdate)
        {
            fragmentTransaction.detach(f);
            fragmentTransaction.attach(f);
        }
        fragmentTransaction.replace(R.id.mainFragment, f);
        fragmentTransaction.commit();
        currentAction = action;
person mc.dev    schedule 30.04.2013
comment
«Исправление» Мика сработало и для меня ... кто-нибудь знает более элегантное решение или может объяснить, почему это работает? - person Gene Myers; 02.02.2014
comment
Это входит в действие? - person Si8; 20.10.2016
comment
В основном да. getSupportFragmentManager() — это метод FragmentActivity. Однако вы можете разместить его где угодно, если у вас есть экземпляр FragmentManager. - person mc.dev; 22.10.2016

Самое быстрое решение, работающее для меня:

 @Override
    public void onPause() {
        super.onPause();
        if (isRemoving() && fragmentView != null) {
            ((ViewGroup) fragmentView).removeAllViews();
        }
    }
person georgehardcore    schedule 14.07.2015