10

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();

...

}
...
Mikhail
  • 2,612
  • 3
  • 22
  • 37
  • I would suggest against using a fragment inside of RecyclerView item( I am assuming that it is possible, but also assuming it is bad practice). I would handle all of the views inside of the ViewHolder. – Eugene H May 12 '16 at 22:16

2 Answers2

22

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()

Mikhail
  • 2,612
  • 3
  • 22
  • 37
  • Can you please explain me what is GetUniqueID(); – AkhilGite Feb 04 '17 at 11:09
  • I have replace this line int newContainerId = GetUniqueID();// My method by int newContainerId = 111 + (int)(Math.random() * 9999); – AkhilGite Feb 04 '17 at 11:12
  • @AkhilGite Yes, it will work. You can also use timestamp (to ensure that the Id will not be repeated). – Mikhail Feb 04 '17 at 14:43
  • But still m getting error. Its not working as expected. – AkhilGite Feb 06 '17 at 06:18
  • 1
    man, you saved me. I've been searching for a solution for 3 days. thanks. – Maher Nabil Mar 19 '17 at 10:16
  • How can you put and if inside onBindViewHolder?!!! Then things start going slow... – Damia Fuentes Jul 23 '17 at 11:59
  • 2
    Hi @Mikhail i am not able to achieve the same thing. Its gives me view id not found after implemented in same way! – vivek Aug 15 '20 at 08:00
  • 2
    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. – drmrbrewer Jul 06 '21 at 09:42
  • After scrolling, app gets crashes with error....No view found for id 0xd (unknown) for fragment ..........i have done same implementation – sweet_vish Oct 11 '21 at 15:20
  • Hi @sweet_vish did you get any solution for "No View found" crash – Zeeshan Ali Jun 21 '23 at 06:55
12

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());
            }
        }
    }
vicco
  • 181
  • 2
  • 7
  • 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. ? – nimi0112 Jan 11 '18 at 07:01
  • @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. – vicco Jan 12 '18 at 01:12
  • have a look here https://stackoverflow.com/questions/48202131/android-content-res-resourcesnotfoundexception-unable-to-find-resource-id?noredirect=1#comment83385403_48202131 – nimi0112 Jan 12 '18 at 01:30
  • 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. – nimi0112 Jan 12 '18 at 05:07
  • Sure victor.cruz.isc@gmail.com – vicco Jan 26 '18 at 21:22
  • 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. – vicco Jan 26 '18 at 21:25
  • Thank you for your response buddy, but I already solved it a week/10 days ago. – nimi0112 Jan 27 '18 at 06:24