android-fragments 如何在recycleView按钮中添加带有数据绑定的OnClickListener?

nhaq1z21  于 2022-11-14  发布在  Android
关注(0)|答案(1)|浏览(107)

我从一个API中获取类别,我想通过传递类别名称来启动一个新片段,以便在单击按钮时进行改进。我想在UI中使用getText()在单击的按钮上执行此操作,并启动API调用,该调用将在新片段上显示结果。我使用的API是https://fakestoreapi.com/
据我所知,我必须在UI部件(Fragment的代码)中处理click事件。
下面的代码在我的FakeApiService.kt中

@GET("categories")
suspend fun getCategories(): List<String>

@GET("categories/{product}")
suspend fun getProduct(@Path(value = "product", encoded = true) productType: String): List<Product>

然而,我在向按钮添加clickListener时遇到了麻烦。
下面是recycleView适配器的代码

class MyCategoryRecyclerViewAdapter(
    private val values: ArrayList<String>
    ) : RecyclerView.Adapter<MyCategoryRecyclerViewAdapter.MyCategoryRecyclerViewHolder>() {

    inner class MyCategoryRecyclerViewHolder :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(category: String) {
            binding.category = category
            binding.btnCategoryName.setOnClickListener {
                Log.d("Debug On", "Button clicked ${binding.btnCategoryName.text}")
            }
        }
    }

    private lateinit var binding: CategoryItemBinding

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): MyCategoryRecyclerViewHolder {
        binding = CategoryItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return MyCategoryRecyclerViewHolder()
    }

    override fun onBindViewHolder(holder: MyCategoryRecyclerViewHolder, position: Int) {
        holder.bind(values[position])
    }

    fun updateData(values: ArrayList<String>) {
        this.values.clear()
        this.values.addAll(values)
    }

    override fun getItemCount() = values.size
}

类别片段的我的ViewModel

enum class FakeApiStatus { LOADING, ERROR, DONE }

class CategoryViewModel : ViewModel() {
    // The internal MutableLiveData that stores the status of the most recent request
    private val _status = MutableLiveData<FakeApiStatus>()
    val status: LiveData<FakeApiStatus> = _status
    val categories: ArrayList<String> = ArrayList()

    /**
     * Call getCategories() on init so we can display status immediately.
     */
    init {
        Log.d("Debug", "view model created")
        getCategories()
    }

    private fun getCategories() {
        viewModelScope.launch(Dispatchers.IO) {
            _status.postValue(FakeApiStatus.LOADING)
            try {
                categories.clear()
                categories.addAll(FakeApi.retrofitService.getCategories())
                _status.postValue(FakeApiStatus.DONE)
            } catch (e: Exception) {
                _status.postValue(FakeApiStatus.ERROR)
            }
        }
    }
}

片段的代码。

class CategoryFragment : Fragment() {
    private lateinit var binding: FragmentCategoryBinding
    private val adapter = MyCategoryRecyclerViewAdapter(ArrayList())
    private val viewModel: CategoryViewModel by lazy {
        ViewModelProvider(this)[CategoryViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentCategoryBinding.inflate(inflater)
        binding.rvCategoryList.adapter = adapter
        viewModel.status.observe(viewLifecycleOwner) {
            when (it) {
                FakeApiStatus.DONE -> binding.animationRotation.visibility = View.GONE
                FakeApiStatus.ERROR -> Toast.makeText(
                    context,
                    "No Internet!",
                    Toast.LENGTH_LONG
                ).show()
                else -> {}
            }
            observeCategories()
        }
        return binding.root
    }

    private fun observeCategories(){
        adapter.updateData(viewModel.categories)
        adapter.notifyDataSetChanged()
    }
}

这是我的item布局

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="category"
            type="String" />
    </data>

    <Button
        android:id="@+id/btn_category_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{category.toUpperCase()}"
        tools:text="Test"
        android:onClick="TODO"
        android:layout_margin="@dimen/cardview_default_elevation"
        android:textAppearance="?attr/textAppearanceListItem" />
</layout>

而布局为Fragment(RecycleView)

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

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_category_list"
            android:name="com.example.task.categories.CategoryFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="LinearLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:context=".categories.CategoryFragment"
            tools:listitem="@layout/category_item" />

        <com.google.android.material.progressindicator.CircularProgressIndicator
            android:id="@+id/animation_rotation"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:indeterminate="true"
            android:visibility="visible"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
2ic8powd

2ic8powd1#

在您的fragment / activity中,在类的下面创建一个新的类来处理点击

/**
 * Click listener for YourDomainObject. By giving the block a name it helps a reader understand what it does.
 */
class YourCustomClickClick(val block:(YourDomainObject)-> Unit){
    fun onClick(yourDomainObject:YourDomainObject)= block(yourDomainObject)
}

当您在片段/活动中初始化viewModelAdapter时,您还可以返回回调

viewModelAdapter = YourAdapter(YourCustomClick{item->
            var value = item
            Log.i(logTag,"The item that was clicked: ${value}")
//do any other operations you would like on click here
            return@YourCustomClickCallback

        }

您的适配器类:

class MyCategoryRecyclerViewAdapter(
private val values: ArrayList<String>
)

更改为:传递回调

class MyCategoryRecyclerViewAdapter(
private val values: ArrayList<String>,
val callback:YourCustomClick // the call back created in your fragment/activity
)

在视图保持器中,您可以获得单击的位置,如下所示

override fun onBindViewHolder(holder: YourViewHolder, position: Int) {
        holder.viewDataBinding.also {
                    it.item = item[position] // here we can tell what item is clicked based on the position in the list
                    it.YourCustomClickCallback= callback // assign the call back for each item in the list

        }
    }

最后,在你的视图中,你可以通过创建数据变量来访问回调和域对象。2在你的布局标签中,添加数据标签。3在数据标签中添加一个带有名称和对象路径的变量。4(如果你已经创建了域对象的话)。

<layout> 
    <data>
        <variable
            name="item"
            type="com.example.yourproject.domain.models.Item" />
        <variable
            name="yourCustomCallback"
            type="com..example.yourproject.main.ui.fragments.YourCustomClick" />
    </data>
    </layout>

提示,这可以在xml布局中完成,xml布局将卡片或视图保存到回收器视图中。

class YourViewHolder(val viewDataBinding: CardViewYourItemBinding):
        RecyclerView.ViewHolder(viewDataBinding.root){

        companion object{
            @LayoutRes
            val LAYOUT = R.layout.card_view_your_recycler_item
        }

    }

您可以使用数据绑定在xml中分配回调。

<ImageView
                
            android:clickable="true"
            android:onClick="@{()->yourCustomCallback.onClick(item)}"
<!-- item is passed to your custom on click -->

我所说的域对象是指域模型,它是一个简单的类,你可以示例化它来保存一些数据。
下面是一个用户的简单数据类的示例。

data class User(
val userID: String,
val fireBaseId:String,
val userName: String,
val firstName: String,
val lastName: String,
val score: Int,
val age: Int,
val emailAddress: String,
val photoUrl: String,
val sex: Int,
val emailVerified:Boolean)

相关问题