In recycleView, the general process is to make a stroke or font color change on the selected item, but to improve user experience, you need to add animation. That is, click on the selected element to enlarge, while the previously selected item shrinks, inconvenient to cut GIF, can only put a static image, you imagine ~The CheckBox in the figure is actually an imageView, and its selection and cancellation are also animated. Instead of controlling visible, it can be checked and cancelled by changing the transparency of the image. See the code:

import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.view.View;
import android.widget.ImageView;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.xxx.Wallpaper;
import org.jetbrains.annotations.NotNull;
import java.util.List;

/** * Created by ly on 2021/4/22 14:11 */
public class ManageHomeBgAdapter extends BaseSingleSelectAdapter<Wallpaper> {

    public ManageHomeBgAdapter(List<Wallpaper> mList) {
        super(R.layout.m_item_manage_home_bg, mList, false);
    }

    @Override
    protected void convert(@NotNull BaseViewHolder baseViewHolder, Wallpaper wallInfo) {
        super.convert(baseViewHolder, wallInfo);
        baseViewHolder.setText(R.id.m_tv_item_home_bg_name, wallInfo.name);

        ImageView ivBg = baseViewHolder.getView(R.id.m_iv_item_home_bg);
        GlideUtil.loadRound(getContext(), wallInfo.url, ivBg, PixelUtil.dp2px(8));

        View iv = baseViewHolder.getView(R.id.m_iv_item_home_bg_sel);
        int position = baseViewHolder.getAdapterPosition();
        if (wallInfo.isSelected) {
            // Select the animation
            PropertyValuesHolder vb1 = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.5 f.1.3 f.1f);
            PropertyValuesHolder vb2 = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.5 f.1.3 f.1f);
            PropertyValuesHolder vb3 = PropertyValuesHolder.ofFloat(View.ALPHA, 0.5 f.1f);
            ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(iv, vb1, vb2, vb3);
            objectAnimator.setDuration(duration).start();

            // Select the background to zoom in on the animation (theoretically, you can use the ItemAnimator, but we only zoom in on the image, not the whole item, so use the view animation)
            ivBg.animate().scaleX(1f).scaleY(1f)
                    .withEndAction(() -> ivBg.animate().scaleX(1.05 f).scaleY(1.05 f).setDuration(duration))
                    .setDuration(0).start();
        } else {
            // Animates only the last selected item
            if (getLastSelIndex() >= 0 && getLastSelIndex() == position) {
                ObjectAnimator.ofFloat(iv, "alpha".1f.0).setDuration(duration).start();

                // Background uncheck the animation
                ivBg.animate().scaleX(1.05 f).scaleY(1.05 f)
                        .withEndAction(() -> ivBg.animate().scaleX(1f).scaleY(1f).setDuration(duration))
                        .setDuration(0).start();
            } else {
                iv.setAlpha(0); }}}}Copy the code

Note that it is better to use padding to realize the gap between items, otherwise the itemView may not display fully due to insufficient space after magnification:


      
<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:paddingBottom="7dp"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/m_iv_item_home_bg"
        android:layout_width="match_parent"
        android:layout_height="@dimen/item_wallpaper_h"
        android:scaleType="centerCrop"
        android:adjustViewBounds="true"
        android:paddingLeft="7dp"
        android:paddingRight="7dp"
        android:paddingTop="7dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:src="@mipmap/ic_mine_bg" />

    <ImageView
        android:id="@+id/m_iv_item_home_bg_sel"
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:layout_marginBottom="10dp"
        android:alpha="0"
        app:layout_constraintBottom_toBottomOf="@+id/m_iv_item_home_bg"
        app:layout_constraintEnd_toEndOf="@+id/m_iv_item_home_bg"
        app:layout_constraintStart_toStartOf="@+id/m_iv_item_home_bg"
        android:src="@mipmap/ic_select_bg" />

    <TextView
        android:id="@+id/m_tv_item_home_bg_name"
        style="@style/text_second_s"
        android:paddingTop="9dp"
        app:layout_constraintEnd_toEndOf="@+id/m_iv_item_home_bg"
        app:layout_constraintStart_toStartOf="@+id/m_iv_item_home_bg"
        app:layout_constraintTop_toBottomOf="@+id/m_iv_item_home_bg"
        tools:text=Wallpaper "1" />


</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

Parent class is the package I do, convenient for other places to use, we can refer to it as appropriate:

import android.annotation.SuppressLint;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.xxx.SelectableItem;

import org.jetbrains.annotations.Nullable;

import java.util.List;

/** * Create by ly on 2021/7/21 16:02 */ create by ly on 2021/7/21 16:02 */
public abstract class BaseSingleSelectAdapter<T extends SelectableItem> extends BaseQuickAdapter<T.BaseViewHolder> {
    private int selIndex = -1, lastSelIndex = -1;
    /** * Animation length */
    protected int duration = 300;
    /** * Animation scale factor */
    protected float factor = 0.05 f;

    private boolean showItemAni = true;

    public BaseSingleSelectAdapter(int layoutResId) {
        super(layoutResId);
    }

    public BaseSingleSelectAdapter(int layoutResId, @Nullable List<T> data) {
        super(layoutResId, data);
    }

    public BaseSingleSelectAdapter(int layoutResId, @Nullable List<T> data, boolean showItemAni) {
        super(layoutResId, data);
        this.showItemAni = showItemAni;
    }

    /** * Subclasses need to call this method to get the exact selIndex and animation display item * Created by ly on 2021/7/23 16:04 */
    @Override
    protected void convert(@NonNull BaseViewHolder baseViewHolder, T t) {
        // Select the animation
        if (t.isSelected) {
            selIndex = baseViewHolder.getAdapterPosition();
            if (showItemAni) scaleUp(baseViewHolder.itemView);
        } else {
            if(showItemAni) scaleDown(baseViewHolder.itemView); }}public @Nullable
    T selectOne(int index) {
        if(selIndex ! = index) {// Do not handle clicking on selected cases
            if (selIndex >= 0 && selIndex < getItemCount())
                getItem(selIndex).isSelected = false;
            if (index >= 0 && index < getItemCount()) {
                getItem(index).isSelected = true;
            }

            notifyItemChanged(selIndex);
            notifyItemChanged(index);

            lastSelIndex = selIndex;
            selIndex = index;
        }
        return getSelectItem();
    }

    @SuppressLint("NotifyDataSetChanged")
    public void selectNone(a) {
        if (selIndex >= 0 && selIndex < getData().size()) {
            getData().get(selIndex).isSelected = false;
            notifyItemChanged(selIndex);
        }else {
            for (T datum : getData()) {
                datum.isSelected = false;
            }
            notifyDataSetChanged();
        }
        selIndex = -1;
    }

    public @Nullable
    T getSelectItem(a) {
        return selIndex >= 0 && selIndex < getItemCount() ? getItem(selIndex) : null;
    }

    public int getSelectIndex(a) {
        return selIndex;
    }

    protected int getLastSelIndex(a) {
        return lastSelIndex;
    }

    protected void scaleUp(View view) {
        ViewCompat.animate(view)
                .setDuration(duration)
                .scaleX(1f + factor)
                .scaleY(1f + factor)
                .start();
    }

    protected void scaleDown(View view) {
        ViewCompat.animate(view)
                .setDuration(duration)
                .scaleX(1f)
                .scaleY(1f) .start(); }}Copy the code

Select the generic entity class and use your own class to inherit SelectableItem:

/** * Created by ly on 2021/7/21 16:05 */
public class SelectableItem {
    /** * True: */ is selected
    public boolean isSelected;

}

Copy the code

The above BaseSingleSelectAdapter is suitable for list-radio scenarios and can be inherited according to your own business.

The code relies on third-party libraries: BaseRecyclerViewAdapterHelper, if you use less than, the core code with me also can realize the dynamic effect ~

Ok, the simple selection of animation is complete, I suggest you use animation in the project to improve the user experience.

Thanks for watching.