結果の開始フラグメントのように機能するメソッドはありますか?


91

現在、オーバーレイにフラグメントがあります。これはサービスにサインインするためのものです。電話アプリでは、オーバーレイに表示したいステップはそれぞれ独自の画面とアクティビティです。サインインプロセスには3つの部分があり、それぞれにstartActivityForResult()で呼び出される独自のアクティビティがありました。

次に、フラグメントとオーバーレイを使用して同じことを行います。オーバーレイには、各アクティビティに対応するフラグメントが表示されます。問題は、これらのフラグメントがHoneycomb APIのアクティビティでホストされていることです。最初のフラグメントを機能させることはできますが、startActivityForResult()を実行する必要がありますが、これは不可能です。startFragmentForResult()の行に沿って、新しいフラグメントを開始できる何かがあり、それが完了すると、前のフラグメントに結果を返しますか?

回答:


57

すべてのフラグメントはアクティビティ内にあります。結果のためにフラグメントを開始することはあまり意味がありません。それを格納するアクティビティは常にそれにアクセスでき、その逆も同様だからです。フラグメントが結果を渡す必要がある場合、そのフラグメントはアクティビティにアクセスして結果を設定し、終了することができます。単一のアクティビティでフラグメントを交換する場合でも、両方のフラグメントからアクティビティにアクセスできます。すべてのメッセージパッシングは単にアクティビティを通過できます。

フラグメントとそのアクティビティの間には常にコミュニケーションがあることを覚えておいてください。結果の開始と終了は、アクティビティ間の通信メカニズムです。アクティビティは、必要な情報をフラグメントに委任できます。


11
さらに、あるフラグメントを別のフラグメントからロードするときに、ターゲットフラグメントを設定し、必要に応じて親フラグメントのonActivityResultメソッドを呼び出すことができます。
PJL

4
あなたの答えは不完全です。フラグメントはアクティビティのようなライフサイクルを使用しています。したがって、メソッドを通じて引数を渡すことはできませんが、値を渡すにはバンドルを使用する必要があります。フラグメントがどこかに値を設定したとしても、彼がいつ終了したかを知る必要があります。前のフラグメントは、開始/再開時に値を取得する必要がありますか?これはアイデアです。しかし、値を格納する適切な方法はありません。フラグメントは、他の複数のフラグメント/アクティビティによって呼び出される可能性があります。
Loenix 2016年

59

必要に応じて、フラグメント間の通信にはいくつかの方法があります。

setTargetFragment(Fragment fragment, int requestCode)
getTargetFragment()
getTargetRequestCode()

これらを使用してコールバックできます。

Fragment invoker = getTargetFragment();
if(invoker != null) {
    invoker.callPublicMethod();
}

確認はしていませんが、動作するかもしれません。したがって、の乱用による循環参照によって引き起こされるメモリリークに注意する必要がありsetTargetFragment()ます。
nagoya0 2012

3
これまでのところ最高の解決策!1つのアクティビティとフラグメントのスタックがあり、通知する適切なフラグメントを見つけようとするのは悪夢のような場合に最適です。ありがとう
ダミアンプラカ2013年

1
@ userSeven7sこれは置換ケースでは機能しませんが、非表示ケースでは機能します
Muhammad Babar 14

1
ターゲットフラグメントにWeakReferenceを使用すると、@ nagoya0のほうがうまくいきます
quangson91

11

フラグメント間で同じViewModelを共有するだけです

SharedViewModel

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel

class SharedViewModel : ViewModel() {

    val stringData: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

}

FirstFragment

import android.arch.lifecycle.Observer
import android.os.Bundle
import android.arch.lifecycle.ViewModelProviders
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class FirstFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        sharedViewModel.stringData.observe(this, Observer { dateString ->
            // get the changed String
        })

    }

}

SecondFragment

import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGrou

class SecondFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        changeString()
    }

    private fun changeString() {
        sharedViewModel.stringData.value = "Test"
    }

}

3
こんにちはレボン、上記のコードは同じビューモデルインスタンスを共有しないと思います。ViewModelProviders.of(this).get(SharedViewModel :: class.java)にthis(fragment)を渡しています。これにより、フラグメントの2つの個別のインスタンスが作成されます。アクティビティViewModelProviders.of(activity).get(SharedViewModel :: class.java)を渡す必要があります
Shailendra Patil

@ShailendraPatilいいキャッチ、今すぐ修正します。
Levon Petrosyan

4

私の2セント。

古いフラグメントを新しいフラグメントと交換して、hideとshow / add(existing / new)を使用してフラグメント間を切り替えます。したがって、この答えは、私と同じようにフラグメントを使用する開発者向けです。

次に、このonHiddenChangedメソッドを使用して、古いフラグメントが新しいフラグメントから元のフラグメントに切り替えられたことを確認します。以下のコードを参照してください。

新しいフラグメントを離れる前に、古いフラグメントによってクエリされるグローバルパラメータに結果を設定しました。これは非常に単純な解決策です。

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (hidden) return;
    Result result = Result.getAndReset();
    if (result == Result.Refresh) {
        refresh();
    }
}

public enum Result {
    Refresh;

    private static Result RESULT;

    public static void set(Result result) {
        if (RESULT == Refresh) {
            // Refresh already requested - no point in setting anything else;
            return;
        }
        RESULT = result;
    }

    public static Result getAndReset() {
        Result result = RESULT;
        RESULT = null;
        return result;
    }
}

getAndReset()方法とは?
EpicPandaForce 2017

onResume()2番目のフラグメントが却下されたときに、最初のフラグメントでも呼び出されませんか?
PJ_Finnegan

4

最近、Googleはちょうどに新しい機能を追加しましたFragmentManager作られたFragmentManagerフラグメントの結果を得るために中央ストアとして機能することができるように。フラグメント間でデータを簡単にやり取りできます。

開始フラグメント。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setResultListener("requestKey") { key, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result...
    }
}

結果を返したいフラグメント。

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

スニペットは、Googleの公式ドキュメントから取得されます。 https://developer.android.com/training/basics/fragments/pass-data-between#kotlin

書かれたこの回答のデータでは、この機能はまだalpha状態にあります。この依存関係を使用して試してみることができます。

androidx.fragment:fragment:1.3.0-alpha05

2

フラグメントでは、getActivity()を呼び出すことができます。これにより、フラグメントを作成したアクティビティにアクセスできるようになります。そこから、customizeメソッドを呼び出して値を設定するか、値を渡すことができます。


1

結果のフラグメントを開始できるAndroidライブラリ-FlowRがあります。

結果のフラグメントを開始します。

Flowr.open(RequestFragment.class)
    .displayFragmentForResults(getFragmentId(), REQUEST_CODE);

処理により、呼び出しフラグメントが生成されます。

@Override
protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
    super.onFragmentResults(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            demoTextView.setText("Result OK");
        } else {
            demoTextView.setText("Result CANCELED");
        }
    }
}

結果をフラグメントに設定します。

Flowr.closeWithResults(getResultsResponse(resultCode, resultData));

1

インターフェイス(およびKotlin)を使用したソリューション。核となるアイデアは、コールバックインターフェイスを定義し、それをアクティビティに実装し、フラグメントから呼び出すことです。

まず、インターフェースを作成しますActionHandler

interface ActionHandler {
    fun handleAction(actionCode: String, result: Int)
}

次に、これを子(この場合はフラグメント)から呼び出します。

companion object {
    const val FRAGMENT_A_CLOSED = "com.example.fragment_a_closed"
}

fun closeFragment() {
    try {
        (activity as ActionHandler).handleAction(FRAGMENT_A_CLOSED, 1234)
    } catch (e: ClassCastException) {
        Timber.e("Calling activity can't get callback!")
    }
    dismiss()
}

最後に、これを親に実装してコールバック(この場合はアクティビティ)を受信します。

class MainActivity: ActionHandler { 
    override fun handleAction(actionCode: String, result: Int) {
        when {
            actionCode == FragmentA.FRAGMENT_A_CLOSED -> {
                doSomething(result)
            }
            actionCode == FragmentB.FRAGMENT_B_CLOSED -> {
                doSomethingElse(result)
            }
            actionCode == FragmentC.FRAGMENT_C_CLOSED -> {
                doAnotherThing(result)
            }
        }
    }

0

データを戻す最も簡単な方法は、setArgument()を使用することです。たとえば、fragment3を呼び出すfragment2を呼び出すfragment1、fragment1-> framgnet2-> fargment3があるとします。

フラグメント1

public void navigateToFragment2() {
    if (fragmentManager == null) return;

    Fragment2 fragment = Fragment2.newInstance();
    String tag = "Fragment 2 here";
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .add(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commitAllowingStateLoss();
}

fragment2では、通常どおりfragment3を呼び出します

private void navigateToFragment3() {
    if (fragmentManager == null) return;
    Fragment3 fragment = new Fragment3();
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .replace(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commit();
}

fragment3でタスクを完了したら、次のように呼び出します。

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
if (fragmentManager == null) return;
fragmentManager.popBackStack();
Bundle bundle = new Bundle();
bundle.putString("bundle_filter", "data");
fragmentManager.findFragmentByTag("Fragment 2 here").setArguments(bundle);

今フラグメント2では、引数を簡単に呼び出すことができます

@Override
public void onResume() {
    super.onResume();
    Bundle rgs = getArguments();
    if (args != null) 
        String data = rgs.getString("bundle_filter");
}

これに注意してください。いくつかのケースで機能する可能性がありますが、フラグメントに元の引数(この例では「フラグメント2」)があった場合、これはフラグメントの作成に使用された元の引数を上書きします。フラグメントは破棄され、再作成されます。
フアン

0

アーキテクチャに応じて実行できるもう1つのことは、フラグメント間で共有ViewModelを使用することです。したがって、私の場合、FragmentAはフォームであり、FragmentBはユーザーがアイテムを検索して選択し、ViewModelに保存できるアイテム選択ビューです。その後、FragmentAに戻ると、情報はすでに保存されています。

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