フラグメントにonBackPressed()を実装する方法は?


460

onBackPressed()Androidアクティビティで実装する方法と同様に、Androidフラグメントで実装できる方法はありますか?

フラグメントのライフサイクルにはありませんonBackPressed()onBackPressed()Android 3.0フラグメントを乗り越える他の方法はありますか?


13
私見、フラグメントは「戻る」ボタンを知らず、気にしないでください。通常はFragmentManagerによって処理されるフラグメントを使用しますが、アクティビティは[戻る]ボタンを気にする可能性があります。しかし、フラグメントはそのより大きな環境(たとえば、アクティビティー内のいくつかの1つであるかどうか)を認識していないため、フラグメントが適切なBACK機能を判別しようとすることは、実際には安全ではありません。
CommonsWare 2011年

61
すべてのフラグメントがWebViewを表示するためにそれを実行し、戻るボタンが押されたときにWebViewが前のページに「戻る」場合はどうでしょうか。
mharper 2012年

Michael Herbigの答えは私にとって完璧でした。しかし、getSupportFragmentManager()を使用できない人のために。代わりにgetFragmentManager()を使用してください。
Dantalian 2013

[類似の質問] [1]に対するDmitry Zaitsevの回答は問題なく機能します。[1]:stackoverflow.com/a/13450668/1372866
ashakirov 2013

6
mharperに答えるには、webview.setOnKeyListener(new OnKeyListener(){@Override public boolean onKey(View v、int keyCode、KeyEvent event){if(keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack())のようにします。 {webview.goBack(); return true;} return false;}});
Omid Aminiva 2015年

回答:


307

このようにonBackPressedして、アクティビティでオーバーライドを解決しました。すべてのFragmentTransactionですaddToBackStackコミットする前に:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}


4
count == 1は、戻るボタンを1回押すだけで最初のフラグメントを閉じることができます。しかし、それ以外の場合は最良のソリューション
Kunalxigxag

2
サポートv7ライブラリを使用していて、アクティビティがFragmentActivity(またはAppCompatActivityなどのサブクラス)から拡張されている場合、これはデフォルトで行われます。FragmentActivity#onBackPressedを
Xiao

3
ただし、このコードではフラグメントのクラスの変数、クラス、関数を使用できません
user25

2
@Prabsサポートv4フラグメントを使用している場合は、getSupportFragmentManager()。getBackStackEntryCount();を使用していることを確認してください。
Veer3383 2017

152

私の意見では、最善の解決策は:

Javaソリューション

シンプルなインターフェースを作成します。

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

そしてあなたの活動で

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

最後にあなたのフラグメントで:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

KOTLINソリューション

1-インターフェースの作成

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2-アクティビティを準備する

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3-ターゲットフラグメントに実装する

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}

1
なにR.id.main_container?それはFragmentPagerのIDですか?
Nathan F.

1
Kotlinソリューションの@MaximeJalluでは、安全な呼び出し(.?)と非nullの強制()を混在させると!!、NPEが発生する可能性があります。キャストが常に安全であるとは限りません。私はむしろ書きたいif ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }kotlineyを以上(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
ANTEK

1
@Antekこんにちは、あなたのリターンをありがとう、あなたの修正をするためにあなたは投稿を編集することができたでしょう。ソリューション全体を厳しく判断しないでください。
Maxime Jallu 2018

4
そうじゃないの.onBackPressed()?.takeIf { !it }?.let{...}.not()逆を返すだけです。
David Miguel

1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }これは、KotlinとNavigationControllerを使用するユーザー向けです
Pavle Pavlov

97

@HaMMeRedの回答によると、これはどのように機能するかを示す疑似コードです。BaseActivity(SlidingMenu libの例のように)子フラグメントを持つメインアクティビティが呼び出されたとしましょう。手順は次のとおりです。

最初に、ジェネリックメソッドを持つインターフェースを実装するインターフェースとクラスを作成する必要があります。

  1. クラスインターフェイスの作成 OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. スキルを実装するクラスを作成する OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. 今から、コードBaseActivityとそのフラグメントに取り組みます

  4. クラスの上にプライベートリスナーを作成する BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. リスナーを設定するcreateメソッド BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. オーバーライドでonBackPressedそのようなものを実装する

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. あなたのフラグメントにonCreateViewあなたは私たちのリスナーを追加する必要があります

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

ほら、今度はフラグメントをクリックして戻ると、カスタムのon backメソッドをキャッチする必要があります。


複数のフラグメントがリスナーであるonBackPressed()場合、「戻る」ボタンが押されたときにどのフラグメントが表示されていたかをどのように判断しますか?
Al Lelopath 2013

2
:あなたは、クリックリスナー代わりに新しいbaseBackPressedListenerをカスタマイズして、自分のbehaviuorを設定するために、匿名としてそこに呼んで、このような何かすることができます((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish

おかげで、私はこれについて私の立場を変更しましたが、私は今ローカル放送メッセージとの分離をお勧めします。私はそれが高度に分離されたより高品質のコンポーネントを生み出すと思います。
HaMMeReD 2014年


これは完璧に機能しました。アクティビティを再開します
raphaelbgr 2016年

86

これは私のために働きました:https : //stackoverflow.com/a/27145007/3934111

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

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}

2
完璧に働いた!ただし、IF条件をもう1つ記述することで、デフォルトのアクションも処理する必要があります。それ以来、slideUpPanelが展開されている場合は下げていました。それで、私は最初にif(panel is up)のチェックをマークしなければなり
ませんでした

18
このソリューションの問題は、getView()がメインビューのみを返すことです(フォーカスを得た場合、サブビューは返しません)。したがって、EditTextがあり、テキストを入力してから「戻る」を1回押してキーボードを非表示にした場合、「戻る」の2回目の押しは「EditText」(ビュー自体)から開始され、このメソッドは「 [戻る]ボタンを押します(メインビューのみをキャッチするため)。私が理解しているように、「EditText」やその他のビューでキープレスイベントをキャッチできますが、「複雑な」フォームがある場合、これは可能な限りクリーンで保守性が良くありません。
Pelpotronic 2015

これは、断片である悪夢のための非常にシンプルなソリューションです。「複雑な」edittextフォームがある場合は、おそらくプロジェクトを削除してやり直す必要があります。標準の動作を使用する場合はfalseを返します。バックプレスをインターセプトし、
それで

65

そのような機能が必要な場合は、アクティビティでオーバーライドして、次に追加する必要があります YourBackPressedすべてのフラグメントにインターフェイスを [戻る]ボタンが押されたときに関連するフラグメントを呼び出すことができます。

編集:以前の回答を追加します。

今日これを行う場合は、ブロードキャストを使用します。他のパネルがマスター/メインコンテンツパネルに一斉に更新されることを期待している場合は、順序付けされたブロードキャストを使用します。

LocalBroadcastManagerSupport Libraryでこれを支援できますonBackPressed。気になるフラグメントをブロードキャストで送信してサブスクライブするだけです。メッセージングは​​より分離された実装であり、拡張性が高いと思うので、これが私の公式の実装推奨事項になります。Intentのアクションをメッセージのフィルターとして使用するだけです。新しく作成したACTION_BACK_PRESSEDを送信し、アクティビティから送信して、関連するフラグメントでリッスンします。


4
かなり気の利いたアイデア!いくつかの追加の考え:+アクティビティからのブロードキャストでこのロジックを実現することが重要です(フラグメントではありません)。OnBackPressedはアクティビティレベルでのみ使用できるため、ここでイベントをトラップし、すべての「リスニング」フラグメントにブロードキャストすることが重要です。+ EventBus(SquareのOttoなど)を使用すると、このようなケースの実装は簡単になります。
Kaushik Gopal 2014年

2
SquareのEventBusはまだ使用していませんが、今後の製品で使用する予定です。私はそれがより良いpure-javaソリューションであると思います。もしあなたがあなたのアーキテクチャのセクションをAndroidでノックアウトできれば、なお良いでしょう。
HaMMeReD 2014

どのようにして上位フラグメントのみがイベントを処理できるようにしますか?たとえば、現在表示されているフラグメントを破壊したい場合
eugene '11 / 10/14

ブロードキャストレシーバーは、それを使用している場合はライフサイクルにバインドする必要があるため、表示されていない場合はイベントを受信しません。
HaMMeReD 2014年

また、LocalBroadcastManager注文した放送を行うことはできません
Xiao

46

どれも簡単に実装できず、最適な方法で機能しません。

フラグメントには、仕事をするonDetachメソッド呼び出しがあります。

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

これは仕事をします。


20
しかし、ここでの問題は、フラグメントがバックプレスのために切り離されたのか、それとも他のアクションのために切り離されたのかわからないということです。
2014

7
onDetach()は、フラグメントがそのアクティビティにアタッチされなくなったときに呼び出されます。これは、onDestroy()の後に呼び出されます。戻るボタンを押すとこのコードが表示されますが、フラグメントからフラグメントに変更するとこのコードも表示されます。あなたはこの仕事をするために気の利いた何かをすることができます...
apmartin1991

DialogFragmentで正常に動作します。
CoolMind 2016年

2
stackoverflow.com/a/27103891/2914140でisRemoving()説明されているように追加することを忘れないでください。
CoolMind 2016

1
これは間違っています。それはいくつかの理由で呼び出すことができます
バリ

40

androidx.appcompat:appcompat:1.1.0以上を使用している場合はOnBackPressedCallback、次のようにフラグメントにを追加できます

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

https://developer.android.com/guide/navigation/navigation-custom-backを参照してください


を使用している場合androidx-core-ktx、使用できますrequireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
最大

LifecycleOwnerこの例のようにparamを追加する必要があることに注意することが重要です。これがないと、handleBackPressed()[戻る]ボタンが押された場合、後で開始されたフラグメントが呼び出されます。
エリックB.

このメソッドはナビゲーションライブラリに必要ですか?
沈黙

25

addToBackStack以下のようにフラグメント間を移行しているときに追加してください:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

を書くとaddToBackStack(null)、それだけで処理しますが、タグを付けると、手動で処理する必要があります。


他に必要なことはありますか、それともaddToBackStackメソッドを追加するだけで十分ですか?
Sreecharan Desabattula 2014

1
それを追加するだけで機能するはずですが、それでも機能しない場合は、コードに何かがあるはずです。コードをどこかに置いて見てください@SreecharanDesabattula
Rudi


20

この質問といくつかの回答は5年以上前のものなので、私の解決策を共有しましょう。これは、@ oyenigunからの回答に対するフォローアップと最新化です。

更新: この記事の最後で、アクティビティをまったく含まない抽象Fragment拡張を使用した代替実装を追加しました。これは、さまざまなバック動作を必要とするネストされたフラグメントを含むより複雑なフラグメント階層を持つ人にとって便利です。

私が使用するフラグメントの一部は、ポップアップボタンなどの小さな情報ビューなど、戻るボタンで閉じたい小さなビューを持っているため、これを実装する必要がありましたが、これは、フラグメント内の戻るボタン。

まず、インターフェースを定義します

public interface Backable {
    boolean onBackPressed();
}

私が呼び出すこのインターフェースBackable(私は命名規則のスティックラーです)にはonBackPressed()boolean値を返す必要のある単一のメソッドがあります。戻るボタンの押下が戻るイベントを「吸収」したかどうかを知る必要があるため、ブール値を適用する必要があります。戻るtrueということは、それがあり、それ以上のアクションは必要ないことを意味しfalseます。このインターフェースは、それ自体のファイルである必要があります(できれば、interfaces)。クラスをパッケージに分離することをお勧めします。

次に、上のフラグメントを見つけます

Fragmentバックスタックの最後のオブジェクトを返すメソッドを作成しました。タグを使用しています... IDを使用している場合は、必要な変更を加えます。この静的メソッドは、ナビゲーション状態などを処理するユーティリティクラスにありますが、もちろん、最適な場所に配置します。啓発のために、私はというクラスに私の物を入れましたNavUtils

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

バックスタックカウントが0より大きいことを確認します。それ以外の場合は、 ArrayOutOfBoundsException。実行時にがスローされる可能性があります。0以下の場合は、nullを返します。後でnull値を確認します...

第三に、断片的に実装する

Backable戻るボタンの動作をオーバーライドする必要があるフラグメントにインターフェイスを実装します。実装方法を追加します。

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

ではonBackPressed()、オーバーライド、何が必要ロジック置きます。戻るボタンで戻るスタックをポップしない場合(デフォルトの動作)、trueを返します。。これは、バックイベントが吸収されたことを示します。それ以外の場合は、falseを返します。

最後に、アクティビティで...

onBackPressed()メソッドをオーバーライドし、このロジックを追加します。

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

バックスタックで現在のフラグメントを取得し、次にnullチェックを実行して、それがBackableインターフェイスを実装しているかどうかを判断します。その場合、イベントが吸収されたかどうかを判断します。もしそうなら、我々はで終わりましたonBackPressed()あり、戻ることができます。それ以外の場合は、通常のバックプレスとして扱い、superメソッドを呼び出します。

アクティビティを含まない2番目のオプション

場合によっては、アクティビティでこれをまったく処理したくなく、フラグメント内で直接処理する必要があります。しかし、あなたはできないと誰言う、バックプレスAPIを使用してフラグメントを作成するか?フラグメントを新しいクラスに拡張するだけです。

Fragmentを拡張し、View.OnKeyListnerインターフェースを実装する抽象クラスを作成します...

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

ご覧のとおり、拡張BackableFragmentするフラグメントはすべて、View.OnKeyListenerインターフェースを使用して自動的にバッククリックをキャプチャします。標準ロジックを使用してonBackButtonPressed()実装されたonKey()メソッド内から抽象メソッドを呼び出し、戻るボタンの押下を識別します。戻るボタン以外のキークリックを登録する必要がある場合は、フラグメントでsuperオーバーライドするときに必ずメソッドを呼び出してonKey()ください。そうしないと、オーバーライドされます、抽象化で動作を。

使いやすく、拡張して実装するだけです。

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

onBackButtonPressed()スーパークラスのメソッドは抽象なので、拡張したら実装する必要がありますonBackButtonPressed()voidフラグメントクラス内でアクションを実行する必要があるだけで戻り、プレスの吸収をアクティビティに中継する必要がないため、戻ります。戻るボタンを使用して行う処理で処理が不要な場合は、必ず Activity onBackPressed()メソッドを呼び出してください。そうしないと、戻るボタンが無効になります。これは望ましくありません。

警告 ご覧のとおり、これはキーリスナーをフラグメントのルートビューに設定するため、フォーカスする必要があります。このクラスを拡張するフラグメント(または他の同じ内部フラグメントまたはビュー)に含まれる編集テキスト(または他のフォーカススティーリングビュー)がある場合は、個別に処理する必要があります。EditText拡張してバックプレスに集中できなくなる良い記事があります。

誰かがこれが役に立つと思うことを願っています。ハッピーコーディング。


ありがとう、それは素晴らしい解決策です。どうしてsuper.onBackPressed();二度電話するの?
CoolMind 2017年

のnull状態に関するシナリオごとに1回だけ呼び出されますcurrentFragment。フラグメントがnull Backableではなく、フラグメントがインターフェースを実装している場合、アクションは実行されません。フラグメントがnullではなく、それが実装されていない場合はBackable、superメソッドを呼び出します。フラグメントがnullの場合、最後のスーパーコールにスキップします。
mwieczorek 2017年

すみません、よくわかりませんでした。a currentFragmentがnullではなく、Backableのインスタンスであり、デタッチされている場合(backボタンを押してフラグメントを閉じた)、最初に出現したものsuper.onBackPressed();が呼び出され、次に2番目が呼び出されます。
CoolMind 2017年

優れたソリューション
David Toledo

たぶん "Closeable"は "Backable"より少し良い音になります
pumnao

15

解決策は簡単です:

1)すべてのフラグメントが拡張する基本フラグメントクラスがある場合は、このコードをそのクラスに追加します。そうでない場合は、そのような基本フラグメントクラスを作成します

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2)Activityクラスで、onBackPressedを次のようにオーバーライドします。

private BaseFragment _currentFragment;

@Override
public void onBackPressed()
{
      super.onBackPressed();
     _currentFragment.onBackPressed();
}

3)フラグメントクラスに、必要なコードを追加します。

 @Override
 public void onBackPressed()
 {
    setUpTitle();
 }


9

onBackPressed() フラグメントをアクティビティから切り離します。

@Sterling Diazの回答によると、彼は正しいと思います。しかし、いくつかの状況は間違っています。(例:画面の回転)

それで、isRemoving()目標を達成するかどうかを検出できたと思います。

onDetach()またはで記述できますonDestroyView()。仕事です。

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}

8

よく私はこのようにそれをやった、そしてそれは私のために働く

シンプルなインターフェース

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

実装例

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

次に、アクティビティのonBackPressedをオーバーライドします

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}

動かない!super.onbackpressed()は常に呼び出されます:(
Animesh Mangla

このイベントの内部フラグメントを渡す方法。アクティビティにViewPagerがあり、そのViewPagerにフラグメントがありますが、これらすべてのフラグメントには子フラグメントがあります。その子フラグメントにボタンイベントを返す方法。
Kishan Vaghela、2016年

@KishanVaghelaよく私はそれ以来アンドロイドに触れなかったが、私は1つのアイデアを持っています。私の答えを更新するために私に24日を与えてください。
Błażej

問題ありません。1つのオプションは、リスナーイベントを親フラグメントからすべての子フラグメントに渡す必要があることです。すべてのフラグメントに対してこれを行う必要があるため、これは理想的な方法ではありません。
Kishan Vaghela

@KishanVaghela活動中のマネージャーについて考えていました。そのインターフェースでフラグメントを追加/削除できます。しかし、このアプローチでは、サブフラグメントを持つすべてのフラグメントにマネージャーを実装する必要があります。したがって、おそらくより良い解決策は、シングルトンとしてマネージャーを作成することです。次に、フラグメント/オブジェクトを追加/削除し、「onBackPressed」でこのマネージャーのメソッド「onBackPressed」を呼び出す必要があります。そのメソッドは、マネージャーに追加されたすべてのオブジェクトでメソッド 'onClick'を呼び出します。それは多かれ少なかれあなたに明らかですか?
Błażej

8

以下のようにプロジェクトにインターフェイスを追加する必要があります。

public interface OnBackPressed {

     void onBackPressed();
}

次に、このインターフェイスをフラグメントに実装する必要があります。

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

また、このonBackPressedイベントは、以下のようなアクティビティのonBackPressedイベントの下でトリガーできます。

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}

これは優れたメソッドですが、getActivity().onBackPressed();「java.lang.StackOverflowError:スタックサイズ8MB」という例外が呼び出されるため、呼び出さないでください。
CoolMind 2017年

8

これは、トリックを実行する小さなコードです。

 getActivity().onBackPressed();

それが誰かを助けることを願っています:)


4
彼は、アクティビティメソッドを単に呼び出すのではなく、動作をオーバーライドするように求めました。
mwieczorek 2017年

2
私はそれを注意深く読みました、ありがとう。ソリューションは、オーバーライドする方法を尋ねられたときに、onBackPressedメソッドをアクティビティにリレーするだけです。
mwieczorek 2017年

7

EventBusを使用する場合、それはおそらくはるかに単純なソリューションです。

あなたのフラグメントで:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

そしてあなたのActivityクラスであなたは定義することができます:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.javaは単なるPOJOオブジェクトです

これは非常にクリーンであり、インターフェース/実装の面倒はありません。


7

これは私の解決策です:

MyActivity.java:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

そしてフラグメントで:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});

5

ナビゲーションコンポーネントを使用すると、次のように実行できます。

ジャワ

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

コトリン

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}

4

onDestroyView()を使用するのはどうですか?

@Override
public void onDestroyView() {
    super.onDestroyView();
}

5
実際のフラグメントから別のフラグメントに行くのはどうですか?あなたの方法を使用して何が起こりますか?:)
Choletski 2015

最も役に立たない答え。i = ior と同じif (1 < 0) {}です。
CoolMind 2018年

4
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}

フラグメントがフォーカス可能なビュー(テキストの編集など)を持たない場合にのみ、この回答のようにフラグメントに戻るボタン自体を処理させます。それ以外の場合は、フラグメントを含むアクティビティにOnBackPress()で最初の回答が書き込まれるときに実行させます。フォーカス可能なビューはフラグメントのフォーカスを盗むからです。ただし、clearFocus()メソッドですべてのフォーカス可能なビューをクリアフォーカスすることでフラグメントのフォーカスを取り戻し、フラグメントに再度フォーカスできます。
Nguyen Tan Dat

4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

フラグメントに以下を追加します。mainactivityのインターフェースを実装することを忘れないでください。

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}

4

私の解決策(Kotlin)では、

私は上のパラメータとしてonBackAlternative機能を使用していBaseActivity

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

BaseFragmentonBackPressAlternativeを設定する関数があります。

BaseFragment

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

これで、onBackPressAlternativeがフラグメントで使用できるようになりました。

サブフラグメント

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}

3

ft.addToBackStack()メソッドを実装しないでください。そうすると、戻るボタンを押したときにアクティビティが終了します。

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();


3

次の手順に従ってください。

常にフラグメントを追加している間、

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

次に、メインアクティビティでオーバーライドします。 onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

アプリの戻るボタンを処理するには、

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

それでおしまい!


3

アクティビティのライフサイクルでは、FragmentActivityまたはAppCompatActivityを使用した場合、常にAndroidの戻るボタンがFragmentManagerトランザクションを処理します。

バックスタックを処理するために、バックスタックカウントを処理したり、何かにタグ付けしたりする必要はありませんが、フラグメントを追加または置換する間、フォーカスを維持する必要があります。戻るボタンのケースを処理するには、次のスニペットを見つけてください。

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

ここでは、ホームフラグメントがアプリケーションのホームページであるため、ホームフラグメントにバックスタックを追加しません。addToBackStackをHomeFragmentに追加した場合、アプリは活動中のすべてのフレームを削除するのを待機し、空白の画面が表示されるため、次の条件を維持します。

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

これで、以前に追加したacitvityのフラグメントを確認でき、HomeFragmentに到達するとアプリが終了します。次のスニペットもご覧ください。

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}

3

フラグメント: メソッドを配置するBaseFragmentを作成します。

 public boolean onBackPressed();

アクティビティ:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

アクティビティは、アタッチされた可視フラグメントに対して実行され、それぞれのフラグメントでonBackPressed()を呼び出し、それらの1つが「true」を返した場合は処理を中止します(処理されたため、それ以上のアクションは行われません)。


これは最もエレガントな方法で完璧に機能します!
asozcan

3

よるとAndroidXのリリースノートandroidx.activity 1.0.0-alpha01リリースされ、紹介されComponentActivity、既存の新しい基本クラスFragmentActivityAppCompatActivity。そしてこのリリースは私たちに新機能をもたらします:

OnBackPressedCallbackビアaddOnBackPressedCallbackを登録してonBackPressed()、アクティビティのメソッドをオーバーライドする必要なくコールバックを受信できるようになりました。


この例を示すことができますか?その方法で解決できません。
ブライアンブライス

1
私は例を見つけることがあります@BryanBryceなく、公式ドキュメント:developer.android.com/reference/androidx/activity/...
TonnyL

このメソッドはb / cを解決しませんAppCompatActivityは実際にはandroidx.activity.ComponentActivityではなくandroidx.core.app.ComponentActivityから拡張しています。
wooldridgetm

3

フラグメントをアクティビティに登録して、バックプレスを処理できます。

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

使用法:

断片的に:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

活動中:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}

3

onBackPressedを処理してカスタムの戻るナビゲーションを提供することは、フラグメント内のコールバックにより簡単になりました。

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

なんらかの条件に基づくデフォルトのバックアクションが必要な場合は、以下を使用できます。

 NavHostFragment.findNavController(this@MyFragment).navigateUp();

3

フラグメントのonCreateメソッド内に以下を追加します。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

このmyFragmentバックプレスは追加できましたが、現在はアクティビティバックプレスが機能していません
Sunil Chaudhary

2

最良のソリューション、

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

            }
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.