【问题标题】:Android: Enable a Button inside RecyclerView.ViewHolderAndroid:在 RecyclerView.ViewHolder 中启用按钮
【发布时间】:2020-08-09 20:44:46
【问题描述】:

我需要启用一个布局 (introscreen.xml) 内的按钮 (id: readyButtonIntro)。为此,我在 RecyclerView.ViewHolder 中有另一个按钮。

这是我需要访问的布局

introscreen.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorWhite"
    tools:context=".IntroScreenVC">


    <LinearLayout
        android:id="@+id/indicatorContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="40dp"
        android:gravity="center"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent" />

    <Button
        android:id="@+id/readyButtonIntro"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:background="@color/colorWhite"
        android:textColor="@color/colorTerciary"
        android:alpha="0"
        android:enabled="false"
        android:text="Ready"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

另一个按钮在 ViewHolder 里面

slide_item_container.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="15dp"
    >

    <Button
        android:id="@+id/addData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="Agregar Datos"
        android:background="@drawable/button_rounded2"
        />

</LinearLayout>

如何从监听按钮的类内部启用?

class IntroSlideViewHolder(view: View) : RecyclerView.ViewHolder(view) {

    init {
        addData.setOnClickListener(View.OnClickListener {

        //NEED TO ENABLE THE BUTTON
        // val introScreen = IntroScreenVC()
        // introScreen.readyButton()

    }
}

我对 IntroScreenVC 很感兴趣,但总是有一个错误,即它的 null,如果传递上下文或视图,则什么也不做。


     fun readyButton(){

         readyButtonIntro.isEnabled = true
      }

你能帮我解决这个问题吗?我真的很感激。 非常感谢! 问候。


编辑:

我将适配器和 ViewHolder 放在一起以获取更多信息。 我忽略了这一点,因为我不想造成混乱。对不起..

类构造函数


data class IntroSlide(val title: String, val description: String, val icon: Int, val firstButton: Boolean, val secondButton: Boolean, val thirdButton: Boolean)

IntroScreenVC.kt

class IntroScreenVC: AppCompatActivity() {

    private val introSliderAdapter = IntroScreenAdapter(
        listOf(
            IntroSlide(
                "title1",
            "description1",
                R.drawable.logo,
                false,
                false,
                false
            ),
            IntroSlide(
                "title2",
                "description2",
                R.drawable.doggrooming,
            true,
                false,
                false
            ),
            IntroSlide(
                "title3",
                "description3",
                R.drawable.introscreen3,
                false,
                true,
                false
            )
        )
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.introscreen)

        introSliderViewPager.adapter = introSliderAdapter
    }
}


IntroScreenAdapter.kt

class IntroScreenAdapter(private val introSlides: List<IntroSlide>) : RecyclerView.Adapter<IntroSlideViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntroSlideViewHolder {

        val layoutInflater = LayoutInflater.from(parent?.context)
        val cellForRow = layoutInflater.inflate(R.layout.slide_item_container,parent,false)
        return IntroSlideViewHolder(cellForRow)
    }

    override fun getItemCount(): Int {
        return introSlides.size
    }

    override fun onBindViewHolder(holder: IntroSlideViewHolder, position: Int) {
        holder.bind(introSlides[position])

    }

}

class IntroSlideViewHolder(view: View) : RecyclerView.ViewHolder(view) {

    private val textTitle = view.findViewById<TextView>(R.id.textTitle)
    private val textDescription = view.findViewById<TextView>(R.id.textDescription)
    private val imageIcon = view.findViewById<ImageView>(R.id.imageSlideIcon)
    private val addData = view.findViewById<Button>(R.id.addData)
    private val addPet = view.findViewById<Button>(R.id.agregarMascota)

    val contexto = itemView.context;

    fun bind(introSlide: IntroSlide) {
        textTitle.text = introSlide.title
        textDescription.text = introSlide.description
        imageIcon.setImageResource(introSlide.icon)
        addData.isEnabled = introSlide.firstButton
        addPet.isEnabled = introSlide.thirdButton
        }
    }



    init {
        addData.setOnClickListener(View.OnClickListener {

           //ADD A ALERTDIALOG AND WHEN PRESS OK NEED TO ENABLE THAT BUTTON
            val mDialogView =  LayoutInflater.from(contexto).inflate(R.layout.alertdialog_add_data,null)
            val builder = AlertDialog.Builder(contexto)
            builder.setView(mDialogView)
            val dialog: AlertDialog = builder.create()
            dialog.show()
            dialog.getWindow()?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
            mDialogView.agregarDatosOK.setOnClickListener {
            //HERE I NEED TO ENABLE THE BUTTON 
            //readyButtonIntro(introscreen.xml)
            }
          }
}




编辑2:

这就是我对 sharedPreferences 所做的。

IntroScreenAdapter.kt

class IntroSlideViewHolder(view: View) : RecyclerView.ViewHolder(view) {

    private val textTitle = view.findViewById<TextView>(R.id.textTitle)
    private val textDescription = view.findViewById<TextView>(R.id.textDescription)
    private val imageIcon = view.findViewById<ImageView>(R.id.imageSlideIcon)
    private val addData = view.findViewById<Button>(R.id.addData)
    private val addPet = view.findViewById<Button>(R.id.agregarMascota)

//INIT sharedPreferences
private val prefs: SharedPreferences = view.context.getSharedPreferences(getString(R.string.prefs_file), Context.MODE_PRIVATE)

    val contexto = itemView.context;

    fun bind(introSlide: IntroSlide) {
        textTitle.text = introSlide.title
        textDescription.text = introSlide.description
        imageIcon.setImageResource(introSlide.icon)
        addData.isEnabled = introSlide.firstButton
        addPet.isEnabled = introSlide.thirdButton
        }
    }



    init {
        addData.setOnClickListener(View.OnClickListener {
            val mDialogView = LayoutInflater.from(contexto).inflate(R.layout.alertdialog_add_data,null)
            val builder = AlertDialog.Builder(contexto)
            builder.setView(mDialogView)
            val dialog: AlertDialog = builder.create()
            dialog.show()
            dialog.getWindow()?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
            mDialogView.agregarDatosOK.setOnClickListener {


                    //HERE EDIT THE sharedPreferences
                    with (prefs.edit()) {
                        putBoolean("ready_button_enabled", true)
                        apply()
                    }

                    dialog.dismiss()

            }
          }
}

IntroScreenVC.kt


class IntroScreenVC: AppCompatActivity() {

    private val introSliderAdapter = IntroScreenAdapter(
        listOf(
            IntroSlide(
                "title1",
            "description1",
                R.drawable.logo,
                false,
                false,
                false
            ),
            IntroSlide(
                "title2",
                "description2",
                R.drawable.doggrooming,
            true,
                false,
                false
            ),
            IntroSlide(
                "title3",
                "description3",
                R.drawable.introscreen3,
                false,
                true,
                false
            )
        )
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.introscreen)

        introSliderViewPager.adapter = introSliderAdapter
    }


    //HERE PUT THE RESUME TO EXPECT THE SHOW AND ENABLE THE BUTTON

    override fun onResume() {
        super.onResume()

        val prefs = getSharedPreferences(getString(R.string.prefs_file), Context.MODE_PRIVATE)
        val buttonEnabled = prefs.getBoolean("ready_button_enabled", false)
        readyButtonIntro.isEnabled = buttonEnabled
        if (buttonEnabled) {
            readyButtonIntro.alpha = 1f
        }else {
            readyButtonIntro.alpha = 0f

        }

    }
}




解决方案:

进入活动(IntroScreenVC)

class IntroScreenVC: AppCompatActivity(), IntroScreenAdapter.AdapterOnClick {

    private val introSliderAdapter =
        listOf(
           ...
        )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.introscreen)

        introSliderViewPager.adapter = IntroScreenAdapter(introSliderAdapter, this)
}

...

    override fun onClick() {
        //HERE ENABLE AND SHOW THE BUTTON
        readyButtonIntro.isEnabled = true
        readyButtonIntro.alpha = 1f

    }

并进入Adapter和RecyclerView


class IntroScreenAdapter(private val introSlides: List<IntroSlide>, val adapterOnClick: AdapterOnClick) : RecyclerView.Adapter<IntroScreenAdapter.IntroSliderViewHolder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntroScreenAdapter.IntroSliderViewHolder {

        val layoutInflater = LayoutInflater.from(parent?.context)
        val cellForRow = layoutInflater.inflate(R.layout.slide_item_container,parent,false)
        return IntroSliderViewHolder(cellForRow)
    }

...

inner class IntroSliderViewHolder(view: View) : RecyclerView.ViewHolder(view) {

...

init {
            addData.setOnClickListener(View.OnClickListener {
                val mDialogView = LayoutInflater.from(contexto).inflate(R.layout.alertdialog_add_data,null)
                val builder = AlertDialog.Builder(contexto)
                builder.setView(mDialogView)
                val dialog: AlertDialog = builder.create()
                dialog.show()
                dialog.getWindow()?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
                mDialogView.agregarDatosOK.setOnClickListener {

                        //FINALLY HERE CHANGE THE BUTTON TO ENABLE :)
                 adapterOnClick.onClick()

}
}
}
}

【问题讨论】:

    标签: android android-studio android-layout kotlin android-recyclerview


    【解决方案1】:

    据我了解,您有一个 A 类试图在 B 类中进行通信(更改某些内容)。 有多种解决此类问题的方法,具体取决于您的具体需求。

    根据您提供的代码,您的布局和类的关系对我来说不够清楚,无法给出更准确的答案。 首先,我了解您正在使用回收站视图。 回收站视图可以包含许多项目,我假设您希望能够从每个项目中启用该按钮。

    为了让您的 IntroScreen 类与您的 viewholder 进行通信,您必须将一个引用传递给 ViewHolder 构造函数。

    为此,您可以实现一个简单的“回调模式”。 这是一个定义接口(例如启用按钮的函数)和实现回调的示例。 阅读here 以查看Java 中解释清楚的示例。在 Kotlin 中,你可以这样做。

    这里总结一下实现步骤:

    1. 定义实现抽象方法enableButton的接口EnableButtonCallback
    2. 让您的 InfoScreen 类实现该接口(您可以在其中启用按钮)
    3. 将 InfoScreen 类传递给 RecyclerView 适配器,然后从适配器传递给 ViewHolder
    4. 在您的 ViewHolder onClickListener 中调用接口方法 enableButton

    2020/08/11 更新

    我尝试根据您更新的代码提供建议。 在介绍屏幕中,您设置了 viewPager 适配器,但仍然不清楚该属性来自何处以及它的确切显示位置。我想也许你只是删掉了参数定义。但是,我只是假设您正确设置了视图,这在这里不是问题。对于通过 viewPager 使用回收站视图,我找到了一些相关信息here

    我还不能清楚地看到您的用例。您是否在持续添加数据?那么 IntroScreen 中的按钮是否应该永久启用? 在这种情况下,SharedPreferences 可能是保留此类信息的不错选择。即使它不需要需要被持久化。读取一个共享首选项文件是轻量级且足够快的,可以在主线程上完成。 我将在这里给你一个示例实现:

    1. 获取共享首选项对象
        val sharedPref = activity?.getSharedPreferences(
            "intro_button_settings_file", Context.MODE_PRIVATE) // String with the key should be in your string resource file
    
    1. 将 sharedPref 传递给适配器和 viewHolder 并写入:
        with (sharedPref.edit()) {
            putBoolean("ready_button_enabled", true) // String with the key should be in your string resource file
            commit()
        }
    
    1. 在 IntroScreen 中检查设置
        val readyButtonShouldBeEnabled = sharedPref.getBoolean("ready_button_enabled", 
            false) // defaults to false
    

    如果在单击启用按钮(将设置设置为 true)后,您需要返回到 IntroScreen 活动:那么您可以在活动的 onResume 方法中启用按钮 一个不同的解决方案是: 您检查 IntroScreen onClick 方法中的设置。 然后,您无需禁用该按钮。 你刚刚设置:

    // in your IntroScreen readyButtonIntro onClick method
    val buttonEnabled = sharedPref.getBoolean("ready_button_enabled", 
            false)
    if (!buttonEnabled) {
        // optional: write a Toast to notify the user why the button is doing nothing (yet)
        Toast.makeText(yourIntroScreenContext, "First agregar datos", Toast.LENGTH_SHORT).show()
        return // onClick returns, so nothing else will happen when clicked
    }
    ... // your code when the button **should** be enabled
    

    如果您的按钮应该再次被禁用,只需将 false 保存到设置中即可。

    由于我不了解您的用例的更多信息,因此这对我来说似乎是一个简单快捷的解决方案。这样您就无需费心实现接口。无论如何,当单击 viewHolder 中的按钮时,您的 IntroScreen 活动中不会立即执行操作。您仍然希望用户返回 IntroScreen 并单击启用的按钮。 然后检查您的按钮是否仅在单击时才启用,这对我来说似乎足够了。

    【讨论】:

    • 感谢 cewaphi 的快速回复。是的,你理解正确。我有两节课。 class IntroScreenVC: AppCompatActivity() { ... } 和另一个类 IntroScreenAdapter(private val introSlides: List) : RecyclerView.Adapter() { ... } 在 ViewHolder 内部有 setOnClickListener。我试图做你告诉我的事情,但按下按钮时总是有同样的 null 错误。请给我更多的例子吗?
    • @EzequielMartinez stackoverflow.com/a/12142492/5339146 检查此参考
    • @EzequielMartinez 知道 IntroScreenVC 类是一项活动对我来说是很有价值的信息。在您为此活动发布的布局中,没有 RecyclerView。那么这个 RecyclerView 在哪里呢?它是在另一个活动中还是在您的 IntroScreenVC 活动启动的片段中?
    • @EzequielMartinez 然后我可以根据您的信息更新我的答案。我建议在 Activity 内(使用回收器视图)、Activity 和 Fragment 之间或两个 Fragment 之间进行通信的不同方法。
    • @cewaphi 感谢您抽出宝贵时间...真的 :) ...我使用您需要的所有信息编辑问题。我希望它可以帮助你理解我。再次感谢...
    猜你喜欢
    • 2018-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    相关资源
    最近更新 更多