You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无需特殊手段实现LiveData<PagedList<X>>到LiveData<PagedList<Y>>映射?

解决方案:通过包装数据源工厂实现领域对象到UI对象的分页映射

不用反射、修改分页库或者强行侵入android.arch.paging包的核心思路,是在数据加载链路中插入类型映射逻辑——通过包装原生的PositionalDataSource和DataSource.Factory,在数据从数据源返回时自动完成领域对象(X)到UI对象(Y)的转换,最终生成符合需求的LiveData<PagedList<Y>>。

步骤1:实现映射版的PositionalDataSource

创建一个通用的包装类,继承自PositionalDataSource<Y>,内部持有原生的PositionalDataSource<X>和映射函数,在加载数据的回调中完成类型转换:

Kotlin 示例

class MappingPositionalDataSource<X, Y>(
    private val originalDataSource: PositionalDataSource<X>,
    private val mapper: (X) -> Y
) : PositionalDataSource<Y>() {

    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<Y>) {
        originalDataSource.loadInitial(params, object : LoadInitialCallback<X>() {
            override fun onResult(data: List<X>, position: Int, totalCount: Int) {
                // 将X列表映射为Y列表后返回
                callback.onResult(data.map(mapper), position, totalCount)
            }
        })
    }

    override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<Y>) {
        originalDataSource.loadRange(params, object : LoadRangeCallback<X>() {
            override fun onResult(data: List<X>) {
                callback.onResult(data.map(mapper))
            }
        })
    }
}

Java 示例

public class MappingPositionalDataSource<X, Y> extends PositionalDataSource<Y> {
    private final PositionalDataSource<X> originalDataSource;
    private final Function<X, Y> mapper;

    public MappingPositionalDataSource(PositionalDataSource<X> originalDataSource, Function<X, Y> mapper) {
        this.originalDataSource = originalDataSource;
        this.mapper = mapper;
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Y> callback) {
        originalDataSource.loadInitial(params, new LoadInitialCallback<X>() {
            @Override
            public void onResult(@NonNull List<X> data, int position, int totalCount) {
                List<Y> mappedData = data.stream().map(mapper).collect(Collectors.toList());
                callback.onResult(mappedData, position, totalCount);
            }
        });
    }

    @Override
    public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Y> callback) {
        originalDataSource.loadRange(params, new LoadRangeCallback<X>() {
            @Override
            public void onResult(@NonNull List<X> data) {
                List<Y> mappedData = data.stream().map(mapper).collect(Collectors.toList());
                callback.onResult(mappedData);
            }
        });
    }
}

步骤2:实现映射版的DataSource.Factory

创建一个包装的DataSource.Factory,用来生成上面的MappingPositionalDataSource,作为原生工厂和映射数据源的桥梁:

Kotlin 示例

class MappingDataSourceFactory<X, Y>(
    private val originalFactory: DataSource.Factory<Int, X>,
    private val mapper: (X) -> Y
) : DataSource.Factory<Int, Y>() {

    override fun create(): DataSource<Int, Y> {
        val originalDataSource = originalFactory.create() as PositionalDataSource<X>
        return MappingPositionalDataSource(originalDataSource, mapper)
    }
}

Java 示例

public class MappingDataSourceFactory<X, Y> extends DataSource.Factory<Integer, Y> {
    private final DataSource.Factory<Integer, X> originalFactory;
    private final Function<X, Y> mapper;

    public MappingDataSourceFactory(DataSource.Factory<Integer, X> originalFactory, Function<X, Y> mapper) {
        this.originalFactory = originalFactory;
        this.mapper = mapper;
    }

    @NonNull
    @Override
    public DataSource<Integer, Y> create() {
        PositionalDataSource<X> originalDataSource = (PositionalDataSource<X>) originalFactory.create();
        return new MappingPositionalDataSource<>(originalDataSource, mapper);
    }
}

步骤3:生成映射后的LiveData<PagedList>

最后用包装后的工厂构建LiveData<PagedList<Y>>,完全复用原生的分页配置和边界回调(如果有的话):

Kotlin 示例

// 假设你已有原生的数据源工厂和分页配置
val originalFactory: DataSource.Factory<Int, X> = YourOriginalDataSourceFactory()
val pagedListConfig = PagedList.Config.Builder()
    .setPageSize(20)
    .setEnablePlaceholders(true)
    .build()

// 转换为LiveData<PagedList<Y>>
val mappedFactory = MappingDataSourceFactory(originalFactory) { x ->
    // 这里编写X到Y的具体映射逻辑
    Y(x.id, x.displayName, x.detailInfo)
}

val mappedLiveData: LiveData<PagedList<Y>> = LivePagedListBuilder(mappedFactory, pagedListConfig)
    .setBoundaryCallback(yourOriginalBoundaryCallback) // 复用原生边界回调
    .build()

Java 示例

// 原生配置
DataSource.Factory<Integer, X> originalFactory = new YourOriginalDataSourceFactory();
PagedList.Config pagedListConfig = new PagedList.Config.Builder()
        .setPageSize(20)
        .setEnablePlaceholders(true)
        .build();

// 转换
MappingDataSourceFactory<X, Y> mappedFactory = new MappingDataSourceFactory<>(originalFactory, x -> {
    // X到Y的具体映射逻辑
    return new Y(x.getId(), x.getDisplayName(), x.getDetailInfo());
});

LiveData<PagedList<Y>> mappedLiveData = new LivePagedListBuilder<>(mappedFactory, pagedListConfig)
        .setBoundaryCallback(yourOriginalBoundaryCallback)
        .build();

方案优势

  • 完全规避访问限制:所有代码都在你的应用包下,不需要依赖分页库的内部私有实现。
  • 保留完整分页特性:原生数据源的分页逻辑、加载状态、边界回调都能完整复用,仅在数据返回时做类型转换。
  • 通用可复用:MappingPositionalDataSource和MappingDataSourceFactory是通用类,可在项目中多次复用不同的X/Y映射场景。

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:11:02