Search code examples
androidandroid-orientationandroid-recyclerview

Android: fragments with RecyclerView orientation change error


I am using Sliding tabs in my app and I have 2 fragments with a view pager.Inside a fragment there is a recycler view also.

Categories fragment

public class Categories extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    if (cat == null) {
        cat = inflater.inflate(R.layout.categories, container, false);
    } else {
        ((ViewGroup) cat.getParent()).removeAllViews();
    }
     catData = new ArrayList<>();

    if(mRecyclerView == null) {
        mRecyclerView = (RecyclerView) cat.findViewById(R.id.recyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
    }else{
        ((ViewGroup) mRecyclerView.getParent()).removeAllViews();
    }
    return mRecyclerView;

}

 Handler handler = new Handler();

Runnable mStatusChecker = new Runnable() {
    @Override
    public void run() {
        GetCategoriesTask access = new GetCategoriesTask();
        access.execute();
        //this function can change value of mInterval.
        handler.postDelayed(mStatusChecker, 10000);
    }
};


private class GetCategoriesTask extends AsyncTask<Void, Void, ArrayList<CategoryData>> {

    @Override
    protected void onPreExecute() {

    }

    //task is running in background
    @Override
    protected ArrayList<CategoryData> doInBackground(Void... arg0) {

        ServiceHandler sh = new ServiceHandler();
        String baseAddress = "url";
        String jsonStr = sh.makeServiceCall(ServiceHandler.GET,baseAddress);

        if(jsonStr != null){
            try{
                JSONObject jsonObj = new JSONObject(jsonStr);
                JSONArray data = jsonObj.getJSONArray("data");

                catData.clear();
                for(int i = 0;i < data.length();i++){
                    JSONObject c = data.getJSONObject(i);
                    String categoryName = c.getString("category");

                    catData.add(new CategoryData(categoryName));
                }
                return catData;

            }catch (JSONException ex){
                ex.printStackTrace();
            }
        }

        Log.d("Response: ", "> " + jsonStr);
        return null;
    }

    //after executing background task, this method will execute.
    @Override
    protected void onPostExecute(ArrayList<CategoryData> data) {

        if(!notifier) {
            adapter = new CategoryRecyclerViewAdapter(getContext(), data);
            mRecyclerView.setAdapter(adapter);
            notifier = true;
        }
        else{                
                adapter.notifyDataSetChanged();
                Toast.makeText(getContext(), "changed", Toast.LENGTH_SHORT).show();
        }
    }
}

@Override
public void onResume() {
    super.onResume();     
    mStatusChecker.run();

}
@Override
public void onSaveInstanceState(final Bundle outState) {
    // super.onSaveInstanceState(outState);
}

@Override
public void onPause() {
    super.onPause();
    handler.removeCallbacks(mStatusChecker);

}

@Override
public void onDestroy() {
    super.onDestroy();
    handler.removeCallbacks(mStatusChecker);

}
@Override
public void onStop() {
    super.onStop();        
    handler.removeCallbacks(mStatusChecker);
}}

Discover Fragment

I have only the oncreate method.

public class Discover extends Fragment{

public View v;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    if (v == null) {
        v = inflater.inflate(R.layout.categories, container, false);
    } else {
        ((ViewGroup) v.getParent()).removeAllViews();
    }
    return v;
}

The problem is when I change the orientation of the app it gives an exception and displays the message "unfortunately ezycity has stopped".When I click the ok button app restarts again and work normally.

logcat

08-26 10:17:25.954    4452-4452/com.ezycity.seneru.ezycity E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.ezycity.seneru.ezycity, PID: 4452
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ezycity.seneru.ezycity/com.ezycity.seneru.ezycity.MainActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390)
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3950)
        at android.app.ActivityThread.access$900(ActivityThread.java:151)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5257)
        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:903)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
 Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.view.ViewGroup.addViewInner(ViewGroup.java:3936)
        at android.view.ViewGroup.addView(ViewGroup.java:3786)
        at android.support.v4.view.ViewPager.addView(ViewPager.java:1342)
        at android.view.ViewGroup.addView(ViewGroup.java:3727)
        at android.view.ViewGroup.addView(ViewGroup.java:3700)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1032)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1197)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1179)
        at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1991)
        at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:165)
        at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:507)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1236)
        at android.app.Activity.performStart(Activity.java:6006)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2288)

So how I solve this problem?


Solution

  • So lets take a look at this piece of code:

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup    container, @Nullable Bundle savedInstanceState) {
    
        if (cat == null) {
            cat = inflater.inflate(R.layout.categories, container, false);
        } else {
            (   (ViewGroup) cat.getParent()).removeAllViews();
        }
         catData = new ArrayList<>();
    
        if(mRecyclerView == null) {
            mRecyclerView = (RecyclerView) cat.findViewById(R.id.recyclerView);
            mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        }else{
            ((ViewGroup) mRecyclerView.getParent()).removeAllViews();
        }
        return mRecyclerView;
    
    }
    

    At first you are inflating cat which is not attatched to the root, due to the last parameter. That is correct.

    Then you are searching for mRecyclerView in cat. This also means the parent of mRecyclerView is cat.

    After that you are returning mRecyclerView. So the pager is trying to add mRecyclerView to a page, but mRecyclerView already has a parent (cat).


    So the solution for this fragment might be:

    instead of:

     return mRecyclerView;
    

    use:

    return cat;