簡體   English   中英

如果我在 XML 布局中聲明一個片段,如何將它傳遞給 Bundle?

[英]If I declare a fragment in an XML layout, how do I pass it a Bundle?

我有一個用片段替換的活動。 該活動采用了一個 Intent,該 Intent 包含有關該活動應該顯示哪些數據的一些額外信息。

現在我的 Activity 只是一個執行相同工作的 Fragment 的包裝器,如果我在 XML 中聲明帶有標簽的 Fragment,如何將該捆綁包放入 Fragment?

如果我要使用 FragmentTransaction 將 Fragment 放入 ViewGroup,我將有機會在 Fragment 構造函數中傳遞此信息,但我想知道片段在 XML 中定義的情況。

既然我的 Activity 只是一個執行相同工作的 Fragment 的包裝器,那么如果我在 XML 中使用標記聲明片段,我如何將該包放入 Fragment 中?

你不能。

但是,歡迎您在FragmentManager上調用findFragmentById()以檢索膨脹后的片段,然后在片段上調用某些方法以將數據與其關聯。 雖然顯然不能setArguments() ,但您的片段可以通過其他方式( onSaveInstanceState()setRetainInstance(true)等)安排在配置更改后保留數據本身。

這不是封裝的方式,但我最終從父活動“拉”了包:

Bundle bundle = getActivity().getIntent().getExtras();

您不能傳遞 Bundle(除非您以編程方式而不是通過 XML 擴充片段),但您可以通過 XML 將參數(或屬性)傳遞給片段。

該過程類似於您定義 View 自定義屬性的方式 除了 AndroidStudio(目前)在此過程中不會為您提供幫助。

假設這是您使用參數的片段(我將使用 kotlin,但它也完全適用於 Java):

class MyFragment: Fragment() {

    // your fragment parameter, a string
    private var screenName: String? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        if (screenName == null) {
            screenName = arguments?.getString("screen_name")
        }
    }
}

你想做這樣的事情:

<fragment
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/myFragment"
    android:name="com.example.MyFragment"
    app:screen_name="@string/screen_a"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

注意app:screen_name="@string/screen_a"

要使其工作,只需將其添加到值文件中( fragment_attrs.xml或選擇您想要的任何名稱):

<!-- define your attribute name and type -->
<attr name="screen_name" format="string|reference"/>

<!-- define a bunch of constants you wanna use -->
<string name="screen_a" translatable="false">ScreenA</string>
<string name="screen_b" translatable="false">ScreeenB</string>

<!-- now define which arguments your fragment is gonna have (can be more then one) -->
<!-- the convention is "FragmentClassName_MembersInjector" -->
<declare-styleable name="MyFragment_MembersInjector">
    <attr name="screen_name"/>
</declare-styleable>

差不多完成了,你只需要在你的片段中讀取它,所以添加方法:

override fun onInflate(context: Context?, attrs: AttributeSet?, savedInstanceState: Bundle?) {
    super.onInflate(context, attrs, savedInstanceState)
    if (context != null && attrs != null && screenName == null) {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.MyFragment_MembersInjector)
        if (ta.hasValue(R.styleable.MyFragment_MembersInjector_screen_name)) {
            screenName = ta.getString(R.styleable.MyFragment_MembersInjector_screen_name)
        }
        ta.recycle()
    }
}

等等,你的片段中的 XML 屬性 :)

限制:

  • Android Studio(截至目前)不會在布局 XML 中自動完成此類參數
  • 您不能傳遞Parcelable而只能傳遞可以定義為 Android 屬性的內容

另一種選擇是不在 XML 中聲明片段。 我知道這不是你想要做的。 但是,您可以在視圖中聲明一個簡單的布局,如下所示:

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" />

然后在您的Activity類中,您使用片段以編程方式擴展布局。 這樣您就可以使用 args 傳遞參數。

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
MyFragment fragment = MyFragment.newInstance();
Bundle args = new Bundle();
args.putInt(Global.INTENT_INT_ROLE, 1);
fragment.setArguments(args);
fragmentTransaction.add(R.id.fragment_container, fragment, "MyActivity");
fragmentTransaction.commit();

在片段中,

if (getArguments() != null) {
   int role = getArguments().getInt(Global.INTENT_INT_ROLE); }

這種方法不像在 xml 中聲明它那么干凈和簡單,但是我已經轉向它,因為它可以讓您更好地控制片段。

我知道答案為時已晚,但我認為有人需要:)

只是在活動覆蓋onAttachFragment()

@Override
public void onAttachFragment(Fragment fragment)
{
    super.onAttachFragment(fragment);

    if (fragment.getId() == R.id.frgBlank)
    {
        Bundle b = new Bundle();
        b.putString("msg", "Message");

        fragment.setArguments(b);
    }
}

並在片段 onCreateView 方法中

Bundle b = getArguments();
if (b != null)
{
    Toast.makeText(getBaseContext(), b.getString("msg"), Toast.LENGTH_SHORT).show();
}

我看到的唯一解決方案是不使用參數作為數據交換通道。 相反,讓您的片段從其他地方獲取必要的信息。 回調以獲取適當的活動,查詢臨時存儲內存,單例對象等。

另一個有用的解決方案是使用框架,允許不相關的對象通過 Mediator 設計模式交換消息,如Otto

這種方法對我有用。

您不會從任何地方傳遞 Bundle,而是可以在片段本身的 onAttach 方法中設置 arguments。

稍后在片段的生命周期方法中,您可以使用這些捆綁包 arguments。

override fun onAttach(context: Context) {
        super.onAttach(context)
        if(arguments == null){
            val bundle = Bundle()
            bundle.putString("mykey", "myvalue")
            arguments = bundle
        }
    }

有人可能會問,為什么要在片段中設置arguments,而我們可以直接在可用的地方使用這些值。 沒錯,但是當您將這些 arguments 傳遞給其他一些類時,這種方法也可以工作,比如說任何視圖 model。

例如

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

        /*
         Here I am passing these arguments to a viewmodel 
        */
        viewModel.prepareData(arguments) 
        
        --------
        --------
        --------

    }

謝謝。

androidx.fragment模塊(2021 年 2 月)的 v1.3.0 起,他們棄用了onAttachFragment()回調,轉而使用附加到您的FragmentManagerFragmentOnAttachListener 因此,在您的FragmentActivityonCreate中,您可以執行以下操作:

getSupportFragmentManager().addFragmentOnAttachListener((fragmentManager, fragment) -> {
    if (fragment instanceof MyFragment) {
        Bundle args = getIntent().getExtras();
        fragment.setArguments(args);
    }
}

如果您在 XML 中使用<fragment> ,這將在setContentView()之后觸發,但如果您使用<FragmentContainerView> ,則稍后會發生。

請小心,因為我注意到配置更改可能會重新附加super.onCreate中的現有 Fragment,因此需要在此之前添加偵聽器。 (也就是說,這對於傳遞Bundle參數不是必需的,否則應該首先嘗試使用onSaveInstanceState() 。)


順便說一句,如果需要,如果需要在 Fragment 的視圖實例化后在 Activity 中執行任何操作,監聽“onAttach”也可能是一個方便的地方,可以添加 Fragment 的視圖 生命周期的觀察者。 例如添加:

        LiveData<LifecycleOwner> liveData = fragment.getViewLifecycleOwnerLiveData();
        // Let this observer be constrained to the fragment's lifecycle.
        liveData.observe(fragment, new Observer<LifecycleOwner>() {
            @Override
            public void onChanged(LifecycleOwner lifecycleOwner) {
                // ...do work...
                // If you don't need to listen anymore, can clean ourselves up early.
                liveData.removeObserver(this);
            }
        });

或者作為從 Activity 觀察 Fragment 的視圖生命周期的替代方法(並且作為 Fragment 已棄用的onActivityCreated()的替代品),您可以用另一種方式從 Fragment 監聽 Activity 的生命周期! 在 Fragment 的onAttach(context)回調中,添加您自己的實現onCreate(@NonNull LifecycleOwner owner)觀察者 re: onActivityCreated 已棄用,如何正確使用 LifecycleObserver?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM