it-swarm.com.ru

Обработка заголовка 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();
}
72
Christopher Orr

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

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

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

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

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

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

Больше по теме в документация .

27
Maciej Pigulski

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

Прослушивать события 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);
    .
    .
    .
}
11
Meanman

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

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

Лучше всего, чтобы ОС выполняла как можно больше работы. Предполагая, что каждый фрагмент правильно назван с использованием .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
Lee Hounshell

Я использую аналогичное решение для подход Ли , но вместо этого заменяю метод 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());
}
5
Gnzlt

Используйте метод Фрагменты:

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

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

4
Jurijs Turjanskis

Вы можете решить с OnKeyDown! У меня есть bool mainisopen = true <- Основной фрагмент виден другим Фрагмент 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);
    }

}
1
BitDEVil2K16

Наилучшим подходом является использование предоставленного Android метода интерфейса 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 (title), где заголовок хранит соответствующее название заголовка.

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

Пример:

Допустим, наш Android бэкстек пуст:

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

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

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

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

1
Abhishek