recyclerview 仿快手头像挂饰的条目选择全代码
时间: 2024-04-27 22:19:45 浏览: 120
好的,以下是一个基于RecyclerView和自定义LayoutManager实现快手头像挂饰的条目选择效果的完整代码:
AvatarDecorationLayoutManager.java:
```
public class AvatarDecorationLayoutManager extends RecyclerView.LayoutManager {
private int mVerticalScrollOffset = 0; // 竖直方向的偏移量
private int mTotalHeight = 0; // 总高度
private int mItemWidth = 0; // 每个条目的宽度
private int mItemHeight = 0; // 每个条目的高度
private int mSpace = 10; // 每个挂饰之间的间距
private int mMaxItemCountInLine = 5; // 每行最大的挂饰数量
private SparseArray<Rect> mItemFrames = new SparseArray<>(); // 保存每个条目的位置信息
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getItemCount() == 0 || state.isPreLayout()) {
return;
}
detachAndScrapAttachedViews(recycler); // 移除所有已经添加的View
mItemWidth = (getWidth() - getPaddingLeft() - getPaddingRight() - (mMaxItemCountInLine - 1) * mSpace) / mMaxItemCountInLine;
mItemHeight = mItemWidth; // 每个条目的高度和宽度相等
int left = getPaddingLeft();
int top = getPaddingTop();
int lineMaxHeight = 0; // 当前行中最高的挂饰的高度
for (int i = 0; i < getItemCount(); i++) {
View view = recycler.getViewForPosition(i);
addView(view);
measureChildWithMargins(view, 0, 0);
int width = getDecoratedMeasuredWidth(view);
int height = getDecoratedMeasuredHeight(view);
if (i % mMaxItemCountInLine == 0) { // 每行第一个挂饰
left = getPaddingLeft();
top += lineMaxHeight + mSpace; // 上一行中最高挂饰的高度加上间距作为当前行的起始位置
lineMaxHeight = 0;
}
Rect rect = new Rect(left, top, left + mItemWidth, top + mItemHeight);
mItemFrames.put(i, rect);
left += mItemWidth + mSpace;
lineMaxHeight = Math.max(lineMaxHeight, height); // 更新当前行中最高挂饰的高度
}
mTotalHeight = top + lineMaxHeight + getPaddingBottom(); // 计算总高度
mTotalHeight = Math.max(mTotalHeight, getVerticalSpace()); // 总高度不能小于RecyclerView的高度
recycleAndFillItems(recycler, state);
}
@Override
public boolean canScrollVertically() {
return true;
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
int travel = dy;
if (mVerticalScrollOffset + dy < 0) {
travel = -mVerticalScrollOffset;
} else if (mVerticalScrollOffset + dy > mTotalHeight - getVerticalSpace()) {
travel = mTotalHeight - getVerticalSpace() - mVerticalScrollOffset;
}
mVerticalScrollOffset += travel;
offsetChildrenVertical(-travel);
recycleAndFillItems(recycler, state);
return travel;
}
private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getItemCount() == 0 || state.isPreLayout()) {
return;
}
Rect visibleRect = new Rect(
getPaddingLeft(),
getPaddingTop() + mVerticalScrollOffset,
getWidth() - getPaddingRight(),
getVerticalSpace() + mVerticalScrollOffset - getPaddingBottom());
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
int position = getPosition(view);
if (!Rect.intersects(visibleRect, mItemFrames.get(position))) {
removeAndRecycleView(view, recycler);
}
}
for (int i = 0; i < getItemCount(); i++) {
if (Rect.intersects(visibleRect, mItemFrames.get(i))) {
View view = recycler.getViewForPosition(i);
measureChildWithMargins(view, 0, 0);
addView(view);
Rect rect = mItemFrames.get(i);
layoutDecoratedWithMargins(view, rect.left, rect.top - mVerticalScrollOffset, rect.right, rect.bottom - mVerticalScrollOffset);
}
}
}
private int getVerticalSpace() {
return getHeight() - getPaddingTop() - getPaddingBottom();
}
}
```
AvatarDecorationAdapter.java:
```
public class AvatarDecorationAdapter extends RecyclerView.Adapter<AvatarDecorationAdapter.ViewHolder> {
private List<AvatarDecoration> mAvatarDecorations;
public AvatarDecorationAdapter(List<AvatarDecoration> avatarDecorations) {
mAvatarDecorations = avatarDecorations;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_avatar_decoration, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
AvatarDecoration avatarDecoration = mAvatarDecorations.get(position);
holder.mAvatarImageView.setImageResource(avatarDecoration.getAvatarResId());
holder.mDecorationImageView.setImageResource(avatarDecoration.getDecorationResId());
holder.itemView.setSelected(avatarDecoration.isSelected());
}
@Override
public int getItemCount() {
return mAvatarDecorations.size();
}
public void toggleSelection(int position) {
AvatarDecoration avatarDecoration = mAvatarDecorations.get(position);
avatarDecoration.setSelected(!avatarDecoration.isSelected());
notifyItemChanged(position);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView mAvatarImageView;
ImageView mDecorationImageView;
public ViewHolder(View itemView) {
super(itemView);
mAvatarImageView = itemView.findViewById(R.id.avatar_image_view);
mDecorationImageView = itemView.findViewById(R.id.decoration_image_view);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((RecyclerView) v.getParent()).getAdapter().notifyItemChanged(getAdapterPosition());
}
});
}
}
}
```
AvatarDecoration.java:
```
public class AvatarDecoration {
private int mAvatarResId;
private int mDecorationResId;
private boolean mSelected;
public AvatarDecoration(int avatarResId, int decorationResId) {
mAvatarResId = avatarResId;
mDecorationResId = decorationResId;
mSelected = false;
}
public int getAvatarResId() {
return mAvatarResId;
}
public void setAvatarResId(int avatarResId) {
mAvatarResId = avatarResId;
}
public int getDecorationResId() {
return mDecorationResId;
}
public void setDecorationResId(int decorationResId) {
mDecorationResId = decorationResId;
}
public boolean isSelected() {
return mSelected;
}
public void setSelected(boolean selected) {
mSelected = selected;
}
}
```
item_avatar_decoration.xml:
```
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:padding="4dp">
<ImageView
android:id="@+id/avatar_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/decoration_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:src="@drawable/ic_decoration"
android:visibility="gone" />
</FrameLayout>
```
使用时,可以在Activity或Fragment中初始化RecyclerView:
```
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new AvatarDecorationLayoutManager());
recyclerView.setAdapter(new AvatarDecorationAdapter(avatarDecorations));
```
其中,avatarDecorations是一个List,保存了所有的头像和挂饰的信息。
在点击事件中,可以通过调用Adapter的toggleSelection方法来切换条目的选中状态。
完整的示例代码可以在我的GitHub仓库中找到:https://github.com/linroid/AvatarDecorationRecyclerView
阅读全文