Fragment replacing in RecyclerView item

19,102

Solution 1

I finnaly found solution. The problem is I set a common container id. But in recycler view need to set unique container id for each item.

So, my code now this:

MyFragment fragment = MyFragment.newInstance("fragment1");
fragmentManager.beginTransaction().replace(UNIQUE_CONTAINER_ID, fragment).commit();

If someone will be useful, here is my complete code (implementation fragment in recycler view):

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 
{

...

// Delete old fragment
int containerId = holder.mediaContainer.getId();// Get container id
Fragment oldFragment = fragmentManager.findFragmentById(containerId);
if(oldFragment != null) {
    fragmentManager.beginTransaction().remove(oldFragment).commit();
}

int newContainerId = View.generateViewId();// Generate unique container id
holder.mediaContainer.setId(newContainerId);// Set container id

// Add new fragment
MyFragment fragment = MyFragment.newInstance("fragment1");
fragmentManager.beginTransaction().replace(newContainerId, fragment).commit();

...

}

Upd.: Instead of using your own method to generate a unique id, it is recommended to use View.generateViewId()

Solution 2

Thanks to Mikhali, I'm able to provide to you a complete running example. Pay special attention on the comments in onBindViewHolder()

    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewLedgerAdapter.ViewHolder>{
        private final String TAG = RecyclerViewAdapter.class.getSimpleName();

        private final float FINAL_OPACITY = 0.3f;
        private final float START_OPACITY = 1f;

        private final int ANIMATION_TIME = 500;
        private final int TYPE_ITEM = 0;
        private final int TYPE_DATE = 1;
        private final int TYPE_TRANSACTION = 2;
        private final int TYPE_PENDING = 3;

        private HashMap<Integer, Integer> mElementTypes;
        private List<Operation> mObjects;
        private Context mContext;
        private Utils.CURRENCIES mCurrencySelected; // Which currency is already selected
        private boolean mCurrencyFilter; // Defines if a currency is already selected to apply filter
        private Animation mAnimationUp;
        private Animation mAnimationDown;

        public RecyclerViewLedgerAdapter(List<Operation> objects, Context context) {
            mElementTypes = new HashMap<Integer, Integer>();
            mObjects = objects;
            mContext = context;
            mCurrencyFilter = false;
            mCurrencySelected = null;
            mAnimationUp = AnimationUtils.loadAnimation(context, R.anim.slide_up);
            mAnimationDown = AnimationUtils.loadAnimation(context, R.anim.slide_down);
        }

        ...
        ...
            Not needed methods
        ...
        ...

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_element_ledger, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, final int position) {
            Operation operation = mObjects.get(position);
            holder.setAppUserActivity(userActivityOperation);

            // Remember that RecyclerView does not have onClickListener, you should implement it
            holder.getView().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // Hide details
                    // iDetailsContainer object could be checked on inner class ViewHolder
                    if(holder.iDetailsContainer.isShown()){
                        holder.iDetailsContainer.setVisibility(View.GONE);
                    }else{
                        // Show details
                        // Get fragment manager inside our fragment
                        FragmentManager fragmentManager = ((UserActivity)mContext).getSupportFragmentManager();

                        // Delete previous added fragment
                        int currentContainerId = holder.iDetailsContainer.getId();
                        // Get the current fragment
                        Fragment oldFragment = fragmentManager.findFragmentById(currentContainerId);
                        if(oldFragment != null) {
                            // Delete fragmet from ui, do not forget commit() otherwise no action
                            // is going to be observed
                            ragmentManager.beginTransaction().remove(oldFragment).commit();
                        }

                        // In order to be able of replacing a fragment on a recycler view
                        // the target container should always have a different id ALWAYS
                        int newContainerId = getUniqueId();
                        // Set the new Id to our know fragment container
                        holder.iDetailsContainer.setId(newContainerId);

                        // Just for Testing we are going to create a new fragment according
                        // if the view position is pair one fragment type is created, if not
                        // a different one is used
                        Fragment f;
                        if(position%2 == 0) {
                            f = new FragmentCard();
                        }else{
                            f=new FragmentChat();
                        }

                        // Then just replace the recycler view fragment as usually
                        manager.beginTransaction().replace(newContainerId, f).commit();

                        // Once all fragment replacement is done we can show the hidden container
                        holder.iDetailsContainer.setVisibility(View.VISIBLE);
                    }
                }

                // Method that could us an unique id
                public int getUniqueId(){
                    return (int)SystemClock.currentThreadTimeMillis();
                }
            });
        }


        public class ViewHolder extends RecyclerView.ViewHolder{
            private View iView;
            private LinearLayout iContainer;
            public LinearLayout iDetailsContainer;
            private ImageView iOperationIcon;
            private ImageView iOperationActionImage;
            private TextView iOperation;
            private TextView iAmount;
            private TextView iTimestamp;
            private TextView iStatus;

            private UserActivityOperation mUserActivityOperation;

            public ViewHolder(View itemView) {
                super(itemView);
                iView = itemView;
                iContainer = (LinearLayout) iView.findViewById(R.id.operation_container);
                iDetailsContainer = (LinearLayout) iView.findViewById(R.id.details_container);
                iOperationIcon = (ImageView) iView.findViewById(R.id.ledgerOperationIcon);
                iOperationActionImage = (ImageView) iView.findViewById(R.id.ledgerAction);
                iOperation = (TextView) iView.findViewById(R.id.ledgerOperationDescription);
                iAmount = (TextView) iView.findViewById(R.id.ledgerOperationCurrencyAmount);
                iTimestamp = (TextView) iView.findViewById(R.id.ledgerOperationTimestamp);
                iStatus = (TextView) iView.findViewById(R.id.ledgerOperationStatus);

                // This linear layout status is GONE in order to avoid the view to use space
                // even when it is not seen, when any element selected the Adapter will manage the
                // behavior for showing the layout - container
                iDetailsContainer.setVisibility(View.GONE);
            }

            ...
            ...
                Not needed methods
            ...
            ...
        }
    }

Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/operation_container_maximum"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="11dp"
    android:layout_marginLeft="30dp"
    android:layout_marginRight="30dp"
    android:layout_marginTop="11dp"
    android:orientation="vertical">


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

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="14dp">

            <ImageView
                android:id="@+id/ledgerOperationIcon"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:src="@drawable/fondear" />

            <ImageView
                android:id="@+id/ledgerAction"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom|right"
                android:src="@drawable/operation_trade" />


        </FrameLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical"
            android:weightSum="2">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/ledgerOperationDescription"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="Descripcion"
                    android:textColor="@color/ledger_desc" />

                <TextView
                    android:id="@+id/ledgerOperationCurrencyAmount"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="2dp"
                    android:text="5000 BTC" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/ledgerOperationTimestamp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="Fecha/Hora"
                    android:textColor="@color/ledger_timestamp" />

                <TextView
                    android:id="@+id/ledgerOperationStatus"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Status" />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>


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



        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="Something hidden" />

        <ImageView
            android:layout_marginTop="15dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/user_btc"
            android:layout_gravity="center_horizontal"/>

    </LinearLayout>

</LinearLayout>

Fragment

    // This is one of the fragments used in the RecyclerViewAdapterCode, and also makes a HTTPRequest to fill the
    // view dynamically, you could laso use any of your fragments.
    public class FragmentCard extends Fragment {

        TextView mTextView;

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

        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_card, container, false);

            mTextView = (TextView) view.findViewById(R.id.tv_fragment_two);
            new UserActivityAsyncTask().execute();
            return view;
        }

        private UserActivityOperation[] asyncMethodGetPendingWithdrawals(){
            BitsoWithdrawal[] userWithdrawals = HttpHandler.getUserWithdrawals(getActivity());
            int totalWithDrawals = userWithdrawals.length;
            UserActivityOperation[] appUserActivities = new UserActivityOperation[totalWithDrawals];
            for(int i=0; i<totalWithDrawals; i++){
                appUserActivities[i] = new UserActivityOperation(userWithdrawals[i], getActivity());
            }
            return appUserActivities;
        }

        private class UserActivityAsyncTask extends AsyncTask<String, Void, Integer> {
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
            }

            @Override
            protected Integer doInBackground(String... strings) {
                // Precess Compound balance
                UserActivityOperation[] compoundBalanceProcessed = asyncMethodGetPendingWithdrawals();
                return compoundBalanceProcessed.length;
            }

            @Override
            protected void onPostExecute(Integer result) {
                super.onPostExecute(result);
                mTextView.setText(result.toString());
            }
        }
    }
Share:
19,102
Mikhail
Author by

Mikhail

Updated on July 16, 2022

Comments

  • Mikhail
    Mikhail almost 2 years

    In my RecyclerView I need replace part of my item to my fragment. But replacing only first item in recycler view. What I am doing is wrong?

    My container (in recycler view item):

    ...
    <FrameLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:id="@+id/container" />
    ...
    

    My update code in RecyclerView adapter:

    ...
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    
    ...
    
    MyFragment fragment = MyFragment.newInstance("fragment1");
    fragmentManager.beginTransaction().replace(R.id.container, fragment).commit();
    
    ...
    
    }
    ...
    
  • AkhilGite
    AkhilGite about 7 years
    Can you please explain me what is GetUniqueID();
  • AkhilGite
    AkhilGite about 7 years
    I have replace this line int newContainerId = GetUniqueID();// My method by int newContainerId = 111 + (int)(Math.random() * 9999);
  • Mikhail
    Mikhail about 7 years
    @AkhilGite Yes, it will work. You can also use timestamp (to ensure that the Id will not be repeated).
  • AkhilGite
    AkhilGite about 7 years
    But still m getting error. Its not working as expected.
  • Maher Nabil
    Maher Nabil about 7 years
    man, you saved me. I've been searching for a solution for 3 days. thanks.
  • Damia Fuentes
    Damia Fuentes almost 7 years
    How can you put and if inside onBindViewHolder?!!! Then things start going slow...
  • nimi0112
    nimi0112 over 6 years
    Hey! thanks for your amazing solution. I am able to do what I wanted but just facing 1 problem i.e I am getting 'Resources$NotFoundException' only on the last item in Recycler view note that it is coming on the last one. I have tried increasing and decreasing the number of items in rc view but the result is the same. Can you help me out please. ?
  • vicco
    vicco over 6 years
    @nimi0112 In order to help you I need to know when you are getting this exception. I think there could be more than one place where you can get it. 1. Maybe you are not inflating correctly the view, so the layout element are not found. 2. Maybe the resource you are trying to set or find is not found so please just check if you resource is available. In order to help you please submit your stack trace if possible or please share where in the code the error is occurring.
  • nimi0112
    nimi0112 over 6 years
  • nimi0112
    nimi0112 over 6 years
    Hey! bro. I have made a sample app separated out of the main project can you help me out in this. Please give your email id so that I can send you the source code of sample app.
  • vicco
    vicco over 6 years
  • vicco
    vicco over 6 years
    I have seen this reference stackoverflow.com/questions/48202131/… as far I can see as the new view needs to get another id, you need to create it and then assign it to the view you are creating. In the example code check this line ---- holder.iDetailsContainer.setId(newContainerId); ---- after that the error should not appear any more.
  • nimi0112
    nimi0112 over 6 years
    Thank you for your response buddy, but I already solved it a week/10 days ago.
  • vivek
    vivek over 3 years
    Hi @Mikhail i am not able to achieve the same thing. Its gives me view id not found after implemented in same way!
  • drmrbrewer
    drmrbrewer almost 3 years
    This is not working for me. I'm getting No view found for id 0xd (unknown) for fragment TitleFragment after onBindViewHolder() completes... note that it's for id 0xd which is a valid id, generated by View.generateViewId() and assigned to the mediaContainer view as per the suggested code, but for some reason it is unknown immediately after onBindViewHolder() completes.
  • sweet_vish
    sweet_vish over 2 years
    After scrolling, app gets crashes with error....No view found for id 0xd (unknown) for fragment ..........i have done same implementation