【问题标题】:How to use databinding with generics in an adapter class?如何在适配器类中使用数据绑定和泛型?
【发布时间】:2020-08-15 12:09:20
【问题描述】:

我正在尝试在我的适配器类中使用泛型。这是我尝试过的:

class ItemAdapter<I>(
    options: FirestoreRecyclerOptions<I>
): FirestoreRecyclerAdapter<I, ItemAdapter<I>.ItemViewHolder<I>>(options) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder<I> {
        val layoutInflater = LayoutInflater.from(parent.context)
        val dataBinding = inflate(layoutInflater, parent, false)
        return ItemViewHolder(dataBinding)
    }

    override fun onBindViewHolder(holder: ItemViewHolder<I>, position: Int, item: I) {
        holder.bindItem(item)
    }

    inner class ItemViewHolder<I>(
        private val dataBinding: ItemDataBinding
    ) : RecyclerView.ViewHolder(dataBinding.root) {
        fun bindItem(item: I) {
            dataBinding.item = item //Here is the problem
        }
    }
}

这是我的布局文件:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data class="ItemDataBinding">
        <variable
            name="item"
            type="com.example.myapp.Item" />
    </data>

    <TextView
        android:id="@+id/item_name_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{item.name}" />
</layout>

我添加了 item 对象,因此我可以访问 name 字段。然而,Android Studio 在这行抱怨:

dataBinding.item = item

与:

Type mismatch.
Required: Item!
Found: I

如何正确使用泛型并摆脱这个问题?谢谢

编辑:

我不需要将特定的类设为通用。我需要以某种方式将该item 对象转换为特定类的对象。我试图在名为classType: Class&lt;I&gt; 的类中添加一个新属性并使用:

dataBinding.item = item as classType

但没有任何运气。我也试过:

dataBinding.item = item as I

再次没有运气。

【问题讨论】:

  • 这能回答你的问题吗? Data binding generic variable
  • @ADM 不,因为我不想让Item 类通用,我需要以某种方式使布局文件中的项目对象以某种方式通用。该项目可以成为Product 对象、User 对象等等。
  • 那么你的变量类型应该是 item 的父类型而不是 item ..
  • @ADM 我不明白你在上一条评论中所说的内容。
  • @ADM 我已经编辑了我的问题。

标签: android kotlin generics google-cloud-firestore android-databinding


【解决方案1】:

错误提示,您需要将项目类型转换为 Item 而不是 I。这是有道理的,因为您的布局声明了类型为Item 的变量。然而,ViewHolder 提供了一个无约束类型的对象I,它显然不符合Item

您可以将泛型类型I 约束为Item 的子类型,而不是强制转换:

class ItemAdapter<I: Item>(...) {
...
    inner class ItemViewHolder<I: Item>(...) {...}
}

这应该使分配dataBinding.item = item 有效。

有点不相关:我认为没有必要为内部类重新声明泛型类型I。您应该可以只使用外部类ItemAdapter&lt;I: Item&gt;I

我不得不承认我没有完全理解你的代码,所以我可能遗漏了一些东西。供您参考,这是我在项目中使用的具有数据绑定的适配器的非常相似的实现。它允许可配置的布局。它唯一假设的是一个名为 model 的绑定变量。

class DataBindingListAdapter<T>(@LayoutRes private val itemLayout: Int, diffCallback: DiffUtil.ItemCallback<T>) : ListAdapter<T, DataBindingListAdapter<T>.ViewHolder>(diffCallback) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, itemLayout, parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    inner class ViewHolder(private val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(model: T?) {
            binding.setVariable(BR.model, model)
            binding.executePendingBindings()
        }
    }

}

【讨论】:

  • 嗨 aax!我很抱歉这么晚才回答。无论如何,谢谢你花时间回答我的问题,你是唯一一个明白我在找什么的人。但是,使用I: Item not 将解决我的问题,这是因为我只需要ItemAdapter&lt;I&gt;,因为I 应该变成Item,在另一个类中应该变成Product 等等。关于您答案的第二部分,是的,我确实可以使用外部班级的I。您对如何解决此问题有任何其他想法吗?投票赞成。
  • 我明白了。我在您的代码中没有得到什么:“inflate”方法来自哪里以及您在哪里定义要使用的布局文件?
  • 可能可行的是设置绑定变量,就像我在示例中所做的那样,使用无类型的 setVariable 方法。您只需要在所有布局文件中一致地命名变量(例如 model 而不是 item/product/...)。
  • ** 我已经直接导入了import com.example.my_app.databinding.ItemDataBinding.inflate。在我的 XML 文件中添加这个 &lt;data class="ItemDataBinding"&gt; 就足以知道哪个布局被夸大了。
  • 让我尝试使用binding.setVariable(BR.model, model),如您的回答中所述,我会回复您。
【解决方案2】:

带有数据绑定的通用适配器类:

class BaseAdapter<S>(
    private val mContext: Context?,
    private var list: List<S>?,//generic list can take any Object 
    private val layoutId: Int,// the item_layout 
    private val listener: BaseAdapterListener? =null// call back inside your view
) : RecyclerView.Adapter<BaseAdapter.DataBindingViewHolder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DataBindingViewHolder {
        val layoutInflater = LayoutInflater.from(mContext)
        return DataBindingViewHolder(DataBindingUtil.inflate(layoutInflater, layoutId, parent, false))
    }

    override fun getItemCount(): Int {
        return list?.size ?: 0
    }

    fun getItem(position: Int):S {
        list?.let {
            if (position < it.size)
                return it[position]
        }
            return list?.get(position) ?: null as S
    }

    override fun onBindViewHolder(holder: DataBindingViewHolder, position: Int) {
        listener?.onBind(holder, position)
    }

    interface BaseAdapterListener {
        fun onBind(holder: DataBindingViewHolder, position: Int)
    }

    class DataBindingViewHolder( val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root)
}

这是在 Fragment 中使用它的方法:

class DemoFragment : Fragment(),BaseAdapter.BaseAdapterListener {
    private var  adapter: BaseAdapter<Audio>?= null// specify your data it taking List<Audio> as parameter
    
    private  lateinit var binding: FragmentPracticeBinding
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
         super.onCreateView(inflater, container, savedInstanceState)
        binding = FragmentPracticeBinding.inflate(inflater,container,false)
        return binding.root;
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        adapter = BaseAdapter(context, arrayListOf(),R.layout.item_audio,this)//created the object of adapter with specific info 
        binding.notesRecyclerView.adapter = adapter
    }

    override fun onBind(holder: BaseAdapter.DataBindingViewHolder, position: Int) {
        (holder.binding as ItemAudioBinding).apply { 
            // play with your item layout 
        }
        // this nothing but your onBindViewHolder of item_layout type
    }
}

【讨论】:

  • 感谢您的回答,但不幸的是,此解决方案根本不涉及我的问题。如何将 item 对象转换为 I 类型的对象?
  • 在我的示例中,如果您近距离看到 baseAdapter,您可以传递任何种类的 Item 对象和任何种类的项布局。 OnBind(....) { holder.binding} 内部是通用类型,我在这里明确地转换为我的项目 ---> holder.binding as ItemAudioBinding 转换为我在创建 baseAdapter 时传递的布局
  • 我正在扩展 FirestoreRecyclerAdapter&lt;I, ItemAdapter&lt;I&gt;.ItemViewHolder&lt;I&gt;&gt; 并且需要根据我在适配器中使用的类型进行转换。
猜你喜欢
  • 2012-08-28
  • 2021-12-25
  • 1970-01-01
  • 2018-09-10
  • 2013-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-01
相关资源
最近更新 更多