我的RecyclerView没有得到适当的通知

我有一个RecyclerView活动和一个ViewModel类。 该活动在ViewModel中调用一个消耗Web服务的方法,并且还观察LiveData字段。 每次该方法从Web服务中取回一个项目时,都会将其设置为LiveData,因此Activity中的观察者将被通知,因此所有项目都将进入RecyclerView。 这是保证,这个流程正常工作,因为我可以在日志中看到,也在UI中。

当我在使用Web服务的方法中使用Thread.slepp(500)延迟时出现问题。

而不是把物品放入RecyclerView,然后等待500millis,然后再放一个,它等待500毫秒* numberOfItems,然后把它们一起画出来。

我可以保证ViewModel和LiveData设置没有问题,因为日志按预期的意义工作,它打印创建的项目标题,等待500毫秒,打印下一个。 所以问题只在于适配器,以及只有在方法调用完成后才通知它。

我的问题是,如何每次调用观察者时通知适配器?

这里是我的三个类的实现:

RecipeList

class RecipeList : LifecycleActivity() { var recipeList: MutableList<Recipe> = mutableListOf() var adapter: RecipeAdapter? = null var viewModel: RecipeViewModel? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_recipe_list) val ingredients = intent.getStringExtra("ingredients") val term = intent.getStringExtra("term") viewModel = ViewModelProviders.of(this).get(RecipeViewModel::class.java) val url = "http://www.recipepuppy.com/api/?i=${ingredients}onions,garlic&q=${term}" val layoutManager = LinearLayoutManager(this) adapter = RecipeAdapter(this, recipeList) rec_recycler_id.layoutManager = layoutManager rec_recycler_id.adapter = adapter subscribe() viewModel?.getRecipe(url) } fun subscribe() { val observer = Observer<Recipe> { recipe -> if (recipe != null) { Log.d("mike", "subscribe ${recipe?.title} ") recipeList.add(recipe) adapter?.notifyDataSetChanged() } } viewModel?.mRecipe?.observe(this, observer) } } 

RecipeViewModel

 class RecipeViewModel(application: Application): AndroidViewModel(application) { var recipes: MutableLiveData<MutableList<Recipe>>? = MutableLiveData<MutableList<Recipe>>() var mRecipe: MutableLiveData<Recipe> = MutableLiveData() fun getRecipe(url:String){ val requestQueue = Volley.newRequestQueue(this.getApplication()) val recipeRequest = JsonObjectRequest(Request.Method.GET,url, Response.Listener { response: JSONObject -> try { val results = response.getJSONArray("results") for( i in 0..results.length()-1){ var recipeObj = results.getJSONObject(i) var title = recipeObj.getString("title") var link = recipeObj.getString("href") var thumbnail = recipeObj.getString("thumbnail") var ingredients = recipeObj.getString("ingredients") var recipe = Recipe(title,ingredients,thumbnail,link) mRecipe.value = recipe Log.d("mike",title) Thread.sleep(200) } }catch (e: JSONException){ e.printStackTrace() } }, Response.ErrorListener { error: VolleyError? -> try{ Log.d("error",error.toString()) }catch (e: JSONException){ e.printStackTrace() } }) requestQueue?.add(recipeRequest) } 

RecipeAdapter

 class RecipeAdapter(val context: Context, var recipes: MutableList<Recipe>) : RecyclerView.Adapter<RecipeAdapter.ViewHolder>() { override fun getItemCount(): Int = recipes.size override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder { val view = LayoutInflater.from(context).inflate(R.layout.recipe_rec_row, null) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder?, position: Int) { holder?.bindViews(recipes[position]) } inner class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) { fun bindViews(recipe: Recipe) { itemView.textView7.text = recipe.title itemView.textView9.text = recipe.ingredients itemView.button6.setOnClickListener() { if(!recipe.link.trim().isEmpty()) context.startActivity<ShowLinkAct>("url" to recipe.link) else context.toast("No link available") } if (!recipe.thumbnail.isEmpty()) { Picasso.with(context) .load(recipe.thumbnail) .placeholder(android.R.drawable.ic_menu_report_image) .error(android.R.drawable.ic_menu_report_image) .into(itemView.imageView) } else { Picasso.with(context).load(android.R.drawable.ic_menu_report_image).into(itemView.imageView) } } } } 

我期待您的建议,提前谢谢

我建议您将适配器的数据存储在适配器中。 如果您正在使用AAC,您还应该检查GithubBrowser的样本。 这里有一点(未经测试)的样本。

警告 :您不应该在RecyclerView中使用上下文操作,因为您可能会泄漏。

BaseAdapter(所有的适配器扩展了这个具有DiffUtil的适配器)

 abstract class DataBoundListAdapter<T, V : ViewDataBinding> : RecyclerView.Adapter<DataBoundViewHolder<V>>() { val log = AnkoLogger(javaClass.simpleName) private var items: List<T>? = null private var dataVersion = 0 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataBoundViewHolder<V> { val binding = createBinding(parent) return DataBoundViewHolder(binding) } protected abstract fun createBinding(parent: ViewGroup): V override fun onBindViewHolder(holder: DataBoundViewHolder<V>, position: Int) { bind(holder.binding, items!![position]) holder.binding.executePendingBindings() } @SuppressLint("StaticFieldLeak") @MainThread fun replace(update: List<T>?) { dataVersion++ if (items == null) { if (update == null) { return } items = update notifyDataSetChanged() } else if (update == null) { val oldSize = items!!.size items = null notifyItemRangeRemoved(0, oldSize) } else { val startVersion = dataVersion val oldItems = items object : AsyncTask<Void, Void, DiffUtil.DiffResult>() { override fun doInBackground(vararg voids: Void): DiffUtil.DiffResult { return DiffUtil.calculateDiff(object : DiffUtil.Callback() { override fun getOldListSize(): Int { return oldItems!!.size } override fun getNewListSize(): Int { return update.size } override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldItem = oldItems!![oldItemPosition] val newItem = update[newItemPosition] return this@DataBoundListAdapter.areItemsTheSame(oldItem, newItem) } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldItem = oldItems!![oldItemPosition] val newItem = update[newItemPosition] return this@DataBoundListAdapter.areContentsTheSame(oldItem, newItem) } }) } override fun onPostExecute(diffResult: DiffUtil.DiffResult) { if (startVersion != dataVersion) { // ignore update return } items = update diffResult.dispatchUpdatesTo(this@DataBoundListAdapter) } }.execute() } } protected abstract fun bind(binding: V, item: T) protected abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean protected abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean override fun getItemCount(): Int { return if (items == null) 0 else items!!.size } } 

这是一个示例适配器。 如果您使用AAC,您可能需要使用数据绑定。 我建议你的情况! 注意上下文操作不应该在RecyclerView中,因为你可能会泄漏。

 class RecipeAdapter(private val dataBindingComponent: DataBindingComponent, private val yourVm: ViewModel, private val context: Context) : DataBoundListAdapter<Recipe, RecipeRecRowBinding>() { override fun createBinding(parent: ViewGroup): RecipeRecRowBinding { val binding = DataBindingUtil.inflate<RecipeRecRowBinding>(LayoutInflater.from(parent.context), R.layout.recipe_rec_row, parent, false, dataBindingComponent) return binding } override fun bind(binding: RecipeRecRowBinding, recipe: Recipe) { binding.model = recipe binding.viewModel = yourVm binding.itemView.textView7.text = recipe.title binding.itemView.textView9.text = recipe.ingredients binding.itemView.button6.setOnClickListener() { if(!recipe.link.trim().isEmpty()) //ohoh, you shouldnt call something on your activity within your adapter context.startActivity<ShowLinkAct>("url" to recipe.link) else //ohoh, you shouldnt call something on your activity within your adapter context.toast("No link available") } if (!recipe.thumbnail.isEmpty()) { Picasso.with(context) .load(recipe.thumbnail) .placeholder(android.R.drawable.ic_menu_report_image) .error(android.R.drawable.ic_menu_report_image) .into(itemView.imageView) } else { Picasso.with(context).load(android.R.drawable.ic_menu_report_image).into(itemView.imageView) } } override fun areItemsTheSame(oldItem: Recipe, newItem: Recipe) = oldItem.id == newItem.id override fun areContentsTheSame(oldItem: Recipe, newItem: Recipe) = oldItem.equals(newItem) } 

最后,您的用户将数据推送到您的适配器并关心更改(DiffUtil)

 fun subscribe() { val observer = Observer<Recipe> { if (it!= null) { // it cant be null since you validate it here Log.d("mike", "subscribe ${it.title} ") adapter.replace(it) } } viewModel?.mRecipe?.observe(this, observer) }