Search code examples
androidkotlinpreferencefragmentclicklistener

How to set click listener to this Button


I cant get this to work I want the sign out Button on this preferences screen to have a ClickListener

This is how it looks like:

enter image description here

Here´s the code and the buttonView is always NULL:

class PreferencesFragment : PreferenceFragmentCompat() {

    lateinit var activity: Context

    private var prefs: SharedPreferences = BleApplication.getInstance().getDefaultSharedPreferences()

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        activity = requireActivity()
    }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val buttonView = view.findViewById<View>(R.id.btn_sign_out)

        if (buttonView != null) {
            buttonView.setOnClickListener {
                Toast.makeText(getActivity(), "You clicked me.", Toast.LENGTH_SHORT).show()
            }
        }
        // Hide the divider
/*        setDivider(ColorDrawable(Color.TRANSPARENT))
        setDividerHeight(0)*/
    }

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        addPreferencesFromResource(R.xml.app_prefs)
    }
}

I also tried the kotlinx.android.synthetic but same problem there

Here´s the xml

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
        android:layout="@layout/pref_category_text"
        android:title="@string/pref_category_remote_battery_title">

        <SwitchPreferenceCompat
            android:key="@string/pref_key_category_remote_battery_switch"
            android:title="@string/pref_category_remote_battery_switch_title"
            android:summary="@string/pref_category_remote_battery_switch_summ"/>

    </PreferenceCategory>

    <PreferenceCategory
        android:layout="@layout/pref_category_text"
        android:title="@string/pref_category_sign_out_title">

        <Preference
            android:key="@string/pref_key_category_signed_out"
            android:widgetLayout="@layout/pref_category_sign_out_button"
            android:title="@string/pref_category_sign_out_button_title"
            android:summary="@string/pref_category_sign_out_buttom_summ"/>

    </PreferenceCategory>

</PreferenceScreen>

Here is the "@layout/pref_category_sign_out_button" layout

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

    <Button
        android:id="@+id/btn_sign_out"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/buttonshape"
        android:text="@string/pref_category_sign_out_title" />
</LinearLayout>

Solution

  • Since your Fragment extends from PreferenceFragmentCompat, you should not try to set a View.OnClickListener but override PreferenceFragmentCompat.onPreferenceTreeClick() instead. According to the documentation, this method is ...

    Called when a preference in the tree rooted at this PreferenceScreen has been clicked.

    Code example in Java:

    @Override
    onPreferenceTreeClick(Preference preference){
        if(preference.getKey().equals(getContext().getString(R.string.pref_key_category_signed_out))){
           // user clicked signout "button"
           // take appropriate actions
           // return "true" to indicate you handled the click
           return true;
        }
        return false;
    }
    

    Code example in Kotlin (I hope I can trust Android Studio :P)

    override fun onPreferenceTreeClick(preferenceScreen: PreferenceScreen, preference: Preference): Boolean {
        return if (preference.key == context.getString(R.string.pref_key_category_signed_out)) {
            // user clicked signout "button"
            // take appropriate actions
            // return "true" to indicate you handled the click
            true
        } else false
    }
    

    This will enable you to catch click events for the Preference but not for the Button.

    In order to do that as well, one can use a custom Preference and override onBindViewHolder(PreferenceViewHolder). Since PreferenceViewHolder - similar to RecyclerView.ViewHolder - has a field itemView which contains the inflated layout, here is a good opportunity to set our own View.OnClickListener.

    SignOutPreference extends from TwoStatePreference (in the com.android.support:preference-v7 library) because replacing the CheckBox widget with your custom Button requires only to set the android:widgetLayout attribute, just like you do in your code snippet:

    <PreferenceCategory
        android:layout="@layout/pref_category_text"
        android:title="@string/pref_category_sign_out_title">
    
        <your.package.name.SignOutPreference
            android:key="@string/pref_key_category_signed_out"
            android:widgetLayout="@layout/pref_category_sign_out_button"
            android:title="@string/pref_category_sign_out_button_title"
            android:summary="@string/pref_category_sign_out_buttom_summ"/>
    
    </PreferenceCategory>
    

    SignOutPreference.java

    public class SignOutPreference extends TwoStatePreference {
        public SignOutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public SignOutPreference(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public SignOutPreference(Context context) {
            super(context);
        }
    
        @Override
        public void onBindViewHolder(final PreferenceViewHolder holder) {
            super.onBindViewHolder(holder);
            Button button = holder.itemView.findViewById(R.id.btn_sign_out);
            if(button != null){
                button.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(holder.itemView.getContext(), "CLICKED!", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    }