4

I have an activity which opens a fragment with view pager. when I change between the tabs I get this error: The specified child already has a parent. You must call removeView() on the child's parent first.

I think is the problem is from the header of the RecyclerView and I posted a new question about it here

the view pager code is:

private void mViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        adapter.addFragment(new a_Frag(), getString("A"));
        adapter.addFragment(new b_Frag(), getString("B"));
        adapter.addFragment(new c_Frag(), getString("C"));
        viewPager.setAdapter(adapter);
    }

the a_frag code as example:

public class a_Frag extends Fragment {
    RecyclerView recyclerView;

    public a_Frag() {
    }

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

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


    }

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

        View view = inflater.inflate(R.layout.rv, container, false);
        recyclerView = (RecyclerView) view.findViewById(R.id.rv);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(new a_Adapter(getContext(), R.layout.group_header));


        return recyclerView;
    }

ExpandableRecyclerView code:

public class ExpandableRecyclerView extends RecyclerView {
    public ExpandableRecyclerView(Context context) {
        super(context, null);
        initRecycler();
    }

    public ExpandableRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initRecycler();
    }

    public ExpandableRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initRecycler();
    }

    private void initRecycler() {
        setClipToPadding(false);
        setItemAnimator(new DefaultItemAnimator());

    }

    @Override
    public Parcelable onSaveInstanceState() {
        //begin boilerplate code that allows parent classes to save state
        Parcelable superState = super.onSaveInstanceState();

        SavedState ss = new SavedState(superState);
        //end

        if (getAdapter() != null)
            ss.stateToSave = ((Adapter) this.getAdapter()).getExpandedGroups();

        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        //begin boilerplate code so parent classes can restore state
        if (!(state instanceof SavedState))  // if state is not instance of out SaveState just restore in reg way
        {
            super.onRestoreInstanceState(state);
            return;
        }
        // else if cast him to SavedState

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        //end

        if (getAdapter() != null)
            ((Adapter) getAdapter()).setExpandedGroups(ss.stateToSave);
    }

    @Override
    public void setAdapter(RecyclerView.Adapter adapter) {
        if (!(adapter instanceof Adapter))
            throw new IllegalArgumentException("adapter has to be of type ExpandableRecyclerView.Adapter");
        super.setAdapter(adapter);
    }


    public interface OnChildItemClickedListener {
        void onChildItemClicked(int group, int position);
    }

    static class SavedState implements Parcelable {
        public static final SavedState EMPTY_STATE = new SavedState() {
        };
        //required field that makes Parcelables from a Parcel
        public static final Creator<SavedState> CREATOR =
                new Creator<SavedState>() {
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
        SparseBooleanArray stateToSave;
        Parcelable superState;

        SavedState() {
            superState = null;
        }

        SavedState(Parcelable superState) {
            this.superState = superState != EMPTY_STATE ? superState : null;
        }

        private SavedState(Parcel in) {
            Parcelable superState = in.readParcelable(ExpandableRecyclerView.class.getClassLoader());
            this.superState = superState != null ? superState : EMPTY_STATE;
            this.stateToSave = in.readSparseBooleanArray();
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(@NonNull Parcel out, int flags) {
            out.writeParcelable(superState, flags);
            out.writeSparseBooleanArray(this.stateToSave);
        }

        public Parcelable getSuperState() {
            return superState;
        }
    }

    public static abstract class Adapter<CVH extends ViewHolder, GVH extends ViewHolder, C, G> extends RecyclerView.Adapter<ViewHolder> {

        private static final int TYPE_HEADER = 0;
        SparseBooleanArray expanded = new SparseBooleanArray();
        private OnChildItemClickedListener onChildItemClickedListener;

        public Adapter() {
        }

        boolean isExpanded(int group) {
            return expanded.get(group);
        }

        SparseBooleanArray getExpandedGroups() {
            return expanded;
        }

        public void setExpandedGroups(SparseBooleanArray expanded) {
            this.expanded = expanded;
        }

        public void expand(int group) {
            if (isExpanded(group))
                return;

            // this lines of code calculate number of shown item in recycler view. also group is counting .
            int position = 0;
            for (int i = 0; i < group; i++) {
                position++;
                if (isExpanded(i))
                    position += getChildItemCount(i);
            }
            position++; // this for percent group

            notifyItemRangeInserted(position, getChildItemCount(group)); // notify recycler view for expanding
            expanded.put(group, true); // save expanding in sparce array
        }

        public void collapse(int group) {
            if (!isExpanded(group)) // if is not expanded . so nothing to collapse.
                return;

            int position = 0;
            for (int i = 0; i < group; i++) {
                position++;
                if (isExpanded(i))
                    position += getChildItemCount(i); // item
            }
            position++;
            notifyItemRangeRemoved(position, getChildItemCount(group));
            expanded.put(group, false);
        }

        public abstract int getGroupItemCount();

        public abstract int getChildItemCount(int group);

        @Override
        public int getItemCount() {
            int count = 0;
            for (int i = 0; i <= getGroupItemCount(); i++) {
                count += isExpanded(i) ? getChildItemCount(i) + 1 : 1;
            }
            return count;
        }

        public abstract G getGroupItem(int position);

        public abstract C getChildItem(int group, int position);

        public Object getItem(int i) {
            int group = 0;
            while (group <= getGroupItemCount()) {
                if (i > 0 && !isExpanded(group)) {
                    i--;
                    group++;
                    continue;
                }
                if (i > 0 && isExpanded(group)) {
                    i--;
                    if (i < getChildItemCount(group))
                        return getChildItem(group, i);
                    i -= getChildItemCount(group);
                    group++;
                    continue;
                }
                if (i == 0)
                    return getGroupItem(group);
            }
            throw new IndexOutOfBoundsException();
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int i) {
            int group = 0;
            while (group <= getGroupItemCount()) {
                if (i > 0 && !isExpanded(group)) {
                    i--;
                    group++;
                    continue;
                }
                if (i > 0 && isExpanded(group)) {
                    i--;
                    if (i < getChildItemCount(group)) {
                        onBindChildViewHolder((CVH) holder, group, i);
                        return;
                    }
                    i -= getChildItemCount(group);
                    group++;
                    continue;
                }
                if (i == 0) {
                    onBindGroupViewHolder((GVH) holder, group);
                    return;
                }
            }
            throw new IndexOutOfBoundsException();
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return viewType == TYPE_HEADER ? onCreateGroupViewHolder(parent) : onCreateChildViewHolder(parent, viewType);
        }

        protected abstract GVH onCreateGroupViewHolder(ViewGroup parent);

        protected abstract CVH onCreateChildViewHolder(ViewGroup parent, int viewType);

        public abstract int getChildItemViewType(int group, int position);

        @Override
        public int getItemViewType(int i) {
            int group = 0;
            while (group <= getGroupItemCount()) {
                if (i > 0 && !isExpanded(group)) {
                    i--;
                    group++;
                    continue;
                }
                if (i > 0 && isExpanded(group)) {
                    i--;
                    if (i < getChildItemCount(group))
                        return getChildItemViewType(group, i);
                    i -= getChildItemCount(group);
                    group++;
                    continue;
                }
                if (i == 0)
                    return TYPE_HEADER;
            }
            throw new IndexOutOfBoundsException();
        }

        public void setOnChildItemClickedListener(OnChildItemClickedListener onItemClickedListener) {
            this.onChildItemClickedListener = onItemClickedListener;
        }

        public void onBindChildViewHolder(CVH holder, final int group, final int position) {
            holder.itemView.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    if (Adapter.this.onChildItemClickedListener != null) {
                        Adapter.this.onChildItemClickedListener.onChildItemClicked(group, position);
                    }

                }
            });
        }

        public void onBindGroupViewHolder(final GVH holder, final int group) {
            if (holder instanceof GroupViewHolder)
                ((GroupViewHolder) holder).setExpanded(isExpanded(group));

            holder.itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (isExpanded(group)) {
                        collapse(group);
                        if (holder instanceof GroupViewHolder)
                            ((GroupViewHolder) holder).collapse();
                    } else {
                        expand(group);
                        if (holder instanceof GroupViewHolder)
                            ((GroupViewHolder) holder).expand();
                    }
                }
            });
        }
    }

    public static abstract class GroupViewHolder extends ViewHolder {

        public GroupViewHolder(View itemView) {
            super(itemView);
        }

        public abstract void expand();

        public abstract void collapse();

        public abstract boolean isExpanded();

        public abstract void setExpanded(boolean expanded);
    }

    public static class SimpleGroupViewHolder extends GroupViewHolder {
        ImageView expandedIndicator;
        TextView text;
        private boolean expanded;

        public SimpleGroupViewHolder(Context context) {
            super(View.inflate(context, R.layout.group_header, null));

            itemView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            expandedIndicator = (ImageView) itemView.findViewById(R.id.carbon_groupExpandedIndicator);
            text = (TextView) itemView.findViewById(R.id.carbon_groupText);
        }

        public SimpleGroupViewHolder(Context context, int layout) {
            super(View.inflate(context, layout, null));

            itemView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            expandedIndicator = (ImageView) itemView.findViewById(R.id.carbon_groupExpandedIndicator);
            text = (TextView) itemView.findViewById(R.id.carbon_groupText);
        }

        public void expand() {
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
            animator.setInterpolator(new DecelerateInterpolator());
            animator.setDuration(200);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
                    expandedIndicator.postInvalidate();
                }
            });
            animator.start();
            expanded = true;
        }

        public void collapse() {
            ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
            animator.setInterpolator(new DecelerateInterpolator());
            animator.setDuration(200);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
                    expandedIndicator.postInvalidate();
                }
            });
            animator.start();
            expanded = false;
        }

        @Override
        public boolean isExpanded() {
            return expanded;
        }

        public void setExpanded(boolean expanded) {
            ViewHelper.setRotation(expandedIndicator, expanded ? 180 : 0);
            this.expanded = expanded;
        }

        public String getText() {
            return text.getText().toString();
        }

        public void setText(String t) {
            text.setText(t);
        }
    }

}

the adapters are the same except for the data so I will put one example:

Adapter code example:

public class Adapter extends ExpandableRecyclerView.Adapter<Adapter.ChildViewHolder, Adapter.GroupViewHolder, String, String> {

    private DatabaseHelper db;
    private ArrayList List;
    private int layout;


    public Adapter(Context c, int resource) {
        layout = resource;
        db = new DatabaseHelper(c);
        List = db.getRecords();//returns list
    }

    @Override
    public int getGroupItemCount() {
        return List.size() - 1;
    }

    @Override
    public int getChildItemCount(int i) {
        return 1;
    }

    @Override
    public String getGroupItem(int i) {
        return null;
    }

    @Override
    public String getChildItem(int group, int child) {
        return null;
    }

    @Override
    protected GroupViewHolder onCreateGroupViewHolder(ViewGroup parent) {
        return new GroupViewHolder(parent.getContext(), layout);
    }

    @Override
    protected ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
        View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.group_item, parent, false);
        return new ChildViewHolder(rootView);
    }

    @Override
    public void onBindGroupViewHolder(GroupViewHolder holder, int group) {
        super.onBindGroupViewHolder(holder, group);

        holder.setText1("A");


        holder.setText2("B");
    }

    @Override
    public void onBindChildViewHolder(ChildViewHolder holder, int group, int position) {
        super.onBindChildViewHolder(holder, group, position);

        holder.t1.setText("t1");


        holder.t2.setText("t2");


        holder.t3.setText("t3");


                   holder.t4.setText("t4");

    }

    @Override
    public int getChildItemViewType(int i, int i1) {
        return 1;
    }

    static class GroupViewHolder extends ExpandableRecyclerView.GroupViewHolder {
        ImageView expandedIndicator;
        TextView text1;
        TextView text2;
        private boolean expanded;


        GroupViewHolder(Context context, int layout) {
            super(View.inflate(context, layout, null));

            itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            expandedIndicator = (ImageView) itemView.findViewById(R.id.groupExpandedIndicator);
            text1 = (TextView) itemView.findViewById(R.id.text1);
            text2 = (TextView) itemView.findViewById(R.id.text2);
        }

        public void expand() {
            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
            animator.setInterpolator(new DecelerateInterpolator());
            animator.setDuration(200);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
                    expandedIndicator.postInvalidate();
                }
            });
            animator.start();
            expanded = true;
        }

        public void collapse() {
            ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
            animator.setInterpolator(new DecelerateInterpolator());
            animator.setDuration(200);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    ViewHelper.setRotation(expandedIndicator, 180 * (float) (animation.getAnimatedValue()));
                    expandedIndicator.postInvalidate();
                }
            });
            animator.start();
            expanded = false;
        }

        @Override
        public boolean isExpanded() {
            return expanded;
        }

        public void setExpanded(boolean expanded) {
            ViewHelper.setRotation(expandedIndicator, expanded ? 180 : 0);
            this.expanded = expanded;
        }

        void setText1(String t) {
            text1.setText(t);
        }


        void setText2(String t) {
            text2.setText(t);
        }
    }

    class ChildViewHolder extends RecyclerView.ViewHolder {
        private TextView t1;
        private TextView t2;
        private TextView t3;
        private TextView t4;


        ChildViewHolder(View itemView) {
            super(itemView);
            t1 = (TextView) itemView.findViewById(R.id.tv1);
            t2 = (TextView) itemView.findViewById(R.id.tv2);
            t3 = (TextView) itemView.findViewById(R.id.tv3);
            t4 = (TextView) itemView.findViewById(R.id.tv4);
        }
    }
}

group_header.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true">

    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/Card1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="7dp"
        android:layout_marginRight="7dp"
        android:layout_marginTop="10dp"        
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/text1"
                style="@style/MyLargeTextAppearance"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center"
                android:padding="16dp"
                android:textColor="@color/highlight"
                android:textStyle="bold" />


        </LinearLayout>
    </android.support.v7.widget.CardView>

    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/Card1"
        android:layout_gravity="center"
        android:layout_marginLeft="7dp"
        android:layout_marginRight="7dp"
        android:layout_marginTop="1dp"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp">

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/text2"
                style="@style/MyLargeTextAppearance"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center"
                android:padding="16dp"
                android:textColor="@color/highlight"
                android:textStyle="bold" />

            <ImageView
                android:id="@+id/groupExpandedIndicator"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_gravity="center_vertical"
                android:layout_marginEnd="16dp"
                android:layout_marginRight="16dp"
                app:srcCompat="@drawable/ic_expand_more" />
        </LinearLayout>
    </android.support.v7.widget.CardView>
</RelativeLayout>

group_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginLeft="7dp"
    android:layout_marginRight="7dp"
    android:layout_marginTop="10dp"
    card_view:cardCornerRadius="10dp"
    card_view:cardElevation="5dp">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/materialGray"
        android:orientation="vertical"
        android:padding="@dimen/activity_horizontal_margin">


        <TextView
            android:id="@+id/t1"
            style="@style/MyLargeTextAppearance"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="TextView" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/separatorViewBackground" />

        <TextView
            android:id="@+id/t2"
            style="@style/MyLargeTextAppearance"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="TextView" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/separatorViewBackground" />

        <TextView
            android:id="@+id/t3"
            style="@style/MyLargeTextAppearance"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="TextView"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/separatorViewBackground" />

        <TextView
            android:id="@+id/t4"
            style="@style/MyLargeTextAppearance"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="TextView" />

    </LinearLayout>
</android.support.v7.widget.CardView>
Zacktamondo
  • 1,891
  • 3
  • 17
  • 33
  • 1
    no progress so far but I think the problem exist in the GroupViewHolder – Zacktamondo Jul 13 '17 at 08:06
  • Do you have a stracktrace of the "specified child already has a parent" exception? – Rasmusob Sep 18 '17 at 09:42
  • 1
    @Rasmusob no I don't – Zacktamondo Sep 18 '17 at 09:43
  • 1
    I guess it's caused by you return `recyclerView` instead of `view` in `onCreateView`. The recyclerview already has a parent in your inflated layout. The recyclerview wrapped with `ViewGroup` , LinearLayout or RelativeLayout which is used in your layout. – Agi Maulana Sep 18 '17 at 09:57
  • 1
    @AgiMaulana you're right one of the fragments was returning recyclerView instead of view which apparently caused the problem. – Zacktamondo Sep 18 '17 at 10:19

1 Answers1

4

Try changing to:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
    View view = inflater.inflate(R.layout.rv, container, false);
    recyclerView = (RecyclerView) view.findViewById(R.id.rv);
    recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
    recyclerView.setAdapter(new a_Adapter(getContext(), R.layout.group_header)); 
    return recyclerView;
}
Shazniq
  • 423
  • 4
  • 16
Surendra Kumar
  • 2,787
  • 1
  • 12
  • 10
  • This doesn't make sense to me. Why would you dismiss the view that holds the actual `RecyclerView`? – Iulian Popescu Sep 20 '17 at 09:23
  • @IulianPopescu We are not dismissing the view. We are returning the same view, if exists. Although the error is not limited to this particular case. It might occur in various other scenarios too. This is one of them . – Surendra Kumar Sep 20 '17 at 09:32