Обновление выбранного состояния навигационного ящика после обратного нажатия

Каков правильный способ обработки выбранного состояния ящика навигации после возврата назад?

У меня есть ящик навигации с n записями (в списке), как образец SDK в Android Studio. Когда я нажимаю на записи ящика навигации, я хочу, чтобы они были добавлены в задний стек, поэтому я могу вернуться к ним.

В onNavigationDrawerItemSelected (int pos) у меня есть

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        if (position == 0) {
            transaction.replace(R.id.container, new FragmentA());
        } else if (position == 1) {
            transaction.replace(R.id.container, new FragmentB());
        } else {
            transaction.replace(R.id.container, new FragmentC());
        }
        transaction.addToBackStack(null);
        transaction.commit();

Когда я нажимаю на вторую запись в ящике, B выбирается и заменяет A. Если после этого я нажимаю кнопку "Назад", фрагмент A отображается снова, как следует, но B все еще выбран в навигационном ящике.

Как я могу получить статус выбора ящика после нажатия?

Как-то мне нужно позвонить в mDrawerListView.setItemChecked(position, true); или NavigationDrawerFragment.selectItem(int position). Но на какую позицию? Как я его помню?

Перехват с onBackPressed?

@Override
    public void onBackPressed() {}

Но как узнать, какой фрагмент активен снова? И в какую позицию это соответствует.

Есть ли какое-то легкое решение, о котором я вижу? Кажется, что использование обратной связи в сочетании с навигационным ящиком и обновление статуса выбора является стандартным шаблоном.

Ответ 1

Этот шаблон описан в разделе "Назад назад для фрагментов" в документации "Надлежащая обратная навигация" .

Если ваше приложение обновляет другие элементы пользовательского интерфейса, чтобы отразить текущее состояние ваших фрагментов, например панель действий, помните для обновления пользовательского интерфейса при совершении транзакции. Вы должны обновить ваш пользовательский интерфейс после того, как задний стек изменится в дополнение к тому, когда вы совершаете транзакцию. Вы можете слушать, когда FragmentTransaction отменяется установкой FragmentManager.OnBackStackChangedListener

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        public void onBackStackChanged() {
            // Update your UI here.
        }
    });

Это будет подходящее место для обновления текущей опции в ящике навигации.

Ответ 2

У меня было много проблем, пытаясь найти решение этой проблемы, и решение, которое я в конечном итоге использовал, на мой взгляд не "хорошо", но оно работает.

В моем приложении я закончил добавление позиции фрагментов в ящике навигации, когда я добавил фрагмент в стопку. Таким образом, я мог бы назвать setItemChecked с положением в моей onBackStackChanged(). Так что в основном я сделал это:

@Override
public void onBackStackChanged() {
    FragmentManager fm = getFragmentManager();
    String name = fm.getBackStackEntryAt(fm.getBackStackEntryCount()-1).getName();
    mDrawerList.setItemChecked(Integer.parseInt(name),true);
}

public class DrawerItemClickListener implements android.widget.AdapterView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectItem(position);
    }
}

private void selectItem(int position) {
    Fragment fragment;

    switch (position) {
        case 0: // Home
            fragment = new ContentFragment();
            break;
        case 1: // Another
           fragment = new AnotherFragment();
            break;
        default: // Logout
        {
            finish();

            Intent intent = new Intent(this, StartupActivity.class);
            startActivity(intent);

            return;
        }
    }

    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment)
            .addToBackStack(Integer.toString(position))
            .commit();

    mDrawerList.setItemChecked(position, true);
    setTitle(mNavigationItems[position]);
    mDrawerLayout.closeDrawer(mDrawerList);
}

Ответ 3

сегодня у меня была та же борьба. Я решил это, реализовав интерфейс FragmentToActivity в каждый Фрагмент и в моей единственной MainActivity.

Интерфейс:

public interface FragmentToActivity {
    public void SetNavState(int id);
}

Фрагменты:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    ((FragmentToActivity) getActivity()).SetNavState(R.id.nav_myitemid); //just add this line
}

MainActivity:

@Override
public void SetNavState(int id) {
    NavigationView nav = (NavigationView) findViewById(R.id.nav_view);
    nav.setCheckedItem(id);
}

И если вы не знаете, откуда приходит R.id.nav_myitemid, вот мой activity_main_drawer.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">    
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_myitemid"
            android:title="@string/home" />
    </group>
</menu>

Ответ 4

Основываясь на превосходном ответе @matiash (не забудьте +1 его/ее, если вы +1 мой ответ)

Для начала мы предполагаем, что мы находимся в Activity

getFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        public void onBackStackChanged() {
            // Update your UI here.
        }
    });

Вы можете найти текущий фрагмент со следующим:

(из fooobar.com/questions/25097/...)

Fragment f = getFragmentManager().findFragmentById(R.id.frag_container);
if (f instanceof CustomFragmentClass) {
    // do something with f
    f.doSomething();
}

Это должно помочь вам приблизиться. Тогда что-то вроде этого даст вам ссылку на ваш навигационный ящик:

nav = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.nav);

Затем в зависимости от того, как вы настроите код фрагмента ящика:

nav.updateSelected(position);

Метод updateSelected(int) будет в вашем классе NavigationDrawerFragment и может выглядеть примерно так:

public void updateSelected(int position) {
    mCurrentSelectedPosition = position;
    if (mDrawerListView != null) {
        mDrawerListView.setItemChecked(position, true);
    }
}

ПРИМЕЧАНИЕ. Существует много способов сделать это, но это должно помочь вам начать.

Ответ 5

Поздно, но для тех, кто использует меню навигации вместо списка, вот как я это сделал

@SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
       int position=0;
       Fragment fragment;
        int id = item.getItemId();
        if (id == R.id.nav_home) {
            fragment=new HomeFragment();
            position=0;

        } else if (id == R.id.nav_profile) {

           fragment=new ProfileFragment();
            position=1;
        }  else if (id == R.id.nav_settings) {
             fragment=new SettingsFragment();
            position=2;
        }

          getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.relative_container, fragment)
            .addToBackStack(""+position)
            .commit();

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

и добавить listner для backstack

 getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {

             FragmentManager fm = getSupportFragmentManager();
             String name = fm.getBackStackEntryAt(fm.getBackStackEntryCount()-1).getName();
               if(!TextUtils.isEmpty(name))
             selectNavigationDrawerItem(Integer.parseInt(name));

        }
    });

selectNavigationDrawerItem метод

 private void selectNavigationDrawerItem(int position){

               if(position==0){
                   navigationView.setCheckedItem(R.id.nav_home);
               }
               else if(position==1){
                   navigationView.setCheckedItem(R.id.nav_profile);
               }
               else if(position==2){
                   navigationView.setCheckedItem(R.id.nav_settings);   
              }

     }