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.
Hi you can use this to animate the views inside your RecyclerView as shown on the GIF below:
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>