14

I try to do something like this : enter image description here

I managed to do my cardViewAdapter but I block to enlarge my cards. I resumed the code of this response (Here the name of the class is : CardsAnimationHelper) to do the animation but it's superimposed.

Before expand: picture After expand: picture

I solved the problem above but if on my cardView I display 10 elements at the same time for a list of 50. If I expand the first, the numbers 11,21,31,41 will also expand. Do you have a trick for this not to happen?

I have reflected, it makes no sense to me. Just before my OnClick method I display a textview where the text is the position. But when I click id are correct so that would mean that when I click it detects the click on several cards. I think I may have a problem with a view in my OnClickListener

My CardView

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 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.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"

        app:cardBackgroundColor="@android:color/white"
        app:cardCornerRadius="2dp"
        app:cardElevation="2dp">

        <!-- Les CardView possèdent des attributs supplémentaires dont
             - cardBackgroundColor
             - cardElevation pour l'élévation (donc aussi l'ombre)
             - cardCornerRadius pour arrondir les angles
         -->

        <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <!-- Les CardView agissent comme des FrameLayout,
             pour avoir une organisation verticale nous devons
             donc rajouter un LinearLayout -->


            <TextView
                android:id="@+id/text_cards"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="?android:selectableItemBackground"
                android:padding="20dp"
                tools:text="Paris"
                android:fontFamily="sans-serif"
                android:textColor="#333"
                android:textSize="18sp" />

            <ImageView
                android:id="@+id/item_description_game_more"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|end"
                android:transitionName="@string/transition_cards_view"
                app:srcCompat="@drawable/ic_expand_more_black_24dp"/>

            <include layout="@layout/cards_resume_game_expand"/>

        </android.support.design.widget.CoordinatorLayout>

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

</LinearLayout>

My New Adapter

public class CardsViewAdapter extends RecyclerView.Adapter<CardsViewAdapter.ViewHolder> {
    private Game[] mDataset;
    private boolean isPopupVisible = false;
    int rotationAngle = 0;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ImageView imageView;
        public LinearLayout test2;

        public ViewHolder(View v) {
            super(v);
            mTextView = (TextView) v.findViewById(R.id.text_cards);
            imageView = (ImageView) v.findViewById(R.id.item_description_game_more);
            test2 = (LinearLayout) v.findViewById(R.id.popup_layout);

        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public CardsViewAdapter(Game[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public CardsViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cards_resume_game, parent, false);
        // set the view's size, margins, paddings and layout parameters
        //...

        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element

        holder.mTextView.setText(String.valueOf(mDataset[position].getId_game()));
        holder.imageView.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Perform action on click
                if (isPopupVisible) {
                    isPopupVisible = false;

                    ObjectAnimator anim = ObjectAnimator.ofFloat(v, "rotation",rotationAngle, rotationAngle + 180);
                    anim.setDuration(500);
                    anim.start();
                    rotationAngle += 180;
                    rotationAngle = rotationAngle%360;

//                    CardsAnimationHelper.changeIconAnim((TextView) v, getString(R.string.icon_chevron_up));
                    CardsAnimationHelper.collapse(holder.test2);
                } else {
                    isPopupVisible = true;

                    ObjectAnimator anim = ObjectAnimator.ofFloat(v, "rotation",rotationAngle, rotationAngle + 180);
                    anim.setDuration(500);
                    anim.start();
                    rotationAngle += 180;
                    rotationAngle = rotationAngle%360;

//                    CardsAnimationHelper.changeIconAnim((TextView) v, getString(R.string.icon_chevron_down));
                    CardsAnimationHelper.expand(holder.test2);
                }
            }
        });



    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}
Community
  • 1
  • 1
filol
  • 1,669
  • 1
  • 15
  • 38
  • did you try `CardsAnimationHelper.collapse(v);` and `CardsAnimationHelper.expand(v);` ? – Suraj Rao Jan 04 '17 at 13:16
  • 1
    here you are sending only your textview and not the card – Suraj Rao Jan 04 '17 at 13:16
  • @SurajRao Indeed, an error on my part but now it is superimposed: Before expand: http://imgur.com/a/8uIsx After expand: http://imgur.com/a/VVz7g – filol Jan 04 '17 at 13:36
  • Possible duplicate of [Expand and collapse CardView](https://stackoverflow.com/questions/35500322/expand-and-collapse-cardview) – Madhur Dec 28 '18 at 03:07

2 Answers2

18

I did not understand what you meant by displaying 10 elements out of 50. However, you can achieve the expand/collapse simply by showing/hiding the views and providing android:animateLayoutChanges="true" into the child layout of the CardView. Here is an example:

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:padding="16dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"/>

        <TextView
            android:id="@+id/hello2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:visibility="gone"/>

        <TextView
            android:id="@+id/hello3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:visibility="gone"/>

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

And corresponding controller:

    TextView t1 = (TextView) findViewById(R.id.hello);
    final TextView t2 = (TextView) findViewById(R.id.hello2);
    final TextView t3 = (TextView) findViewById(R.id.hello3);

    t1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (t2.getVisibility() == View.GONE) {
                t2.setVisibility(View.VISIBLE);
                t3.setVisibility(View.VISIBLE);
            } else {
                t2.setVisibility(View.GONE);
                t3.setVisibility(View.GONE);
            }
        }
    });

Tapping on the first TextView will collapse and expand the CardView along with the animation.

AgileNinja
  • 914
  • 1
  • 7
  • 20
13

You'll need to create a custom class that extends CardView. Inside that class put the following methods:

public void expand() {
    int initialHeight = getHeight();

    measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    int targetHeight = getMeasuredHeight();

    int distanceToExpand = targetHeight - initialHeight;

    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (interpolatedTime == 1){
                // Do this after expanded
            }

            getLayoutParams().height = (int) (initialHeight + (distanceToExpand * interpolatedTime));
            requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    a.setDuration((long) distanceToExpand);
    startAnimation(a);
}

public void collapse(int collapsedHeight) {
    int initialHeight = getMeasuredHeight();

    int distanceToCollapse = (int) (initialHeight - collapsedHeight);

    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (interpolatedTime == 1){
                // Do this after collapsed
            }


            Log.i(TAG, "Collapse | InterpolatedTime = " + interpolatedTime);

            getLayoutParams().height = (int) (initialHeight - (distanceToCollapse * interpolatedTime));
            requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };

    a.setDuration((long) distanceToCollapse);
    startAnimation(a);
}

Note that when you collapse it, you'll need to pass along the height you want it to be when collapsed. The height when expanded is set to WRAP_CONTENT.

I've also added if/else statements that will run when the animation has completed.

Good luck!

LukeWaggoner
  • 8,869
  • 1
  • 29
  • 28
  • I already have this class (see my link in my post): http://stackoverflow.com/a/13381228/7355733 – filol Jan 04 '17 at 14:00
  • You need to have just one layout for your `CardView` that expands. Inside the expansion and collapsing, you can hide and show the views that you want to hide and show based on whether it's expanded or collapsed. i.e. Show the header while it's collapsed, and in the process of showing it, hide the header and show the contents. – LukeWaggoner Jan 04 '17 at 15:18
  • Thx, it's working. The solution was only to make a single layout – filol Jan 04 '17 at 15:40
  • 1
    However on my cardView I display 10 elements at the same time for a list of 50. If I expand the first, the numbers 11,21,31,41 will also expand. Do you have a trick for this not to happen? – filol Jan 04 '17 at 15:46
  • Why is it expanding more than just the card on which you've called `expand()`? – LukeWaggoner Jan 04 '17 at 17:37
  • This is the question ^^ why ? – filol Jan 04 '17 at 18:02
  • How are you calling `expand()`? You should be calling it on a specific instance of `CardView`, and it should only be affecting that one. – LukeWaggoner Jan 04 '17 at 18:11
  • Check my post, i have add my Adapter edited lastly. But for me my `expand()` is only on one item – filol Jan 04 '17 at 18:20
  • I have reflected, it makes no sense to me. Just before my OnClick method I display a textview where the text is the position. But when I click id are correct so that would mean that when I click it detects the click on several cards. – filol Jan 04 '17 at 22:43
  • I'd need to see your CardsAnimationHelper to be sure about this, but I think it's because your ViewHolder class is static. You're instantiating it and passing it around as the ViewHolder anyway, so you don't need that to be static, and making it not static allows you to call methods on one specific card, as opposed to multiple. – LukeWaggoner Jan 05 '17 at 14:08
  • @filol, It's also happening with me, in my case it's 30 items and every 7th or 8th item is expanding. By any chance do you remember what was the solution for it? – Parag Pawar Nov 18 '19 at 19:59