如何在不更改整个列表的情况下更新项目值?

时间:2020-03-07 16:02:40

标签: android android-recyclerview data-binding android-databinding android-binding-adapter

我正在为Scores App开发Grid RecyclerView。分数将通过API调用每5秒更新一次...

我正在将此library用于我的Recyclerview适配器。

我正在使用Observable Fields & List

我需要更新only the scores (textviews),但是现在RecyclerView每5秒更新一次。

请指引我正确的方向。谢谢!

Full Code in Gist

class DashboardFragment : Fragment() {

private lateinit var dashboardViewModel: DashboardViewModel
private lateinit var binding: FragmentDashboardBinding

lateinit var retrofit: Retrofit
lateinit var apiService: APIService
lateinit var disposable: Disposable

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    dashboardViewModel = ViewModelProvider(this).get(DashboardViewModel::class.java)
    fetchData()
}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    binding = DataBindingUtil
        .inflate(inflater, R.layout.fragment_dashboard, container, false)
    binding.viewModel = dashboardViewModel

    return binding.root
}

fun fetchData() {
    val interceptor = HttpLoggingInterceptor()
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
    val client = OkHttpClient.Builder()
        .addInterceptor(interceptor)
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(30, TimeUnit.SECONDS)
        .build()

    val gson = GsonBuilder()
        .setLenient()
        .create()

    retrofit = Retrofit.Builder()
        .baseUrl(APIService.BASE_URL)
        .client(client)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()

    apiService = this.retrofit.create(APIService::class.java)

    callIndicesEndpoint(null)

    disposable = Observable.interval(1000, 2000, TimeUnit.MILLISECONDS)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ aLong: Long? -> this.refreshIndices(aLong)})
        { throwable: Throwable -> this.onError(throwable) }
}

@SuppressLint("CheckResult")
private fun callIndicesEndpoint(aLong: Long?) {
    val observable =
        apiService.indices
    observable.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
        .map { result: ObservableArrayList<Indices> -> result }
        .subscribe(
            { data: ObservableArrayList<Indices> ->
                this.handleResults(data)
            }
        ) { t: Throwable ->
            this.handleError(t)
        }
}

@SuppressLint("CheckResult")
private fun refreshIndices(aLong: Long?) {
    val observable =
        apiService.indices
    observable.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
        .map { result: ObservableArrayList<Indices> -> result }
        .subscribe({ data: ObservableArrayList<Indices> -> this.refreshResults(data)})
        { t: Throwable ->this.handleError(t)}
}

private fun handleResults(data: ObservableArrayList<Indices>) {
    dashboardViewModel.populate(data)
}


private fun refreshResults(data: ObservableArrayList<Indices>) {
    dashboardViewModel.refresh(data)
}

private fun onError(throwable: Throwable) {
    Log.e(">>>", "ERROR")
}

private fun handleError(t: Throwable) {
    Log.e("> >", t.localizedMessage + " - Err - " + t.cause)
    //Add your error here.
}

override fun onPause() {
    super.onPause()
    disposable.dispose()
}
 }

VIEWMODEL

class DashboardViewModel : ViewModel() {

val indices: ObservableList<Indices> = ObservableArrayList<Indices>()

fun populate(data: ArrayList<Indices>) {
    indices.clear()
    indices.addAll(data)
}

fun refresh(data: ArrayList<Indices>) {
    // How to refresh only Items without recreating the List
}

val itemIds: ItemIds<Any> =
    ItemIds { position, item -> position.toLong() }

val indicesItem: ItemBinding<Indices> =
    ItemBinding.of<Indices>(BR.item, R.layout.item_futures)

 }

4 个答案:

答案 0 :(得分:0)

您可以使用广播接收器功能从recyclerview更新特定项目。

答案 1 :(得分:0)

我认为您需要使用DiffUtil,它将对

有很大帮助

DiffUtil找出已更改的内容,RecyclerView可以使用该信息仅更新已更改,添加,删除或移动的项目,这比重做整个列表要有效得多。

这是您可以用来获得帮助的课程:https://codelabs.developers.google.com/codelabs/kotlin-android-training-diffutil-databinding/#3

答案 2 :(得分:0)

当我有类似的东西时,我使用了DiffUtil。

我必须每X秒更新一次费率,而不能更改整个列表。

所以适配器应该是这样的:

class RatesAdapter :
ListAdapter<Rate, RecyclerView.ViewHolder>(RateDiffCallback()) {
private val baseRateView = 0
private val rateView = 1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    return if (viewType == rateView) {
        RateHolder(
            RateItemBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        )
    } else {
        BaseRateHolder(
            BaseRateLayoutBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        )
    }
}


override fun getItemViewType(position: Int): Int {
    return if (position == 0) {
        baseRateView
    } else rateView
}


override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val rate = getItem(position)
    if (holder.itemViewType == rateView) {
        (holder as RateHolder).bind(rate)
        holder.itemView.setOnClickListener {
            swapper.itemSwap(rate)
        }
    } else {
        (holder as BaseRateHolder).bind(rate)
    }
}

class RateHolder(
    private val binding: RateItemBinding
) : RecyclerView.ViewHolder(binding.root) {
    fun bind(item: Rate) {
        binding.apply {
            rate = item
            executePendingBindings()
        }
    }


}

class BaseRateHolder(
    private val binding: BaseRateLayoutBinding
) : RecyclerView.ViewHolder(binding.root) {
    fun bind(item: Rate) {
        binding.apply {
            rate = item
            executePendingBindings()
        }
    }

    val value = binding.value
}
}

private class RateDiffCallback : DiffUtil.ItemCallback<Rate>() {


override fun areItemsTheSame(oldItem: Rate, newItem: Rate): Boolean {
    return oldItem.currencyCode == newItem.currencyCode
}

override fun areContentsTheSame(oldItem: Rate, newItem: Rate): Boolean {
    return oldItem.rate == newItem.rate
}

}

在这里我在适配器中使用了绑定,如果您不熟悉它,我也非常乐于解释

在活动中: 在onCreate之前:

private val ratesAdapter = RatesAdapter() 

在onCreate中:

ratesList.adapter = ratesAdapter

每当您需要更新适配器(包括第一次调用适配器)时:

ratesAdapter.submitList(rates)

费率是我正在使用的模型类 rates 的可变列表 ratesList 是recyclerview

答案 3 :(得分:0)

尝试:notifyItemChanged

与此:

listView.setItemAnimator(null);