Обработка заголовка ActionBar с помощью стека фрагмента?

У меня есть Activity, где я загружаюсь в ListFragment, и, щелкнув, он сверлит уровень и отобразит новый тип ListFragment, заменив исходный (используя метод showFragment ниже). Это помещается в задний стек.

В начале активность показывает заголовок по умолчанию на панели действий (т.е. автоматически устанавливается на основе приложения android:label).

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

Однако при нажатии Back я хотел бы восстановить исходный заголовок по умолчанию. Это не то, о чем FragmentTransaction знает, поэтому заголовок не восстанавливается.

Я смутно читал о FragmentBreadCrumbs, но для этого, похоже, требуется использование настраиваемого представления. Я использую ActionBarSherlock и предпочитаю, чтобы у меня не было собственного пользовательского названия.

Каков наилучший способ сделать это? Возможно ли это без загрузки кода шаблона и отслеживания названий, указанных на этом пути?


protected void showFragment(Fragment f) {
  FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  ft.replace(R.id.fragment_container, f);
  ft.addToBackStack(null);
  ft.commit();
}

Ответ 1

В каждом фрагменте и каждом действии я меняю заголовок наподобие этого. Таким образом, активное название всегда будет правильным:

@Override
public void onResume() {
    super.onResume();
    // Set title
    getActivity().getActionBar()
        .setTitle(R.string.thetitle);
}

Есть случаи, когда onResume не вызывается внутри фрагментов. В некоторых из этих случаев мы можем использовать:

public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if(isVisibleToUser) {
        // Set title
        getActivity().getActionBar()
            .setTitle(R.string.thetitle);
    }
}

Ответ 2

Поскольку исходный ответ довольно старый, это может также помочь. Как указано в документации, можно захотеть зарегистрировать listener для прослушивания изменений заднего стека в хостинге Activity:

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

Затем определите ситуацию в методе обратного вызова и установите правильный заголовок, не получив доступ к ActionBar из Fragment.

Это более элегантное решение, так как Fragment не обязательно знать о существовании ActionBar, а Activity обычно является местом, которое управляет стоп-кадром, поэтому, если он обрабатывается, кажется, более подходящим, Fragment следует всегда рассматривать только по своему содержанию, а не по окружению.

Подробнее о теме в документации.

Ответ 3

Пусть управляющая деятельность выполняет всю работу следующим образом:

Слушайте события backstack (в onCreate() активности):

// Change the title back when the fragment is changed
    getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fragment = getFragment();
            setTitleFromFragment(fragment);
        }
    });

Получить текущий фрагмент из контейнера:

/**
 * Returns the currently displayed fragment.
 * @return
 *      Fragment or null.
 */
private Fragment getFragment() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
    return fragment;
}

Установите фрагмент внутри представления содержимого:

private void setFragment(Fragment fragment, boolean addToBackStack) {
    // Set the activity title
    setTitleFromFragment(fragment);
    .
    .
    .
}

Ответ 4

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

@Override
public void onResume() {
    super.onResume();
    ((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}

Ответ 5

Лучше всего позволить ОС выполнять как можно больше работы. Предполагая, что каждый фрагмент правильно назван с использованием .addToBackStack( "title" ), тогда вы можете переопределить onBackPressed что-то вроде этого для достижения желаемого поведения:

// this example uses the AppCompat support library
// and works for dynamic fragment titles
@Override
public void onBackPressed() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    int count = fragmentManager.getBackStackEntryCount();
    if (count <= 1) {
        finish();
    }
    else {
        String title = fragmentManager.getBackStackEntryAt(count-2).getName();
        if (count == 2) {
            // here I am using a NavigationDrawer and open it when transitioning to the initial fragment
            // a second back-press will result in finish() being called above.
            mDrawerLayout.openDrawer(mNavigationDrawerFragment.getView());
        }
        super.onBackPressed();
        Log.v(TAG, "onBackPressed - title="+title);
        getSupportActionBar().setTitle(title);
    }
}

Ответ 6

Я использую аналогичное решение для подхода Ли, но вместо этого вместо метода onBackStackChanged().

Сначала я устанавливаю имя фрагмента при добавлении транзакции в задний стек.

getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_content, fragment)
                .addToBackStack(fragmentTitle)
                .commit();

Затем я переопределяю метод onBackStackChanged(), и я вызываю setTitle() с последним именем записи Backstack.

@Override
public void onBackStackChanged() {
    int lastBackStackEntryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
    FragmentManager.BackStackEntry lastBackStackEntry =
            getSupportFragmentManager().getBackStackEntryAt(lastBackStackEntryCount);

    setTitle(lastBackStackEntry.getName());
}

Ответ 7

Использовать метод фрагментов:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)

Он вызывается для каждого фрагмента, но onResume не является.

Ответ 8

Лучший подход заключается в использовании андроида, предоставляемого интерфейсом OnBackStackChangedListener метода onBackStackChanged().

Допустим, у нас есть ящик для навигации с 4 параметрами, к которым пользователь может перейти. В этом случае у нас будет 4 фрагмента. Сначала посмотрим на код, а затем я объясню, как работает.

    private int mPreviousBackStackCount = 0;
    private String[] title_name = {"Frag1","Frag2","Frag3","Frag4"};
    Stack<String> mFragPositionTitleDisplayed;

    public class MainActivity extends ActionBarActivity implements FragmentManager.OnBackStackChangedListener
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ....
    ....
    ....
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    mFragPositionTitleDisplayed = new Stack<>();
}

public void displayFragment() {
    Fragment fragment = null;
    String title = getResources().getString(R.string.app_name);
    switch (position) {
        case 0:
            fragment = new Fragment1();
            title = title_name[position];
            break;
        case 1:
            fragment = new Fragment2();
            title = title_name[position];
            break;
        case 2:
            fragment = new Fragment3();
            title = title_name[position];
            break;
        case 3:
            fragment = new Fragment4();
            title = title_name[position];
            break;
        default:
            break;
    }
    if (fragment != null) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.container_body, fragment)
                .addToBackStack(null)
                .commit();
        getSupportActionBar().setTitle(title);
    }
}

@Override
public void onBackStackChanged() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    if(mPreviousBackStackCount >= backStackEntryCount) {
        mFragPositionTitleDisplayed.pop();
        if (backStackEntryCount == 0)
            getSupportActionBar().setTitle(R.string.app_name);
        else if (backStackEntryCount > 0) {
            getSupportActionBar().setTitle(mFragPositionTitleDisplayed.peek());
        }
        mPreviousBackStackCount--;
    }
    else{
        mFragPositionTitleDisplayed.push(title_name[position]);
        mPreviousBackStackCount++;
    }

}   

В показанном коде есть метод displayFragment(). Здесь я показываю фрагмент на основе опции, выбранной из навигационного ящика. Переменная позиция соответствует позиции элемента, нажатого из ListView или RecyclerView в навигационном ящике. Я установил заголовок панели действий соответственно с помощью getSupportActionBar.setTitle(название), где заголовок хранит соответствующее название названия.

Всякий раз, когда мы нажимаем элемент из навигационного ящика, фрагмент отображается в зависимости от элемента, который был нажат на пользователя. Но на задней стороне этот фрагмент добавлен в backstack, и метод onBackStachChanged() получает удар. То, что я сделал, это то, что я создал переменную mPreviousBackStackCount и инициализировал ее до 0. Я также создал дополнительный стек, который будет хранить имена названий панели действий. Всякий раз, когда я добавляю новый фрагмент в backstack, я добавляю соответствующее название названия в созданный стек. На противоположной стороне всякий раз, когда я нажимаю кнопку назад, вызывается onBackStackChanged(), и я вывожу последнее имя заголовка из своего стека и устанавливаю заголовок на имя, полученное методом peek() стека.

Пример:

Давайте скажем, что наш андроид backstack пуст:

Нажмите "Выбор 1" из навигационного ящика: вызывается onBackStachChanged(), а фрагмент 1 добавляется к андроидному backstack, backStackEntryCount устанавливается в 1, а Frag1 переносится в мой стек, а размер mFragPositionTitleDisplayed становится равным 1.

Нажмите Выбор 2 из навигационного ящика: вызывается onBackStachChanged(), а фрагмент 2 добавляется к backstack андроида, backStackEntryCount устанавливается в 2, а Frag2 помещается в мой стек, а размер mFragPositionTitleDisplayed становится равным 2.

Теперь у нас есть 2 элемента как в стеке android, так и в моем стеке. При нажатии кнопки onBackStackChanged() вызывается, а значение backStackEntryCount равно 1. Код входит в часть if и выдает последнюю запись из моего стека. Таким образом, андроид backstack имеет только 1 фрагмент - "Фрагмент 1", и мой стек имеет только 1 заголовок - "Frag1". Теперь я просто заглядываю() в заголовок из своего стека и устанавливаю панель действий в этот заголовок.

Помните: для установки действия bat title используйте peek(), а не pop(), иначе ваше приложение выйдет из строя, когда вы откроете более 2 фрагментов и попытаетесь вернуться назад, нажав кнопку "Назад".

Ответ 9

Вы можете решить с помощью onKeyDown! У меня есть bool mainisopen = true < - MainFragment Visible другой фрагмент mainisopen = false

и вот мой код:

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == false) {
        mainisopen = true;
        HomeFrag fragment = new HomeFrag();
        FragmentTransaction fragmentTransaction =
                getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragmet_cont, fragment);
        fragmentTransaction.commit();
        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
        navigationView.setNavigationItemSelectedListener(this);
        this.setTitle("Digi - Home"); //Here set the Title back
        return true;
    } else {
        if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == true) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Wollen sie die App schliessen!");
            builder.setCancelable(true);

            builder.setPositiveButton("Ja!", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    System.exit(1);
                }
            });

            builder.setNegativeButton("Nein!", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(getApplicationContext(), "Applikation wird fortgesetzt", Toast.LENGTH_SHORT).show();
                }
            });

            AlertDialog dialog = builder.create();
            dialog.show();

            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

}

Ответ 10

Как описано здесь, мое решение заключается в добавлении этого кода в метод MainActivity onCreate(): и изменении заголовка панели действий

FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        Fragment currentFragment = fragmentManager.findFragmentById(R.id.My_Container_1_ID);
        currentFragment.onResume();
    }
});

и изменение заголовка панели действий в методе фрагмента onResume()

@Override
public void onResume() {
    super.onResume();
    AppCompatActivity activity = (AppCompatActivity) getActivity();
    ActionBar actionBar = activity.getSupportActionBar();
    if(actionBar!=null) {
        actionBar.setTitle("Fragment Title");
        actionBar.setSubtitle("Subtitle");
    }

}

Ответ 11

Чтобы обновить заголовок панели действий, нажмите Назад. Просто поставьте

getActivity.setTitle( "название" )

внутри метода onCreateView.