在 Android 开发中,多类型适配器(Multi-type Adapter)是一种常见的适配器模式,允许我们在一个 RecyclerView 中展示不同类型的数据。每个数据项可以根据需要展示不同的布局(ViewType),这种方式在社交应用、新闻应用或电商应用中非常常见,比如新闻列表中可能会包含标题、图片、文本、视频等多种内容类型。

Jetpack Compose 中也可以通过类似的方式实现多类型适配器,虽然 Compose 是声明式的,但我们仍然可以灵活地处理不同类型的数据。

以下是关于 RecyclerView 多类型适配器 和 Jetpack Compose 多类型适配器 的实现和示例。

1. RecyclerView 多类型适配器实现

1.1 设计一个多类型的数据模型

首先,我们需要一个包含不同类型数据的模型。假设我们有一个列表,每个列表项有三种类型:TEXTIMAGEVIDEO

public class Item {
    public enum Type {
        TEXT,
        IMAGE,
        VIDEO
    }

    public Type type;
    public String data; // 该项的数据,如文本、图片URL或视频URL

    // 构造函数
    public Item(Type type, String data) {
        this.type = type;
        this.data = data;
    }
}

1.2 创建适配器

在适配器中,我们会根据每个项的类型,选择不同的布局和 ViewHolder

public class MultiTypeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<Item> itemList;

    // 定义不同的布局类型
    private static final int TYPE_TEXT = 0;
    private static final int TYPE_IMAGE = 1;
    private static final int TYPE_VIDEO = 2;

    public MultiTypeAdapter(List<Item> itemList) {
        this.itemList = itemList;
    }

    @Override
    public int getItemViewType(int position) {
        // 根据数据类型返回不同的 ViewType
        switch (itemList.get(position).type) {
            case TEXT:
                return TYPE_TEXT;
            case IMAGE:
                return TYPE_IMAGE;
            case VIDEO:
                return TYPE_VIDEO;
            default:
                return -1;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case TYPE_TEXT:
                return new TextViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false));
            case TYPE_IMAGE:
                return new ImageViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false));
            case TYPE_VIDEO:
                return new VideoViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_video, parent, false));
            default:
                throw new IllegalArgumentException("Invalid view type");
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Item item = itemList.get(position);
        switch (holder.getItemViewType()) {
            case TYPE_TEXT:
                ((TextViewHolder) holder).bind(item);
                break;
            case TYPE_IMAGE:
                ((ImageViewHolder) holder).bind(item);
                break;
            case TYPE_VIDEO:
                ((VideoViewHolder) holder).bind(item);
                break;
        }
    }

    @Override
    public int getItemCount() {
        return itemList.size();
    }

    // 文本项的 ViewHolder
    class TextViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        TextViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textView);
        }

        void bind(Item item) {
            textView.setText(item.data);
        }
    }

    // 图片项的 ViewHolder
    class ImageViewHolder extends RecyclerView.ViewHolder {
        ImageView imageView;

        ImageViewHolder(View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.imageView);
        }

        void bind(Item item) {
            // 加载图片(使用 Glide 或 Picasso 等库)
            Glide.with(imageView.getContext()).load(item.data).into(imageView);
        }
    }

    // 视频项的 ViewHolder
    class VideoViewHolder extends RecyclerView.ViewHolder {
        VideoView videoView;

        VideoViewHolder(View itemView) {
            super(itemView);
            videoView = itemView.findViewById(R.id.videoView);
        }

        void bind(Item item) {
            // 加载视频(使用 ExoPlayer 或 VideoView)
            videoView.setVideoURI(Uri.parse(item.data));
            videoView.start();
        }
    }
}

1.3 适配器使用

RecyclerView recyclerView = findViewById(R.id.recyclerView);
MultiTypeAdapter adapter = new MultiTypeAdapter(createItems());
recyclerView.setAdapter(adapter);

private List<Item> createItems() {
    List<Item> items = new ArrayList<>();
    items.add(new Item(Item.Type.TEXT, "This is a text item"));
    items.add(new Item(Item.Type.IMAGE, "https://example.com/image.jpg"));
    items.add(new Item(Item.Type.VIDEO, "https://example.com/video.mp4"));
    return items;
}

在这个例子中,我们通过 getItemViewType 来判断每个项的类型,然后在 onCreateViewHolder 中为不同类型的项创建不同的 ViewHolder。每个 ViewHolder 负责处理不同类型的布局。


2. Jetpack Compose 多类型适配器实现

在 Jetpack Compose 中,我们使用 LazyColumn 或 LazyRow 作为 RecyclerView 的替代,来展示多种类型的列表项。我们可以通过条件判断来确定每个项的类型,并渲染不同的 UI 组件。

2.1 定义数据模型

sealed class Item {
    data class TextItem(val text: String) : Item()
    data class ImageItem(val imageUrl: String) : Item()
    data class VideoItem(val videoUrl: String) : Item()
}

2.2 创建 LazyColumn 多类型适配器

@Composable
fun MultiTypeAdapter(items: List<Item>) {
    LazyColumn {
        items(items) { item ->
            when (item) {
                is Item.TextItem -> TextItemView(item)
                is Item.ImageItem -> ImageItemView(item)
                is Item.VideoItem -> VideoItemView(item)
            }
        }
    }
}

@Composable
fun TextItemView(item: Item.TextItem) {
    Text(text = item.text, style = MaterialTheme.typography.h6)
}

@Composable
fun ImageItemView(item: Item.ImageItem) {
    Image(painter = rememberImagePainter(item.imageUrl), contentDescription = null)
}

@Composable
fun VideoItemView(item: Item.VideoItem) {
    // 可以使用 ExoPlayer 或其他 Compose 插件来播放视频
    // 示例仅为占位符
    Text(text = "Video Item: ${item.videoUrl}")
}

2.3 使用 LazyColumn 展示多类型数据

@Composable
fun MultiTypeList() {
    val items = listOf(
        Item.TextItem("This is a text item"),
        Item.ImageItem("https://example.com/image.jpg"),
        Item.VideoItem("https://example.com/video.mp4")
    )
    MultiTypeAdapter(items = items)
}

3. 总结

  • RecyclerView 中的多类型适配器:通过 getItemViewType() 来判断每个项的类型,适配不同的布局,并在 onCreateViewHolder 中根据不同的 ViewType 来创建不同类型的 ViewHolder
  • Jetpack Compose 中的多类型适配器:通过 LazyColumn 和 when 表达式来处理不同类型的列表项,可以根据每个项的类型展示不同的 UI 组件。

这种方式在显示复杂的数据结构时非常有用,能够灵活地展示不同类型的内容。