Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add fragment in RecyclerView.ViewHolder

I have a RecyclerView.ViewHolder which will add different fragment into its FrameLayout based on the instance of the object passed. The problem comes where it is almost impossible to add fragment into the ViewHolder. Take note that I already passed the FragmentManager from the parent. Initially I try with this code

public void setSomething(boolean A) {
    if (A) {
         mFragmentManager.beginTransaction()
            .replace(mBinding.typeContainerLayout.getId(), new FragmentA())
            .commit();
    } else {
        mFragmentManager.beginTransaction()
            .replace(mBinding.typeContainerLayout.getId(), new FragmentB())
            .commit();
    }
}

The problem with this code is that all the ViewHolder share the same id, thus only a single ViewHolder can add the fragment. In my RecyclerView, only the first cell added the fragment. To tackle this problem, I create another FrameLayout and add it into typeContainerLayout. Now my code become like this.

public void setSomething(boolean A) {
    FrameLayout frameLayout = new FrameLayout(mContext);
    frameLayout.setId(View.generateViewId());
    mBinding.typeContainerLayout.removeAllViews();
    mBinding.typeContainerLayout.addView(frameLayout)

    if (A) {
         mFragmentManager.beginTransaction()
            .replace(frameLayout.getId(), new FragmentA())
            .commit();
    } else {
        mFragmentManager.beginTransaction()
            .replace(frameLayout.getId(), new FragmentB())
            .commit();
    }
}

Now each ViewHolder has added the fragment correctly and has their own fragment. However the problem comes when I added like 5 ViewHolder and trying to scroll down the RecyclerView, a runtime error occurred which state

java.lang.IllegalArgumentException: No view found for id 0x4 (unknown) for fragment FragmentA{7c55a69 #0 id=0x4 FragmentA}
                      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1292)
                      at android.support.v4.app.FragmentManagerImpl.moveFragmentsToInvisible(FragmentManager.java:2323)
                      at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2136)
                      at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2092)
                      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1998)
                      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:709)
                      at android.os.Handler.handleCallback(Handler.java:739)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:148)
                      at android.app.ActivityThread.main(ActivityThread.java:5417)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

My guess is that either the id conflicted at some point, or the view got destroyed due to the ViewHolder pattern. So my question is that.

1) Is there any workaround to it?

2) Is there any better practice than adding fragment. The reason I add fragment is so that the logic for the sub item of the ViewHolder can all be located in a single fragment. Of course I can just put both the views for the fragments into the ViewHolder xml. And just setVisible() depending on the condition. But that will just make my ViewHolder contain too many logic.

In case someone is confused why I need fragment. This is what I am trying to achieve. The image

like image 856
Ginsan Avatar asked Feb 13 '17 13:02

Ginsan


2 Answers

You should know exactly that recyclerview created your holder and drew a view for him, so you need to attach a fragment in onViewAttachedToWindowmethod of your adapter. In your "attaching fragment" method you should check if fragment manager already contains those fragments to prevent of creating multiple instances.

Adapter:

 override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {    
    if(holder is FragmentsHolder){
        attachFragmentToContainer(holder.flContainer)            
    }
    super.onViewAttachedToWindow(holder)
}

Method realization:

 fun attachFragmentToContainer(containerId: Int) {
    val fragment = if (fragmentManager?.fragments?.firstOrNull { it is YourFragment } == null)
        YourFragment.instance()
    else
        null

    if (fragment != null) {
        fragmentManager?.beginTransaction()
            ?.add(containerId, fragment)
            ?.commitNowAllowingStateLoss()
    }
}

This tested on big amount of users - no crashes, good perfomance.

like image 53
Artur Antonyan Avatar answered Nov 07 '22 10:11

Artur Antonyan


Short answer: you shouldn't use fragments inside a recyclerView, that's not what they're intended for.

Long answer: here

like image 4
Hanan Rofe Haim Avatar answered Nov 07 '22 10:11

Hanan Rofe Haim