2

In my RecyclerView I need replace part of my item to my fragment. I have followed this answer by Victor Cruz and I am able to achieve what I wanted.

Everything is working fine but I am facing one serious problem i.e I am getting Resources$NotFoundException Unable to find resource ID only in the last item of my RecyclerView, note that this problem is occurring only in the last item rest others are working fine.

Steps which I have tried:

  1. I tried to look for the resource ID in R.java file but all in vain.

  2. I have tried reducing and increasing the number of items in RecyclerView but the problem is still the same.

  3. Searched other relevant answers like assigning the same id to parent layout as passing in replace fragment.
  4. Tried catching the Exception but failed because I think it is not able to find the layout file in on create only in the case of last item.
  5. Checked if it is happening due to any type casting errors.

Please give suggestions where am I doing wrong. I will be happy to provide any other relevant details.

After 5 days of posting the Question (and struggling with this for a week), I am not able to figure problem out. I have made a small sample app performing this particular task, You can download the code from here.

Please help me out of this.

Edit: Posting code:

private void flipcard(final RecyclerView.ViewHolder holder)
{
    final MyHolder myHolderflipcard= (MyHolder) holder;
            // Delete old fragment
            int containerId = myHolderflipcard.container.getId();// Get container id
            Fragment oldFragment = ((FragmentActivity) context).getFragmentManager().findFragmentById(containerId);
            if(oldFragment != null)
            {
                ((FragmentActivity) context).getFragmentManager().beginTransaction().remove(oldFragment).commit();
            }
            int newContainerId = getUniqueId();
            // Set the new Id to our know fragment container
            myHolderflipcard.container.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;
                f = new CardBackFragment();
                // Then just replace the recycler view fragment as usually
                ((FragmentActivity) context).getFragmentManager().beginTransaction()
                        .setCustomAnimations(
                                R.animator.card_flip_right_in,
                                R.animator.card_flip_right_out,
                                R.animator.card_flip_left_in,
                                R.animator.card_flip_left_out)
                        .addToBackStack(null)
                        .replace(newContainerId, f).commit();

                myHolderflipcard.cardView.setVisibility(View.GONE);
            }
}
// Method that could us an unique id
private int getUniqueId(){
    return (int)
            SystemClock.currentThreadTimeMillis();
}

Here is my logcat if it can be of any use.

E/UncaughtException: android.content.res.Resources$NotFoundException: Unable to find resource ID #0x1678
                                                                              at android.content.res.Resources.getResourceName(Resources.java:2209)
                                                                              at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:886)
                                                                              at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067)
                                                                              at android.app.BackStackRecord.run(BackStackRecord.java:834)
                                                                              at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1452)
                                                                              at android.app.FragmentManagerImpl$1.run(FragmentManager.java:447)
                                                                              at android.os.Handler.handleCallback(Handler.java:739)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                              at android.os.Looper.loop(Looper.java:135)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5292)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                              at java.lang.reflect.Method.invoke(Method.java:372)
                                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

E/AndroidRuntime: FATAL EXCEPTION: main
                                                                       Process: **app package name //intentionally written**, PID: 3136
                                                                       android.content.res.Resources$NotFoundException: Unable to find resource ID #0x1678
                                                                           at android.content.res.Resources.getResourceName(Resources.java:2209)
                                                                           at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:886)
                                                                           at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1067)
                                                                           at android.app.BackStackRecord.run(BackStackRecord.java:834)
                                                                           at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1452)
                                                                           at android.app.FragmentManagerImpl$1.run(FragmentManager.java:447)
                                                                           at android.os.Handler.handleCallback(Handler.java:739)
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                           at android.os.Looper.loop(Looper.java:135)
                                                                           at android.app.ActivityThread.main(ActivityThread.java:5292)
                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                           at java.lang.reflect.Method.invoke(Method.java:372)
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)
nimi0112
  • 2,065
  • 1
  • 18
  • 32
  • Add your code snippet . – ADM Jan 11 '18 at 07:56
  • you are trying to access those id which are not in your xm or else define in other xmll. check it once. – Hemant Parmar Jan 11 '18 at 07:57
  • @ADM: code of back fragment or from where I am calling the fragment? – nimi0112 Jan 11 '18 at 07:59
  • How would i know that . Add the whole code block which is crashing . Debug and find out .. – ADM Jan 11 '18 at 08:01
  • @HemantParmar I have crosschecked all my is's which I am calling and also the type of resource which I am setting in them i.e typecasting it to string before setting them in textview and all others but point to note is that it is failing only in the last item of rc view. – nimi0112 Jan 11 '18 at 08:01
  • @ADM please check now – nimi0112 Jan 11 '18 at 08:06
  • @ADM: I have made a sample app separate from the main project. Can you give your email id please so that I can send you the source code and see if you can help me out in this. – nimi0112 Jan 12 '18 at 05:09

2 Answers2

6

This problem is interesting. After debugging your sample app for a while this is my finding:

  • Theresource id that is missing is the id of created fragment - CardBackFragment.
  • The resource id is given dynamically after you replace and commit the fragment

For prof of what I have just said, here is a screenshot when I was debugging your app notice the id that is shown: enter image description here And here is the missing id that the debugger told it was missing: enter image description here

And to be clear, the newContainerId tis translated into hex id that is here (sorry for my bad english)

So what happen here?

The answer lies in the way the code is execute the line: myHolderflipcard.cardView.setVisibility(View.GONE); That was trigger after you commit the fragment to be shown.

Here is what happen: When you told the card view to be gone, the last item is removed from ui -> Because it is removed from ui and it is the last item on the recycler view -> the height of the recycler view is shorten to minimize the view. The bug happen for the last item because the recycler view understand that the row layout that hold the question is empty and it is the last item -> the last item is instead transfer to the question row above. Meanwhile, the thread that insert the fragment into your framelayout is not done. So when it's done and it try to find the containerid, it cannot find it. Hence, the crash.

So the way to fix it is to wait for the frame to be added completely then you remove the question

Here is the fix:

  • Remove myHolderflipcard.cardView.setVisibility(View.GONE); line from your flipcard method

  • On the outside create a: private MyHolder curHolder;

  • Create a runnable to hide the CardView:

    private Handler handler = new Handler();
    
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
           Log.d("mId", String.valueOf(curHolder.container.getId()));
            curHolder.cardView.setVisibility(View.GONE);
    

    // handler.postDelayed(this, 500); } };

  • post it after the commit is done:

            Fragment f;
            f = new CardBackFragment();
            // Then just replace the recycler view fragment as usually
            ((FragmentActivity) context).getFragmentManager().beginTransaction()
                    .setCustomAnimations(
                            R.animator.card_flip_right_in,
                            R.animator.card_flip_right_out,
                            R.animator.card_flip_left_in,
                            R.animator.card_flip_left_out)
                    .addToBackStack(null)
                    .replace(newContainerId, f).commit();
            // Once all fragment replacement is done we can show the hidden container
            handler.post(runnable);
    

Although it happens really fast. You can use handler.postDelayed(runnable, 100); instead if you want to ensure that the fragment is successfully replaced under any circumstances

And here is the full code (since I'm really bad at english, so I post it just in case)

    private void flipcard(final RecyclerView.ViewHolder holder)
    {
        final MyHolder myHolderflipcard= (MyHolder) holder;

        String nim=mysr_id.get(Integer.parseInt(mpref.getradio_button_value()));
        Pattern pattern = Pattern.compile("[0-9]+");
        Matcher matcher = pattern.matcher(nim);
        if (matcher.find())
        {
            currentsrid=Integer.parseInt(matcher.group(0));

            if (currentsrid!=flag)
            {
                flag = Integer.parseInt(matcher.group(0));
                // Delete old fragment
                int containerId = myHolderflipcard.container.getId();// Get container id
                Fragment oldFragment = ((FragmentActivity) context).getFragmentManager().findFragmentById(containerId);
                if(oldFragment != null)
                {
                    ((FragmentActivity) context).getFragmentManager().beginTransaction().remove(oldFragment).commit();
                }
                int newContainerId = 0;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    newContainerId = View.generateViewId();
                }

                // Set the new Id to our know fragment container

                myHolderflipcard.container.setId(newContainerId);

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    curHolder = myHolderflipcard;
                }


                // 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;
                    f = new CardBackFragment();
                    // Then just replace the recycler view fragment as usually
                    ((FragmentActivity) context).getFragmentManager().beginTransaction()
                            .setCustomAnimations(
                                    R.animator.card_flip_right_in,
                                    R.animator.card_flip_right_out,
                                    R.animator.card_flip_left_in,
                                    R.animator.card_flip_left_out)
                            .addToBackStack(null)
                            .replace(newContainerId, f).commit();
                    // Once all fragment replacement is done we can show the hidden container
                    handler.post(runnable);

                    //myHolderflipcard.container.setVisibility(View.VISIBLE);
                    //myHolderflipcard.radioGroup.setVisibility(View.GONE);
                    //myHolderflipcard.tvQuestion.setVisibility(View.GONE);
//                    myHolderflipcard.cardView.setVisibility(View.GONE);
                }

            }else
            {
               // backtoorignal=false;
                // ((FragmentActivity)context). getFragmentManager().popBackStack();
            }

        }

    }

    private MyHolder curHolder;
    private Handler handler = new Handler();

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
           Log.d("mId", String.valueOf(curHolder.container.getId()));
            curHolder.cardView.setVisibility(View.GONE);
        }
    };
Vũ Thành Tâm
  • 442
  • 2
  • 8
  • 2
    Thanks a lot bro for solving my problem with patience and understanding it logically. I will always be highly grateful to you. Well, I would like to remain in touch. Let me know if you are interested in it too. – nimi0112 Jan 16 '18 at 05:29
  • 1
    Thanks for the high regards but I found the solution only by chance. So you don't have to do that. ^^ – Vũ Thành Tâm Jan 16 '18 at 06:21
1

Just check small thing.
It might not be the cause but normally it happens when setText on Textview with Integer(number).
like bellow

int a = 999;    
myTextView.setText(a);`

so I assume that you use setText call with number inside CardBackFragment

Soo Chun Jung
  • 595
  • 3
  • 14
  • Please check my point number 5. I already checked for any typecasting errors. Vũ Thành Tâm's solution solved my problem. – nimi0112 Jan 16 '18 at 05:31