панель действий вверх с фрагментами

88

У меня есть вкладки ActionBar / ViewPager макет с три вкладки говорит A , B и C . На вкладке C вкладке (фрагмент), я добавляю еще один фрагмент скажет фрагмент D . с участием

 DFragment f= new DFragment();
 ft.add(android.R.id.content, f, "");
 ft.remove(CFragment.this);
 ft.addToBackStack(null);
 ft.commit();

Я изменяю панель действий в onResume DFragment, чтобы добавить кнопку:

ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);

Теперь в DFragment, когда я нажимаю аппаратную кнопку (телефон) Назад, я возвращаюсь к исходному макету с вкладками (ABC) с выбранным CFragment. Как я могу достичь этой функции с помощью кнопки вверх на панели действий?

СохаилАзиз
источник
Возможный дубликат Как реализовать onBackPressed () во фрагментах?
Code-Apprentice,

Ответы:

185

Реализуйте OnBackStackChangedListenerи добавьте этот код в свою активность фрагментов.

@Override
public void onCreate(Bundle savedInstanceState) {
    //Listen for changes in the back stack
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    //Handle when activity is recreated like on orientation Change
    shouldDisplayHomeUp();
}

@Override
public void onBackStackChanged() {
    shouldDisplayHomeUp();
}

public void shouldDisplayHomeUp(){
   //Enable Up button only  if there are entries in the back stack
   boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
   getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}

@Override
public boolean onSupportNavigateUp() {
    //This method is called when the up button is pressed. Just the pop back stack.
    getSupportFragmentManager().popBackStack();
    return true;
}
Роджер Гарсон Ньето
источник
20
О том onSupportNavigateUp(), «Метод не отменяет метод из своего суперкласса».
Фред
10
Если у вас уже есть onOptionsItemSelected, можно также проверить itemId android.R.id.homeвместо добавления onSupportNavigateUp.
domsom 06
1
Если версия API> = 14, используйте onNavigateUp вместо onSupportNavigateUp @Override public boolean onNavigateUp () {// Этот метод вызывается при нажатии кнопки вверх. Просто возвратный стек. getFragmentManager (). popBackStack (); вернуть истину; }
Tejasvi Hegde
1
Должен ли быть курсор вверх, который отображается рядом со значком вашего приложения в ActionBar? Когда я реализую этот код, я его не вижу. Я могу только щелкнуть значок, но он ничего не делает. Android 4.0+.
Azurespot
3
Если onBackStackChanged () не переопределяет, убедитесь, что ваше действие реализует интерфейс FragmentManager.OnBackStackChangedListener.
CBA110,
41

Я понял. просто переопределите onOptionsItemSelected в активности хостинга и откройте backstack, например

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home: {
            FragmentManager fm = getSupportFragmentManager();
            if (fm.getBackStackEntryCount() > 0) {
                fm.popBackStack();
                return true;
            }
            break;
        }
    }
    return super.onOptionsItemSelected(item);
}

Звоните getActionBar().setDisplayHomeAsUpEnabled(boolean);и getActionBar().setHomeButtonEnabled(boolean);в , onBackStackChanged()как описано в ответ ниже.

СохаилАзиз
источник
3
вам также необходимо вызвать getActivity (). getActionBar (). setDisplayHomeAsUpEnabled (false); чтобы убрать кнопку вверх после того, как вы
вытолкнете
Это неправильный способ. Кнопка вверх остается активной.
Роджер Гарсон Ньето
1
Вы должны поместить этот код в switchоператор с регистром android.R.id.home.
Фред
18

Если у вас есть одно родительское действие и вы хотите, чтобы эта кнопка вверх работала как кнопка возврата, вы можете использовать этот код:

добавьте это в onCreate в свой основной класс активности

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
            if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
                getSupportActionBar().setHomeButtonEnabled(true);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            } else {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSupportActionBar().setHomeButtonEnabled(false);
            }
        }

    });

а затем добавьте onOptionsItemSelected вот так:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
     ....
 }

Я обычно использую это все время и кажется вполне законным

Дэниел Йонкер
источник
1
Я пробовал точно использовать этот код, в одном Activityиз своих, в надежде, что он вернется к фрагменту, с которого он начался. Когда я перехожу к своему Activity, рядом со значком приложения появляется кнопка «Назад» , но я нажимаю на значок, и ничего не происходит (он должен вернуться к фрагменту). Есть идеи, почему? Спасибо.
Azurespot
1
@Daniel, ваш код законный .. он работает. Вы просто можете захотеть использовать его с опцией try catch на всякий случай ... вы знаете, чтобы предотвратить любые непредвиденные исключения и сбои приложений
Зуко
1
@NoniA. Это только возврат к предыдущему фрагменту (например, фрагмент B -> фрагмент A), если вы увеличиваете 1 фрагмент в новом действии, это не вернется к предыдущему действию.
Дэниел Йонкер,
10

вы можете вернуться с помощью кнопки «вверх», например кнопки «назад»;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            super.onBackPressed();
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Моаз Рашад
источник
8

Я знаю, что это старый вопрос, но может быть, кому-то (вроде меня) он тоже нужен.

Если ваша Activity расширяет AppCompatActivity , вы можете использовать более простое (двухэтапное) решение:

1 - Всякий раз, когда вы добавляете фрагмент, отличный от домашнего, просто показывайте кнопку вверх сразу после совершения транзакции фрагмента. Как это:

    // ... add a fragment
    // Commit the transaction
    transaction.commit();

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Затем при нажатии кнопки ВВЕРХ вы ее скрываете.

@Override
public boolean onSupportNavigateUp() {
    getSupportActionBar().setDisplayHomeAsUpEnabled(false);        
    return true;
}

Вот и все.

Мартом
источник
7

Я использовал комбинацию ответов Роджера Гарсона Ньето и Сохайлазиза . В моем приложении есть одна MainActivity и загруженные в нее фрагменты A, B, C. Мой «домашний» фрагмент (A) реализует OnBackStackChangedListener и проверяет размер backStack; если меньше единицы, то кнопка ВВЕРХ скрывается. Фрагменты B и C всегда загружают кнопку возврата (в моем дизайне B запускается из A, а C запускается из B). Сама MainActivity просто выдвигает задний стек при нажатии кнопки ВВЕРХ и имеет методы для отображения / скрытия кнопки, которую вызывают фрагменты:

Основное занятие:

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // Respond to the action bar's Up/Home button
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }

fragmentA (реализует FragmentManager.OnBackStackChangedListener):

public void onCreate(Bundle savedinstanceSate) {
    // listen to backstack changes
    getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);

    // other fragment init stuff
    ...
}

public void onBackStackChanged() {
    // enable Up button only  if there are entries on the backstack
    if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
        ((MainActivity)getActivity()).hideUpButton();
    }
}

фрагментB, фрагментC:

public void onCreate(Bundle savedinstanceSate) {
    // show the UP button
    ((MainActivity)getActivity()).showUpButton();

    // other fragment init stuff
    ...
}
многословный
источник
5

Это сработало для меня. Переопределите, например, onSupportNavigateUp и onBackPressed (код в Kotlin);

override fun onBackPressed() {
    val count = supportFragmentManager.backStackEntryCount
    if (count == 0) {
        super.onBackPressed()
    } else {
        supportFragmentManager.popBackStack()
    }
}

override fun onSupportNavigateUp(): Boolean {
    super.onSupportNavigateUp()
    onBackPressed()
    return true
}

Теперь во фрагменте, если отобразить стрелку вверх

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Нажав на нее, вы вернетесь к предыдущему действию.

bebe
источник
5

Котлин:

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
        setupHomeAsUp()
    }

    private fun setupHomeAsUp() {
        val shouldShow = 0 < supportFragmentManager.backStackEntryCount
        supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
    }

    override fun onSupportNavigateUp(): Boolean = 
        supportFragmentManager.popBackStack().run { true }

    ...
}
fada21
источник
2

Это очень хорошее и надежное решение: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

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

Для некоторых из вас, возможно, есть небольшой недостаток в абстрактном классе ...

Вкратце решение по ссылке выглядит так:

// Abstract Fragment handling the back presses

public abstract class BackHandledFragment extends Fragment {
    protected BackHandlerInterface backHandlerInterface;
    public abstract String getTagText();
    public abstract boolean onBackPressed();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(!(getActivity()  instanceof BackHandlerInterface)) {
            throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
        } else {
            backHandlerInterface = (BackHandlerInterface) getActivity();
        }
    }

    @Override
    public void onStart() {
        super.onStart();

        // Mark this fragment as the selected Fragment.
        backHandlerInterface.setSelectedFragment(this);
    }

    public interface BackHandlerInterface {
        public void setSelectedFragment(BackHandledFragment backHandledFragment);
    }
}   

И использование в деятельности:

// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment

public class TheActivity extends FragmentActivity implements BackHandlerInterface {
    private BackHandledFragment selectedFragment;

    @Override
    public void onBackPressed() {
        if(selectedFragment == null || !selectedFragment.onBackPressed()) {
            // Selected fragment did not consume the back press event.
            super.onBackPressed();
        }
    }

    @Override
    public void setSelectedFragment(BackHandledFragment selectedFragment) {
        this.selectedFragment = selectedFragment;
    }
}
Amio.io
источник
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
bummi
Кстати: если вы обнаружите дубликаты, отметьте их как таковые. Спасибо.
bummi 03
важно установитьSelectedFragment внутри onStart?
VLeonovs