- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个 RecyclerView Activity 和一个 ViewModel 类。该 Activity 调用 ViewModel 中的一个方法,该方法使用 Web 服务,它还观察一个 LiveData 字段。每次该方法从 Web 服务返回一个项目时,它都会将其设置为 LiveData,因此 Activity 中的观察者会收到通知,因此所有项目都会进入 RecyclerView。正如我在日志和 UI 中看到的那样,可以保证此流程正常工作。
当我在使用 Web 服务的方法中使用 Thread.slepp(500) 进行延迟时,问题就出现了。
它不是将一个项目放入 RecyclerView 然后等待 500 毫秒再放入另一个,而是等待 500 毫秒 * numberOfItems 然后将它们全部绘制在一起。
我可以保证 ViewModel 和 LiveData 设置没有问题,因为日志按预期工作,它打印创建的项目的标题,等待 500 毫秒,打印下一个。所以问题只在于适配器以及如何仅在方法调用完成后才通知它。
我的问题是每次调用观察者时如何通知适配器?
这是我对这三个类的实现:
食谱列表
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,您可能需要使用数据绑定(bind)。我建议你这样做!注意上下文操作不应该在 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)
}
关于android - 我的 RecyclerView 没有得到适当的通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46426352/
在 C# 及其同类语言中,我们总是使用 public string SomeString { get; set;} 但是你也可以使用(我最近才发现这个,而且是在和编译器闲逛的时候发现的) public
我已经为 Controller 中的函数编写了 Swagger 注释,但在生成 swagger-ui 代码时出现错误。以下是我的注释代码 /*** End of Annotation For dele
我正在 PHP 中开发一项服务,该服务使用 exec 函数调用 jar 文件,如下所示: $text = "string with accents á, ó, ú or العربية"; exec(
我正在尝试了解有关在程序中利用/防止缓冲区溢出的方法的更多信息。我知道如果大小是恒定的,下面的代码很容易受到攻击,但是如果大小每次都是随机的怎么办?是否还有办法从堆栈中获取它并以某种方式动态改变溢出字
对于一项学校作业,我应该制作一个可以以小时、分钟和秒为单位存储时间的时间类。一切正常,但仅声明 get 时属性总是返回 0;并设置; private int seconds, minutes, hou
我正在遍历一些测验对象并将结果存储到json变量中。出现"ReferenceError is not defined"错误,不确定原因。 JS代码 // This function will send
使用 Nifi 的 PutDatabaseRecord 处理器在 MySQL 中插入阿拉伯字符(非拉丁语)时,字符被“??????”替换 插入后,阿拉伯字符串被替换为??????。我已经使用 utf8
谁能告诉我为什么 gets(abc) 使用 char[] 而不是使用 int? int abc; char name[] = "lolrofl"; printf("Hello %s.\n",na
为什么在使用 as.POSIXct 转换下面的时间戳时得到所有 NA? > head(tmp$timestamp_utc) [1] Fri Jul 03 00:15:00 EDT 2015 Fri J
def get_submultiples(n): # Get all submultiples of n if n == 1: return [1] i = 2
有没有办法访问基本模型的实际 child ,意思是:继续使用 django Docs 中的示例,让我们假设我正在建模不同的外卖餐厅,它们只是有共同点 姓名 都有deliver方法 至此: class
我正在寻找一个范围的总和,但我总是得到“未定义”。我相信有些东西出现在错误的位置,但我不确定它是什么。 第 1 部分:“编写一个范围函数,它接受两个参数(start 和 end),并返回一个包含从 s
我已将 spring 版本从 4.2.3 更新到 5.0.2,并将安全性从 5.0.1 更新到 5.0.10 并使用 spring -flex版本1.6.0.RC1。 像这样使用 BlazeDS 依赖
我可以输入但在输出中,我得到的结果为零。我使用两门类(class),一门是主要的,是日志,另一门是成绩计算。在成绩计算器中,我编写了方法和构造函数,在日志中,类通过构造函数调用这些方法。 import
我在使用 go 时遇到了构建问题。我想知道这是编译器中的错误还是代码的问题。 // removed the error handling for sake of clarity file, _ :=
我的角色在与盒子互动时出现问题。我有一个 GameObject Player 附加了一个脚本来与游戏中的盒子交互,脚本是: using UnityEngine; using System.Collec
有谁知道为什么我不能在下面生成百分比 codeIshere (第 97-117 行)? var format=d3.format(".1%"); var percent = format(functi
我正在尝试编写图像识别代码,以针对不同动物图像训练系统,这就是代码。我使用 anaconda 作为解释器,使用pycharm作为环境。 import tensorflow as tf import o
我正在尝试在 Java 中初始化 Matcher,但无论字符串是否已初始化且不为 null,都会继续获取 NPE。 这是代码: pattern.compile("\\s"); System.out.p
所以我有这段代码: ; (function (g) { var d = document, i, am = d.createElement('script'), h = d.head || d.g
我是一名优秀的程序员,十分优秀!