Veri akışlarını dönüştürme

Sayfalanmış verileri kullanıyorsanız genellikle veri akışını dönüştürebilirsiniz. Örneğin, her ekip üyesinin veya sunumdan önce öğeleri farklı bir türe dönüştürerek kullanıcı arayüzü. Veri akışı dönüştürme için bir diğer yaygın kullanım alanı da liste, ayırıcılar ile kullanılabilir.

Daha genel olarak ifade etmek gerekirse dönüşümleri doğrudan veri akışına uygulayarak ile birlikte kod deposu ve kullanıcı arayüzü yapılarınızı ayrı tutun.

Bu sayfada, Numaralandırma Aracı'nın temel kullanımıyla ilgili bilginiz olduğu varsayılır. kitaplığı'nda bulabilirsiniz.

Temel dönüşümleri uygulama

Çünkü PagingData bir akışa yerleştirilmiş olarak, dönüşüm işlemlerini ve sunma aşamaları arasında otomatik olarak geçiş yapmayı unutmayın.

Akıştaki her PagingData nesnesine dönüşüm uygulamak için dönüşümleri bir map() akışta işlem:

Kotlin

pager.flow // Type is Flow<PagingData<User>>.
  // Map the outer stream so that the transformations are applied to
  // each new generation of PagingData.
  .map { pagingData ->
    // Transformations in this block are applied to the items
    // in the paged data.
}

Java

PagingRx.getFlowable(pager) // Type is Flowable<PagingData<User>>.
  // Map the outer stream so that the transformations are applied to
  // each new generation of PagingData.
  .map(pagingData -> {
    // Transformations in this block are applied to the items
    // in the paged data.
  });

Java

// Map the outer stream so that the transformations are applied to
// each new generation of PagingData.
Transformations.map(
  // Type is LiveData<PagingData<User>>.
  PagingLiveData.getLiveData(pager),
  pagingData -> {
    // Transformations in this block are applied to the items
    // in the paged data.
  });

Verileri dönüştürme

Bir veri akışındaki en temel işlem, veri akışındaki türü. PagingData nesnesine erişim elde ettikten sonra bir map() gerçekleştirebilirsiniz PagingData içindeki sayfalı listedeki her bir bağımsız öğe için işlem nesnesini tanımlayın.

Bunun yaygın kullanım alanlarından biri, ağ veya veritabanı katmanı nesnesini özellikle kullanıcı arayüzü katmanında kullanılan bir nesnedir. Aşağıdaki örnekte, şu harita işlemi türünü uygulamak için:

Kotlin

pager.flow // Type is Flow<PagingData<User>>.
  .map { pagingData ->
    pagingData.map { user -> UiModel(user) }
  }

Java

// Type is Flowable<PagingData<User>>.
PagingRx.getFlowable(pager)
  .map(pagingData ->
    pagingData.map(UiModel.UserModel::new)
  )

Java

Transformations.map(
  // Type is LiveData<PagingData<User>>.
  PagingLiveData.getLiveData(pager),
  pagingData ->
    pagingData.map(UiModel.UserModel::new)
)

Yaygın olarak kullanılan bir diğer veri dönüşümü ise kullanıcıdan sorgu gibi bir giriş almaktır. dizesine dönüştürülebilir ve görüntülenecek istek çıkışına dönüştürülebilir. Bunu ayarlama kullanıcının sorgu girişini dinlemeyi ve yakalamayı gerektirir. isteğinde bulunmak ve sorgu sonucunu kullanıcı arayüzüne geri aktarmaktır.

Akış API'si kullanarak sorgu girişini dinleyebilirsiniz. Akış referansını muhafaza et ViewModel içinde. Kullanıcı arayüzü katmanına doğrudan erişimi olmamalıdır, bunun yerine kullanıcı sorgusunun ViewModel'ini bildirmek için bir işlev tanımlayın.

Kotlin

private val queryFlow = MutableStateFlow("")

fun onQueryChanged(query: String) {
  queryFlow.value = query
}

Java

private BehaviorSubject<String> querySubject = BehaviorSubject.create("");

public void onQueryChanged(String query) {
  queryFlow.onNext(query)
}

Java

private MutableLiveData<String> queryLiveData = new MutableLiveData("");

public void onQueryChanged(String query) {
  queryFlow.setValue(query)
}

Veri akışındaki sorgu değeri değiştiğinde, sorgu değerini istenen veri türüne dönüştürür ve sonucu kullanıcı arayüzüne döndürür katmanıdır. Özel dönüştürme işlevi, kullandığınız dile ve çerçeveye ancak hepsi benzer işlevler sunuyor.

Kotlin

val querySearchResults = queryFlow.flatMapLatest { query ->
  // The database query returns a Flow which is output through
  // querySearchResults
  userDatabase.searchBy(query)
}

Java

Observable<User> querySearchResults =
  querySubject.switchMap(query -> userDatabase.searchBy(query));

Java

LiveData<User> querySearchResults = Transformations.switchMap(
  queryLiveData,
  query -> userDatabase.searchBy(query)
);

flatMapLatest veya switchMap gibi işlemler kullandığınızda yalnızca en son sonuçlar kullanıcı arayüzüne döndürülür. Kullanıcı sorgu girişini değiştirirse veritabanı işlemi tamamlanmadan önce bu işlemler sonuçları siler çıkarıp yeni aramayı hemen başlatabilirsiniz.

Verileri filtrele

Sık yapılan diğer bir işlem de filtrelemedir. Verileri ölçütlere göre filtreleyebilirsiniz veya gizlenmesi gereken verileri kullanıcı arayüzünden kaldırabilirsiniz. başka ölçütlere göre yapılır.

Bu filtre işlemlerini map() çağrısının içine yerleştirmeniz gerekir, çünkü PagingData nesnesine uygulanır. Veriler filtrelenerek PagingData, yeni PagingData örneği kullanıcı arayüzü katmanına görüntüleyin.

Kotlin

pager.flow // Type is Flow<PagingData<User>>.
  .map { pagingData ->
    pagingData.filter { user -> !user.hiddenFromUi }
  }

Java

// Type is Flowable<PagingData<User>>.
PagingRx.getFlowable(pager)
  .map(pagingData ->
    pagingData.filter(user -> !user.isHiddenFromUi())
  )
}

Java

Transformations.map(
  // Type is LiveData<PagingData<User>>.
  PagingLiveData.getLiveData(pager),
  pagingData ->
    pagingData.filter(user -> !user.isHiddenFromUi())
)

Liste ayırıcıları ekleyin

Sayfalama kitaplığı dinamik liste ayırıcılarını destekler. Listeyi iyileştirebilirsiniz doğrudan veri akışına ayırıcı ekleyerek okunabilirliği RecyclerView liste öğesi. Sonuç olarak, ayırıcılar tam olarak ViewHolder nesneleri sayesinde etkileşim, erişilebilirlik odağı ve View tarafından sağlanan diğer özellikler.

Sayfalı listenize ayırıcılar eklemek için izlemeniz gereken üç adım vardır:

  1. Ayırıcı öğeleri içerecek şekilde kullanıcı arayüzü modelini dönüştürün.
  2. Yükleme arasına ayırıcıları dinamik olarak eklemek için veri akışını dönüştürün verileri sunmaktan ibaret değildir.
  3. Ayırıcı öğeleri işleyecek şekilde kullanıcı arayüzünü güncelleyin.
ziyaret edin.

Kullanıcı arayüzü modelini dönüştürme

Sayfalama kitaplığı, liste ayırıcıları RecyclerView içine gerçek olarak ekler ancak ayırıcı öğeler, veri öğelerinden ayırt edilebilmelidir farklı bir ViewHolder türüne bağlanmalarını sağlamak için ayrı bir kullanıcı arayüzü sunar. Çözüm, Kotlin mühürlü bir sınıf . Alternatif olarak liste öğesi sınıfınız ve kampanyalarınıza göre genişletilen bir temel sınıf oluşturabilirsiniz. ayırıcı sınıfıdır.

User öğeden oluşan sayfalı bir listeye ayırıcılar eklemek istediğinizi varsayalım. İlgili içeriği oluşturmak için kullanılan aşağıdaki snippet, örneklerin kullanılabileceği bir temel sınıfın nasıl oluşturulacağını gösterir bir UserModel veya SeparatorModel:

Kotlin

sealed class UiModel {
  class UserModel(val id: String, val label: String) : UiModel() {
    constructor(user: User) : this(user.id, user.label)
  }

  class SeparatorModel(val description: String) : UiModel()
}

Java

class UiModel {
  private UiModel() {}

  static class UserModel extends UiModel {
    @NonNull
    private String mId;
    @NonNull
    private String mLabel;

    UserModel(@NonNull String id, @NonNull String label) {
      mId = id;
      mLabel = label;
    }

    UserModel(@NonNull User user) {
      mId = user.id;
      mLabel = user.label;
    }

    @NonNull
    public String getId() {
      return mId;
    }

    @NonNull
    public String getLabel() {
      return mLabel;
      }
    }

    static class SeparatorModel extends UiModel {
    @NonNull
    private String mDescription;

    SeparatorModel(@NonNull String description) {
      mDescription = description;
    }

    @NonNull
    public String getDescription() {
      return mDescription;
    }
  }
}

Java

class UiModel {
  private UiModel() {}

  static class UserModel extends UiModel {
    @NonNull
    private String mId;
    @NonNull
    private String mLabel;

    UserModel(@NonNull String id, @NonNull String label) {
      mId = id;
      mLabel = label;
    }

    UserModel(@NonNull User user) {
      mId = user.id;
      mLabel = user.label;
    }

    @NonNull
    public String getId() {
      return mId;
    }

    @NonNull
    public String getLabel() {
      return mLabel;
      }
    }

    static class SeparatorModel extends UiModel {
    @NonNull
    private String mDescription;

    SeparatorModel(@NonNull String description) {
      mDescription = description;
    }

    @NonNull
    public String getDescription() {
      return mDescription;
    }
  }
}

Veri akışını dönüştürme

Veri akışını yükledikten sonra ve öncesinde dönüştürme işlemleri uygulamanız gerekir. göstermeniz gerekir. Dönüşüm işlemleri aşağıdaki işlemleri yapmalıdır:

  • Yüklenen liste öğelerini, yeni temel öğe türünü yansıtacak şekilde dönüştürün.
  • Ayırıcıları eklemek için PagingData.insertSeparators() yöntemini kullanın.

Dönüştürme işlemleri hakkında daha fazla bilgi edinmek için Temel bilgileri uygulama dönüşümleri hakkında daha fazla bilgi edinin.

Aşağıdaki örnekte Ayırıcılar içeren PagingData<UiModel> akışına PagingData<User> akışı eklendi:

Kotlin

pager.flow.map { pagingData: PagingData<User> ->
  // Map outer stream, so you can perform transformations on
  // each paging generation.
  pagingData
  .map { user ->
    // Convert items in stream to UiModel.UserModel.
    UiModel.UserModel(user)
  }
  .insertSeparators<UiModel.UserModel, UiModel> { before, after ->
    when {
      before == null -> UiModel.SeparatorModel("HEADER")
      after == null -> UiModel.SeparatorModel("FOOTER")
      shouldSeparate(before, after) -> UiModel.SeparatorModel(
        "BETWEEN ITEMS $before AND $after"
      )
      // Return null to avoid adding a separator between two items.
      else -> null
    }
  }
}

Java

// Map outer stream, so you can perform transformations on each
// paging generation.
PagingRx.getFlowable(pager).map(pagingData -> {
  // First convert items in stream to UiModel.UserModel.
  PagingData<UiModel> uiModelPagingData = pagingData.map(
    UiModel.UserModel::new);

  // Insert UiModel.SeparatorModel, which produces PagingData of
  // generic type UiModel.
  return PagingData.insertSeparators(uiModelPagingData,
    (@Nullable UiModel before, @Nullable UiModel after) -> {
      if (before == null) {
        return new UiModel.SeparatorModel("HEADER");
      } else if (after == null) {
        return new UiModel.SeparatorModel("FOOTER");
      } else if (shouldSeparate(before, after)) {
        return new UiModel.SeparatorModel("BETWEEN ITEMS "
          + before.toString() + " AND " + after.toString());
      } else {
        // Return null to avoid adding a separator between two
        // items.
        return null;
      }
    });
});

Java

// Map outer stream, so you can perform transformations on each
// paging generation.
Transformations.map(PagingLiveData.getLiveData(pager),
  pagingData -> {
    // First convert items in stream to UiModel.UserModel.
    PagingData<UiModel> uiModelPagingData = pagingData.map(
      UiModel.UserModel::new);

    // Insert UiModel.SeparatorModel, which produces PagingData of
    // generic type UiModel.
    return PagingData.insertSeparators(uiModelPagingData,
      (@Nullable UiModel before, @Nullable UiModel after) -> {
        if (before == null) {
          return new UiModel.SeparatorModel("HEADER");
        } else if (after == null) {
          return new UiModel.SeparatorModel("FOOTER");
        } else if (shouldSeparate(before, after)) {
          return new UiModel.SeparatorModel("BETWEEN ITEMS "
            + before.toString() + " AND " + after.toString());
        } else {
          // Return null to avoid adding a separator between two
          // items.
          return null;
        }
      });
  });

Kullanıcı arayüzündeki tutma yeri ayırıcıları

Son adım, kullanıcı arayüzünü ayırıcı öğe türüne uygun olacak şekilde değiştirmektir. Ayırıcı öğeleriniz için düzen ve görünüm tutucu oluşturup listeyi değiştirme özelliğini kullanarak RecyclerView.ViewHolder adlı bir bağdaştırıcıyı birden çok görünüm sahibini ele alabilir. Alternatif olarak, projede hem öğe hem de ayırıcı görünüm sahibi sınıflarının genişletildiği temel sınıfları kullanın.

Liste bağdaştırıcınızda aşağıdaki değişiklikleri de yapmanız gerekir:

  • onCreateViewHolder() ve onBindViewHolder() yöntemlerine vaka ekleyin: ayırıcı liste öğelerini hesaba katar.
  • Yeni bir karşılaştırıcı uygulama.

Kotlin

class UiModelAdapter :
  PagingDataAdapter<UiModel, RecyclerView.ViewHolder>(UiModelComparator) {

  override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
  ) = when (viewType) {
    R.layout.item -> UserModelViewHolder(parent)
    else -> SeparatorModelViewHolder(parent)
  }

  override fun getItemViewType(position: Int) {
    // Use peek over getItem to avoid triggering page fetch / drops, since
    // recycling views is not indicative of the user's current scroll position.
    return when (peek(position)) {
      is UiModel.UserModel -> R.layout.item
      is UiModel.SeparatorModel -> R.layout.separator_item
      null -> throw IllegalStateException("Unknown view")
    }
  }

  override fun onBindViewHolder(
    holder: RecyclerView.ViewHolder,
    position: Int
  ) {
    val item = getItem(position)
    if (holder is UserModelViewHolder) {
      holder.bind(item as UserModel)
    } else if (holder is SeparatorModelViewHolder) {
      holder.bind(item as SeparatorModel)
    }
  }
}

object UiModelComparator : DiffUtil.ItemCallback<UiModel>() {
  override fun areItemsTheSame(
    oldItem: UiModel,
    newItem: UiModel
  ): Boolean {
    val isSameRepoItem = oldItem is UiModel.UserModel
      && newItem is UiModel.UserModel
      && oldItem.id == newItem.id

    val isSameSeparatorItem = oldItem is UiModel.SeparatorModel
      && newItem is UiModel.SeparatorModel
      && oldItem.description == newItem.description

    return isSameRepoItem || isSameSeparatorItem
  }

  override fun areContentsTheSame(
    oldItem: UiModel,
    newItem: UiModel
  ) = oldItem == newItem
}

Java

class UiModelAdapter extends PagingDataAdapter<UiModel, RecyclerView.ViewHolder> {
  UiModelAdapter() {
    super(new UiModelComparator(), Dispatchers.getMain(),
      Dispatchers.getDefault());
  }

  @NonNull
  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
    int viewType) {
    if (viewType == R.layout.item) {
      return new UserModelViewHolder(parent);
    } else {
      return new SeparatorModelViewHolder(parent);
    }
  }

  @Override
  public int getItemViewType(int position) {
    // Use peek over getItem to avoid triggering page fetch / drops, since
    // recycling views is not indicative of the user's current scroll position.
    UiModel item = peek(position);
    if (item instanceof UiModel.UserModel) {
      return R.layout.item;
    } else if (item instanceof UiModel.SeparatorModel) {
      return R.layout.separator_item;
    } else {
      throw new IllegalStateException("Unknown view");
    }
  }

  @Override
  public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,
    int position) {
    if (holder instanceOf UserModelViewHolder) {
      UserModel userModel = (UserModel) getItem(position);
      ((UserModelViewHolder) holder).bind(userModel);
    } else {
      SeparatorModel separatorModel = (SeparatorModel) getItem(position);
      ((SeparatorModelViewHolder) holder).bind(separatorModel);
    }
  }
}

class UiModelComparator extends DiffUtil.ItemCallback<UiModel> {
  @Override
  public boolean areItemsTheSame(@NonNull UiModel oldItem,
    @NonNull UiModel newItem) {
    boolean isSameRepoItem = oldItem instanceof UserModel
      && newItem instanceof UserModel
      && ((UserModel) oldItem).getId().equals(((UserModel) newItem).getId());

    boolean isSameSeparatorItem = oldItem instanceof SeparatorModel
      && newItem instanceof SeparatorModel
      && ((SeparatorModel) oldItem).getDescription().equals(
      ((SeparatorModel) newItem).getDescription());

    return isSameRepoItem || isSameSeparatorItem;
  }

  @Override
  public boolean areContentsTheSame(@NonNull UiModel oldItem,
    @NonNull UiModel newItem) {
    return oldItem.equals(newItem);
  }
}

Java

class UiModelAdapter extends PagingDataAdapter<UiModel, RecyclerView.ViewHolder> {
  UiModelAdapter() {
    super(new UiModelComparator(), Dispatchers.getMain(),
      Dispatchers.getDefault());
  }

  @NonNull
  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
    int viewType) {
    if (viewType == R.layout.item) {
      return new UserModelViewHolder(parent);
    } else {
      return new SeparatorModelViewHolder(parent);
    }
  }

  @Override
  public int getItemViewType(int position) {
    // Use peek over getItem to avoid triggering page fetch / drops, since
    // recycling views is not indicative of the user's current scroll position.
    UiModel item = peek(position);
    if (item instanceof UiModel.UserModel) {
      return R.layout.item;
    } else if (item instanceof UiModel.SeparatorModel) {
      return R.layout.separator_item;
    } else {
      throw new IllegalStateException("Unknown view");
    }
  }

  @Override
  public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,
    int position) {
    if (holder instanceOf UserModelViewHolder) {
      UserModel userModel = (UserModel) getItem(position);
      ((UserModelViewHolder) holder).bind(userModel);
    } else {
      SeparatorModel separatorModel = (SeparatorModel) getItem(position);
      ((SeparatorModelViewHolder) holder).bind(separatorModel);
    }
  }
}

class UiModelComparator extends DiffUtil.ItemCallback<UiModel> {
  @Override
  public boolean areItemsTheSame(@NonNull UiModel oldItem,
    @NonNull UiModel newItem) {
    boolean isSameRepoItem = oldItem instanceof UserModel
      && newItem instanceof UserModel
      && ((UserModel) oldItem).getId().equals(((UserModel) newItem).getId());

    boolean isSameSeparatorItem = oldItem instanceof SeparatorModel
      && newItem instanceof SeparatorModel
      && ((SeparatorModel) oldItem).getDescription().equals(
      ((SeparatorModel) newItem).getDescription());

    return isSameRepoItem || isSameSeparatorItem;
  }

  @Override
  public boolean areContentsTheSame(@NonNull UiModel oldItem,
    @NonNull UiModel newItem) {
    return oldItem.equals(newItem);
  }
}

Yinelenen işlerden kaçının

Kaçınılması gereken önemli sorunlardan biri, uygulamanın gereksiz işler yapmasını sağlamaktır. Veri getiriliyor: çok maliyetli bir işlemdir ve veri dönüştürme işlemleri de değerli zaman alabilir. Veriler yüklenip kullanıcı arayüzünde görüntülenmek üzere hazırlandıktan sonra kaydedilecektir. ve kullanıcı arayüzünün yeniden oluşturulması gerekebilir.

cachedIn() işlemi, gerçekleşen tüm dönüşümlerin sonuçlarını önbelleğe alır girin. Bu nedenle, cachedIn(), ViewModel'inizdeki son çağrı olmalıdır.

Kotlin

pager.flow // Type is Flow<PagingData<User>>.
  .map { pagingData ->
    pagingData.filter { user -> !user.hiddenFromUi }
      .map { user -> UiModel.UserModel(user) }
  }
  .cachedIn(viewModelScope)

Java

// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
PagingRx.cachedIn(
  // Type is Flowable<PagingData<User>>.
  PagingRx.getFlowable(pager)
    .map(pagingData -> pagingData
      .filter(user -> !user.isHiddenFromUi())
      .map(UiModel.UserModel::new)),
  viewModelScope);
}

Java

// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
PagingLiveData.cachedIn(
  Transformations.map(
    // Type is LiveData<PagingData<User>>.
    PagingLiveData.getLiveData(pager),
    pagingData -> pagingData
      .filter(user -> !user.isHiddenFromUi())
      .map(UiModel.UserModel::new)),
  viewModelScope);

cachedIn() uygulamasını PagingData akışıyla kullanma hakkında daha fazla bilgi için bkz. Şu etkinlikten oluşan bir akış kur: PagingData.

Ek kaynaklar

Sayfalama kitaplığı hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın:

Codelab'ler

Örnekler

ziyaret edin. ziyaret edin.