javaandroidkotlinandroid-recyclerviewandroid-flexboxlayout

Android Recyclerview with Viewholder changing width and height onClick


I want to create a RecyclerView in Android which contains ViewHolders, that change their width and height as shown in the GIF below. I tried several LayoutManagers for the RecyclerView but I did not manage to get the result I wished for.

The RecyclerView should be a 1 dimensional row.

Visualization of the task


Solution

  • Hi you can use this to animate the views inside your RecyclerView as shown on the GIF below:

    enter image description here

    My adapter:

      public class StringAdapter extends RecyclerView.Adapter<StringAdapter.StringHolder> {
            private int defaultWidth = 0;
            private int defaultHeight = 0;
            private static final String[] strings = new String[]{"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 8", "Item 10"};
            private int lastSelected = -1;
            private final RecyclerView.LayoutManager layoutManager;
            private static final int animValue = 50;
        
            StringAdapter(RecyclerView.LayoutManager layoutManager) {
                this.layoutManager = layoutManager;
            }
        
            @NonNull
            @Override
            public StringHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_holder, parent, false);
                return new StringHolder(v);
            }
        
            @Override
            public void onBindViewHolder(@NonNull StringHolder holder, int position) {
                holder.categories.setText(strings[position]);
                ViewGroup.LayoutParams param = holder.card.getLayoutParams();
                if (position == lastSelected) {
                    param.width = defaultWidth + animValue;
                    param.height = defaultHeight + animValue;
                } else {
                    if (defaultWidth != 0 && defaultHeight != 0) {
                        param.width = defaultWidth;
                        param.height = defaultHeight;
                    }
                }
            }
        
            @Override
            public int getItemCount() {
                return strings.length;
            }
        
            protected class StringHolder extends RecyclerView.ViewHolder {
        
                private final TextView categories;
                private final CardView card;
        
                public StringHolder(@NonNull View itemView) {
                    super(itemView);
                    categories = itemView.findViewById(R.id.categories);
                    card = itemView.findViewById(R.id.cardCategories);
                    card.setOnClickListener(v -> {
                        int currentPosition = this.getAbsoluteAdapterPosition();
                        selection(currentPosition, lastSelected);
                        lastSelected = currentPosition;
        
                    });
                }
        
                private void selection(int newPosition, int oldPosition) {
                    if (newPosition != oldPosition) {
                        ConstraintLayout newView = (ConstraintLayout) layoutManager.findViewByPosition(newPosition);
                        ConstraintLayout oldView = (ConstraintLayout) layoutManager.findViewByPosition(oldPosition);
                        if (newView != null) {
                            defaultWidth = card.getWidth();
                            defaultHeight = card.getHeight();
                            selectAnimate(newView.getChildAt(0));
                        }
                        if (oldView != null) {
                            deselectAnimate(oldView.getChildAt(0));
                        } else {
                            notifyItemChanged(oldPosition);
                        }
                    }
                }
        
                private void selectAnimate(View view) {
                    ValueAnimator select = ValueAnimator.ofInt(defaultWidth, defaultWidth + animValue);
                    select.setInterpolator(new AccelerateDecelerateInterpolator());
                    select.setDuration(300);
                    select.start();
                    select.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                            layoutParams.width = (int) animation.getAnimatedValue();
                            layoutParams.height = (int) (animation.getAnimatedValue()) + defaultHeight - defaultWidth;
                            view.requestLayout();
                        }
                    });
                }
        
                private void deselectAnimate(View view) {
                    ValueAnimator select = ValueAnimator.ofInt(defaultWidth + animValue, defaultWidth);
                    select.setInterpolator(new AccelerateDecelerateInterpolator());
                    select.setDuration(300);
                    select.start();
                    select.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                            layoutParams.width = (int) animation.getAnimatedValue();
                            layoutParams.height = (int) (animation.getAnimatedValue()) + defaultHeight - defaultWidth;
                            view.requestLayout();
                        }
                    });
                }
            }
        }
    

    My MainActivity :

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            RecyclerView recyclerView = findViewById(R.id.recyclerView);
            LinearLayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false);
            recyclerView.setLayoutManager(layoutManager);
            StringAdapter adapter = new StringAdapter(layoutManager);
            recyclerView.setAdapter(adapter);
        }
    }
    

    My MainActivity Layout

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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"
        tools:context=".test.MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
          />
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    And finaly this MyHolder Layout :

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_marginStart="5dp">
    
        <androidx.cardview.widget.CardView
            android:id="@+id/cardCategories"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:cardCornerRadius="15dp"
            app:cardUseCompatPadding="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent">
    
            <TextView
                android:id="@+id/categories"
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:paddingStart="10dp"
                android:paddingEnd="10dp"
                android:text="Some Text"
                android:textSize="16sp" />
        </androidx.cardview.widget.CardView>
    </androidx.constraintlayout.widget.ConstraintLayout>