wbk727
wbk727

Reputation: 8408

Incorrect CardView expanded/collapsed on click

For some reason, the Item C CardView is the only one that animates - even when the Item B or the Item A CardView is clicked. The French vowels CardView is not applicable as it works fine. Does anyone know how to fix this so that the correct CardView animates?

enter image description here

GridViewCustom class for the GridView

public class GridViewCustom extends GridView {
    public GridViewCustom(Context context) {
        super(context);
    }

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

    public GridViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int heightSpec;

        if (getLayoutParams().height == AbsListView.LayoutParams.WRAP_CONTENT) {
            heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        }
        else {
            //Any other height should be respected as is.
            heightSpec = heightMeasureSpec;
        }

        super.onMeasure(widthMeasureSpec, heightSpec);
    }
}

fragment class

public class MyFragmentRV extends android.support.v4.app.Fragment {
    private Boolean mCurrentValue;

    public int mGridViewHeight;
    public int txtSubtitleHeight;
    private static final int ITEM_TYPE = 100;
    private static final int HEADER_TYPE = 101;
    private static final int HEADER_TYPE_2 = 102;
    private static final int GRID_TYPE = 103;
    ValueAnimator mAnimatorGV, mAnimatorTV;
    TextView txtArrowGV, txtTitle;

    static final String[] frenchVowels = new String[]{
            "a", "e", "i", "o", "u", "y"
    };

    public MyFragmentRV.MyAdapter adapterGV;

    public MyFragmentRV() {}

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

        return inflater.inflate(R.layout.fragment_rv, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        View v = getView();
        assert v != null;

        recyclerView = v.findViewById(R.id.my_recyclerview);

        // set the linear layout manager
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));

        // SpannableStrings
        int[] attrS = {R.attr.spannablestringtextColor};
        TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrS);
        int colorSS = ta.getColor(0, Color.BLACK); //Color.BLACK - default value (colour will change automatically depending on chosen theme)
        Log.d(TAG, "clickMethod 1) " + Integer.toHexString(colorSS));
        ta.recycle();

        // SpannableString (start)
        SpannableStringBuilder ssb = new SpannableStringBuilder();

        SpannableString str1 = new SpannableString(" Item A ");
        str1.setSpan(new BackgroundColorSpan(Color.BLACK), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        str1.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.yellow)), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        ssb.append(str1);

        SpannableString str2 = new SpannableString(" Hello World");
        str2.setSpan(new ForegroundColorSpan(colorSS), 0, str2.length(), 0);
        ssb.append(str2);
        // SpannableString (end)

        // init data
        data = new ArrayList<>();
        data.add(ssb);
        data.add("Item B");
        data.add("Item C");

        subdata = new ArrayList<>();
        subdata.add("\u2022 a");
        subdata.add("\u2022 b\n\u2022 bb");
        subdata.add("\u2022 c\n\u2022 cc\n\u2022 ccc");

        adapter = createAdapter();

        recyclerView.setAdapter(adapter);

        super.onActivityCreated(savedInstanceState);
    }

    RecyclerView recyclerView;
    ArrayList<CharSequence> data;
    ArrayList<String> subdata;
    RecyclerView.Adapter<ViewHolder> adapter;

    // creates the adapter
    private RecyclerView.Adapter<ViewHolder> createAdapter() {
        return new RecyclerView.Adapter<ViewHolder>() {
            @NonNull
            @Override
            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
                switch (type) {
                    case HEADER_TYPE:
                        return new ViewHolder(inflateHelper(R.layout.header, parent));

                    case HEADER_TYPE_2:
                        return new ViewHolder(inflateHelper(R.layout.header, parent));

                    case ITEM_TYPE:
                        return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));

                    case GRID_TYPE:
                        return new ViewHolder(inflateHelper(R.layout.recyclerview_item_gv, parent));

                    default:
                        return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
                }
            }

            @Override
            public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
                final Typeface iconFont = FontManager.getTypeface(getContext(), FontManager.FONTAWESOME);

                switch (getItemViewType(position)) {
                    case HEADER_TYPE:
                        Button expandButton = viewHolder.itemView.findViewById(R.id.button);
                        expandButton.setText("Expand all");
                    break;
                    case HEADER_TYPE_2:
                        Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
                        collapseButton.setText("Collapse all");
                    break;
                    case ITEM_TYPE:
                        // get the current item
                        CharSequence itemA = data.get(position - 3);
                        String itemB = subdata.get(position - 3);

                        //
                        txtTitle = viewHolder.itemView.findViewById(R.id.tv_tv_A);
                        txtTitle.setText(itemA);

                        final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
                        txtSubtitle.setText(itemB);
                        txtSubtitle.setVisibility(View.GONE);

                        //Add onPreDrawListener
                        txtSubtitle.getViewTreeObserver().addOnPreDrawListener(
                        new ViewTreeObserver.OnPreDrawListener() {

                            @Override
                            public boolean onPreDraw() {
                                txtSubtitle.getViewTreeObserver().removeOnPreDrawListener(this);
                                txtSubtitle.setVisibility(View.GONE);

                                final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                                final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                                txtSubtitle.measure(widthSpec, heightSpec);

                                txtSubtitleHeight = txtSubtitle.getMeasuredHeight();

                                return true;
                            }
                        });

                        final TextView txtArrowTV = viewHolder.itemView.findViewById(R.id.tv_tv_expandcollapse);
                        txtArrowTV.setText(R.string.fa_icon_chevron_down);
                        txtArrowTV.setTypeface(iconFont);

                        //
                        CardView cardView = viewHolder.itemView.findViewById(R.id.cv_tv);
                        LinearLayout mLinearLayoutTV = viewHolder.itemView.findViewById(R.id.cardview_tv_titlerow);

                        //
                        cardView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if(txtSubtitle.getVisibility() == View.GONE){
                                    expandTV(txtSubtitle, txtArrowTV);
                                } else {
                                    collapseTV(txtSubtitle, txtArrowTV);
                                }
                            }
                        });

                        mLinearLayoutTV.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if(txtSubtitle.getVisibility() == View.GONE){
                                    expandTV(txtSubtitle, txtArrowTV);
                                } else {
                                    collapseTV(txtSubtitle, txtArrowTV);
                                }
                            }
                        });

                        txtArrowTV.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if(txtSubtitle.getVisibility() == View.GONE){
                                    expandTV(txtSubtitle, txtArrowTV);
                                } else {
                                    collapseTV(txtSubtitle, txtArrowTV);
                                }
                            }
                        });
                    break;
                    case GRID_TYPE:
                        TextView titleG = viewHolder.itemView.findViewById(R.id.tv_gv_A);

                        titleG.setText("French vowels");

                        txtArrowGV = viewHolder.itemView.findViewById(R.id.tv_gv_expandcollapse);
                        txtArrowGV.setText(R.string.fa_icon_chevron_down);
                        txtArrowGV.setTypeface(iconFont);

                        final GridView mGridViewA = viewHolder.itemView.findViewById(R.id.gv);
                        mGridViewA.setVisibility(View.GONE);
                        mGridViewA.setEnabled(false);
                        mGridViewA.setVerticalScrollBarEnabled(false);
                        adapterGV = new MyFragmentRV.MyAdapter(getActivity().getApplicationContext(), 0);
                        mGridViewA.setAdapter(adapterGV);
                        for (String fVowel : fVowels) {
                            adapterGV.addAdapterItem(new MyFragmentRV.AdapterItem(fVowel));
                        }


                        //Add onPreDrawListener
                        mGridViewA.getViewTreeObserver().addOnPreDrawListener(
                        new ViewTreeObserver.OnPreDrawListener() {

                            @Override
                            public boolean onPreDraw() {
                                mGridViewA.getViewTreeObserver().removeOnPreDrawListener(this);
                                mGridViewA.setVisibility(View.GONE);

                                final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                                final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                                mGridViewA.measure(widthSpec, heightSpec);

                                mGridViewHeight = mGridViewA.getMeasuredHeight();

                                return true;
                            }
                        });

                        CardView cardViewG = viewHolder.itemView.findViewById(R.id.cv_gv);
                        LinearLayout mLinearLayoutGV = viewHolder.itemView.findViewById(R.id.cardview_gv_titlerow);

                        cardViewG.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if(mGridViewA.getVisibility() == View.GONE){
                                    expandGV(mGridViewA);
                                } else {
                                    collapseGV(mGridViewA);
                                }
                            }
                        });

                        mLinearLayoutGV.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if(mGridViewA.getVisibility() == View.GONE){
                                    expandGV(mGridViewA);
                                } else {
                                    collapseGV(mGridViewA);
                                }
                            }
                        });

                        txtArrowGV.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if(mGridViewA.getVisibility() == View.GONE){
                                    expandGV(mGridViewA);
                                } else {
                                    collapseGV(mGridViewA);
                                }
                            }
                        });
                    break;
                }
            }

            @Override
            public int getItemCount() {
                return data.size() + 3;
            }

            @Override
            public int getItemViewType(int position) {
                switch (position) {
                    case 0:
                        return HEADER_TYPE;
                    case 1:
                        return HEADER_TYPE_2;
                    case 2:
                        return GRID_TYPE;
                    default: return ITEM_TYPE;
                }
            }
        };
    }

    private View inflateHelper(int resId, ViewGroup parent) {
        return LayoutInflater.from(getActivity()).inflate(resId, parent, false);
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }

    public void expandGV() {
        mGridViewA.setVisibility(View.VISIBLE);
        txtArrowGV.setText(R.string.fa_icon_chevron_up);
        ValueAnimator mAnimatorGV = slideAnimator(0, mGridViewHeight);
        mAnimatorGV.start();
    }
    public void collapseGV() {
        txtArrowGV.setText(R.string.fa_icon_chevron_down);

        int finalGVHeight = mGridViewA.getHeight();

        mAnimatorGV = slideAnimator(finalGVHeight, 0);
        mAnimatorGV.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationEnd(Animator animator) {
                mGridViewA.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationStart(Animator animator) {
            }

            @Override
            public void onAnimationCancel(Animator animator) {
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });
        mAnimatorGV.start();
    }
    public void expandTV() {
        txtSubtitle.setVisibility(View.VISIBLE);
        txtArrowTV.setText(R.string.fa_icon_chevron_up);
        mAnimatorTV = slideAnimator(0, txtSubtitleHeight);
        mAnimatorTV.start();
    }
    public void collapseTV() {
        txtArrowTV.setText(R.string.fa_icon_chevron_down);
        int finalTVHeight = txtSubtitle.getHeight();
        mAnimatorTV = slideAnimator(finalTVHeight, 0);
        mAnimatorTV.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationEnd(Animator animator) {
                txtSubtitle.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationStart(Animator animator) {
            }

            @Override
            public void onAnimationCancel(Animator animator) {
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });
        mAnimatorTV.start();
    }

    public ValueAnimator slideAnimator(int start, int end, final View txtSubtitle) {

        final ValueAnimator animator = ValueAnimator.ofInt(start, end);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                // update height
                int value = (Integer) valueAnimator.getAnimatedValue();

                ViewGroup.LayoutParams layoutParamsTV = txtSubtitle.getLayoutParams();
                layoutParamsTV.height = value;
                txtSubtitle.setLayoutParams(layoutParamsTV);
            }
        });
        return animator;
    }

    /// Adapter for GridView
    private class MyAdapter extends ArrayAdapter<MyFragmentRV.AdapterItem> {
        private List<MyFragmentRV.AdapterItem> items = new ArrayList<>();

        MyAdapter(Context context, int textviewid) {
            super(context, textviewid);
        }

        void addAdapterItem(MyFragmentRV.AdapterItem item) {
            items.add(item);
        }

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public MyFragmentRV.AdapterItem getItem(int position) {
            return ((null != items) ? items.get(position) : null);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @NonNull
        @Override
        public View getView(final int position, View convertView, @NonNull final ViewGroup parent) {
            View rowView;
            if (convertView == null) {
                rowView = getActivity().getLayoutInflater().inflate(R.layout.gridview_item, parent, false);
            } else {
                rowView = convertView;
            }

            TextView tv = rowView.findViewById(R.id.item_gridview);
            tv.setText(items.get(position).first);

            return rowView;
        }
    }

    public class AdapterItem {
        String first;

        //add more items
        AdapterItem(String first) {
            this.first = first;
        }
    }
}

Based on Nikhil's suggestion

enter image description here

Upvotes: 0

Views: 76

Answers (1)

nupadhyaya
nupadhyaya

Reputation: 1944

The problem here is that you are storing txtSubtitle as a member variable of your Fragment. So when the GridView is done initializing your grid items, txtSubtitle will have the last assigned TextView, which in your case is Item C. So when you perform the animation, it always gets applied to Item C. The solution here is to make txtSubtitle a local final Variable. Remove TextView txtArrowGV, txtArrowTV, txtTitle, txtSubtitle; from MyFragmentRV. In case ITEM_TYPE:

final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
....


 cardView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if(txtSubtitle.getVisibility() == View.GONE){
                                expandTV(txtSubtitle);
                            } else {
                                collapseTV(txtSubtitle);
                            }
                        }
                    });

pass txtSubtitle to expandTV, collapseTV and in expandTV:

public void expandTV(TextView txtSubtitle) {
        txtSubtitle.setVisibility(View.VISIBLE);
        txtArrowTV.setText(R.string.fa_icon_chevron_up);
        mAnimatorTV = slideAnimator(0, txtSubtitleHeight);
        mAnimatorTV.start();
    }

EDIT: change slide animator function as:

public ValueAnimator slideAnimator(int start, int end,TextView txtSubtitle) {

    final ValueAnimator animator = ValueAnimator.ofInt(start, end);

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            // update height
            int value = (Integer) valueAnimator.getAnimatedValue();

            ViewGroup.LayoutParams layoutParamsTV = txtSubtitle.getLayoutParams();
            layoutParamsTV.height = value;
            txtSubtitle.setLayoutParams(layoutParamsTV);
        }
    });
    return animator;
}

and call it like this:

mAnimatorTV = slideAnimator(0, txtSubtitleHeight,txtSubtitle);
mAnimatorTV.start();

Upvotes: 2

Related Questions