Как обновить значения элемента без изменения всего списка? - PullRequest
2 голосов
/ 07 марта 2020

Я работаю над Grid RecyclerView for Scores App. Результаты будут обновляться каждые 5 секунд из API Call ...

Я использую эту библиотеку для моего адаптера Recyclerview.

Я использую Observable Fields & List.

Мне нужно обновить only the scores (textviews), но теперь RecyclerView обновляется каждые 5 секунд.

Пожалуйста, ведите меня в правильном направлении. Спасибо!

Полный код в 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 голосов
/ 25 марта 2020

попробуйте это: notifyItemChanged

с этим:

listView.setItemAnimator(null);
0 голосов
/ 23 марта 2020

Я думаю, что вам нужно использовать DiffUtil, это будет очень полезно, так как

DiffUtil выяснит, что изменилось, RecyclerView может использовать эту информацию для обновления только элементов, которые были изменены, добавлены, удалены или перемещены, что намного эффективнее, чем повторение всего списка.

Вот курс, который вы можете использовать, чтобы получить его: https://codelabs.developers.google.com/codelabs/kotlin-android-training-diffutil-databinding/#3

0 голосов
/ 24 марта 2020

Когда у меня было что-то подобное, я использовал 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)

Rate - это класс модели, который я использую rate - это изменяемый список rateList это переработчик вид

0 голосов
/ 18 марта 2020

Вы можете использовать функциональность широковещательного приемника для обновления определенных элементов из переработчика.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...