0

I have a button (id: readyButtonIntro) inside a layout (introscreen.xml) that i need to enable. To do that, i have another button inside the RecyclerView.ViewHolder.

This is my Layout to need access

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>

And the another button is inside into the 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>

How can I enable from inside the class that listener the button?

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

    init {
        addData.setOnClickListener(View.OnClickListener {

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

    }
}

I have a fun into IntroScreenVC but always have a error that its null, if a pass the context or view, do nothing.


     fun readyButton(){

         readyButtonIntro.isEnabled = true
      }

Could you help me with this? I would really appreciate it. Thank you very much! Regards.


Edit:

I put the adapter and the ViewHolder for more information. I ignored that because I didn't want to create confusion. Sorry for that..

Class Constructor


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)
            }
          }
}




Edit2:

This is what I do with 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

        }

    }
}




SOLUTION:

Into the Activity (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

    }

And the into the Adapter and 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()

}
}
}
}

1 Answers1

1

As I understand your problem you have a class A that is trying to communicate (change something) in class B. There are several options for solving this kind of problem, depending on your exact needs.

From the code you have provided the relation of your Layouts and classes is not clear enough to me to give a more precise answer. First of all, I understand you are using a recycler view. A recycler view can have many items, and I assume you want to be able to enable that button from each item.

In order to let your IntroScreen class communicate with your viewholder, you have to pass a reference to the ViewHolder constructor.

For this purpose you could implement a simple "callback pattern". Here is an example for defining an interface (e.g. for a function that enables the button) and implementing the callback. Have a read here to see a well-explained example in Java. In Kotlin you could do it the same way.

Here a summary of the implementation steps:

  1. define interface EnableButtonCallback that implements an abstract method enableButton
  2. let your InfoScreen class implement that interface (in which you enable the button)
  3. pass your InfoScreen class to your RecyclerView adapter and then from your adapter to your ViewHolder
  4. in your ViewHolder onClickListener call the interface method enableButton

Update 2020/08/11

I try to give suggestions based on your updated code. In the intro screen you set your viewPager adapter, but it is still not clear where this property is coming from and where exactly it is displayed. I guess maybe you just cut out the parameter definition. However, I just assume you have your views set up properly and this is not a problem here. For using recycler view with viewPager I found some related information here.

I can not yet see your use case clearly yet. Are you adding data persistently? Then should your button in the IntroScreen be permanently enabled? In this case probably SharedPreferences are a good choice for persisting this kind of information. Even when it doesn't need to be persisted. Reading one shared preference file is lightweight and quick enough to be done on the main thread. I will give you an example implementation here:

  1. Get a shared preferences object
    val sharedPref = activity?.getSharedPreferences(
        "intro_button_settings_file", Context.MODE_PRIVATE) // String with the key should be in your string resource file
  1. Pass your sharedPref to your adapter and your viewHolder and write to it:
    with (sharedPref.edit()) {
        putBoolean("ready_button_enabled", true) // String with the key should be in your string resource file
        commit()
    }
  1. in your IntroScreen check the setting
    val readyButtonShouldBeEnabled = sharedPref.getBoolean("ready_button_enabled", 
        false) // defaults to false

If, after clicking your enable button (that sets the setting to true), you need to return to your IntroScreen activity: then you could enable your button in your activities onResume method A different solution would be: You check the setting in your IntroScreen onClick method. Then you don't need to disable the button. You just set:

// 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

If your button should be disabled again, simply save false to the setting.

Since I do not know more about your use case, this seems like an easy and quick solution to me. This way you do not need to bother with implementing an interface. Anyways, when clicking your button in your viewHolder there is no immediate action taking place in your IntroScreen activity. You still want the user to return to the IntroScreen and click the enabled button. Then checking if your button was enabled just when clicking on it appears sufficient to me.

cewaphi
  • 410
  • 2
  • 7
  • Thanks cewaphi to the quick reply. Yes you understood correctly. I have two classes. class IntroScreenVC: AppCompatActivity() { ... } and the another class IntroScreenAdapter(private val introSlides: List) : RecyclerView.Adapter() { ... } Inside the ViewHolder have the setOnClickListener. Im trying to do what you told me but always have the same error of null when press the button. Could you give me more examples please? – Ezequiel Martinez Aug 10 '20 at 03:05
  • @EzequielMartinez https://stackoverflow.com/a/12142492/5339146 check this reference – Mihodi Lushan Aug 10 '20 at 05:08
  • @EzequielMartinez It is valuable information for me to know that the IntroScreenVC class is an activity. In the layout you have posted for this acticity, there is no RecyclerView. So where is this RecyclerView? Is it in another activity or is it in a fragment started by your IntroScreenVC activity? – cewaphi Aug 10 '20 at 07:35
  • @EzequielMartinez I can then update my answer based on your information. I would suggest a different approach for communication within an Activity (with recycler view), or between Activity and Fragment, or between two Fragments. – cewaphi Aug 10 '20 at 09:08
  • @cewaphi thanks for your time... really :) ... I edit the problem with all the information that you require. I hope it helps you understand me. Thank you again... – Ezequiel Martinez Aug 11 '20 at 05:39
  • @cewaphi I try to to do with the sharedPreferences but in the method OnResume or OnCreate don't change the result. Always init with the default value false and when I change to true into the addData.setOnClickListener nothing happened. I upload a video to show you what I'm trying to do. The video show you the ViewPager that contains all the adapter and the recyclerView. The Button "Agregar Datos" triggered the Alert Dialog and then when pressed OK I need to enable and show the button with alpha. I hope this short video helps... https://youtu.be/Jw_Wmv9-Aoc – Ezequiel Martinez Aug 11 '20 at 17:45
  • @EzequielMartinez Now I have a better understanding what it looks like. Do you also save the data that the user enters? Then maybe you are using sharedPrefs anyways? Since you set this adapter with these slide views inside your IntroScreenVC activity I assume your adapter is in the scope of this activity. Therefore you could still use an interface. Then on pressing OK you could immediately enable the button and make it visible. If you really need that I could help you implementing it. Let me know how your problem can be solved – cewaphi Aug 11 '20 at 19:18
  • @cewaphi Yes i save the data in Firebase and in the sharedPreferences... when pressing OK into the AlertDialog. Im trying to do with the Interface that you told me but i dont have lucky ... sure im doing something wrong... :( – Ezequiel Martinez Aug 11 '20 at 20:27
  • @EzequielMartinez if you update your question with what you tried I can have a look and try to accomplish it with you together :) – cewaphi Aug 11 '20 at 22:28
  • @cewaphi Ready with the edit2... thanks for all the effort that you give to helps! – Ezequiel Martinez Aug 12 '20 at 16:08
  • @EzequielMartinez The problem here is that your onResume does not need to get called. Because your adapter is inside this activity so your activity never gets paused. Two options to consider: 1. you always leave that button visible and clickable and check if it was officially "enabled" from the sharedPrefs 2. you want this ready button to become visible after the user successfully entered data. In this case you need to implement an interface so that your viewholder can communicate with your activity Do you need help with the implemention of the interface approach ("callback-pattern")? – cewaphi Aug 12 '20 at 19:54
  • @cewaphi I can't believe it!! I resolve this!!! :D thx for all your patience, you are a genius... really! I will accept the correct answer to you and then I will copy the solution with the interface (callback-pattern)... thx again!! – Ezequiel Martinez Aug 13 '20 at 05:00
  • @Ezequiel Martinez congratulations! I am glad to hear you could finally achieve to create your working solution :) – cewaphi Aug 13 '20 at 05:09
  • @EzequielMartinez I just wondered. Why do you disable the button and set the alpha to 0? One line is to set visibility to View.GONE. It is invisible and can not be clicked – cewaphi Aug 13 '20 at 21:05
  • @cewaphi I did it because when all data is in Firebase, I need to show this button "ready" to start the activity "Main". Also, I disable the "Next" button and set alpha 0 to disappear heheh. – Ezequiel Martinez Aug 13 '20 at 22:55
  • @EzequielMartinez As far as I understand you want to 1. disable button click and 2. make the button invisible. You can do both in just one action -> set `visibility` to `gone` – cewaphi Aug 14 '20 at 18:28