14

I have created a custom button which is inflated from an XML layout. Everything works fine, except that the click listener is not triggered.
I suspect the problem is because of android:clickable="true" attribute, as when I remove it the click listener is triggered. But I need to have this attribute set as my custom view uses a selector as background, if I remove it, then the selector won't work anymore.

Here's the class definition:

public class CustomButton extends LinearLayout{

    public CustomButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.custom_button, this, true);

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomButton, 0, 0);
        String titleStr = a.getString(R.styleable.CustomButton_title);
        String subTitleStr = a.getString(R.styleable.CustomButton_subTitle);
        int iconResId = a.getResourceId(R.styleable.CustomButton_icon, R.drawable.ic_launcher);
        a.recycle();

        TextView title = (TextView)findViewById(R.id.title);
        title.setText(titleStr);

        TextView subTitle = (TextView)findViewById(R.id.subTitle);
        subTitle.setText(subTitleStr);

        ImageView icon = (ImageView)findViewById(R.id.icon);
        icon.setImageResource(iconResId);
    }
}

The XML layout that the custom view is inflated from:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:background="@drawable/white_box"
             android:layout_width="match_parent"
             android:layout_height="match_parent">


    <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@drawable/main_button_selector"
            android:clickable="true"
            android:gravity="center"
            android:orientation="horizontal">

        <ImageView
                android:id="@+id/icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:contentDescription="icon"
                android:layout_marginLeft="5dip"
                />

        <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginRight="5dip"
                android:orientation="vertical">

            <TextView
                    android:id="@+id/title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/grey_text"
                    android:textSize="@dimen/mainTextSize"
                    android:textStyle="bold"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/subTitle"
                    android:ellipsize="end"
                    android:maxLines="2"
                    android:textColor="@color/grey"
                    android:textSize="@dimen/mainSubTextSize"/>
        </LinearLayout>
    </LinearLayout>
</FrameLayout>

And here's how I use it in the XML layouts:

 <com.customviews.CustomButton
                android:id="@+id/pay"
                android:layout_weight="1"
                android:layout_width="0dip"
                android:layout_height="fill_parent"
                custom:title="Hello World"
                custom:subTitle="Subtitle"
                custom:icon="@drawable/ic_launcher"/>

and how the activity sets the click listener:

 CustomButton button = (CustomButton) findViewById(R.id.pay);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MyActivity.this, "Hello World!", Toast.LENGTH_LONG).show();

I have seen several threads already addressing this issue but non of them helped me. Would appreciate any help.

Andy Res
  • 15,963
  • 5
  • 60
  • 96
  • Your CustomButton extends LinearLayout. And You are trying set a onClickListener for that. is that something which u want.? – SKK May 28 '13 at 12:43
  • Yes, something like that. But meanwhile the CustomButton to have a background selector. – Andy Res May 28 '13 at 12:46
  • I cannot see the button.setclickable(true) anywhere,is it normal? – wazaminator May 28 '13 at 12:47
  • It is set in the XML layout for the second ViewGroup as: `android:clickable="true"` – Andy Res May 28 '13 at 12:48
  • i have similar problem with my custom view wich have FramLayout as parent ViewGroup and have set clickable attribute to true clickListener won't call but when i remove this like you said, it works but i need this attribute to show ripple effect, how can i achieve this, your asnwer dosent help me my situation – Burhan Khanzada Mar 19 '20 at 11:50
  • IT IS NOW THIS EASY: https://stackoverflow.com/a/66733216/294884 – Fattie Mar 21 '21 at 13:59

1 Answers1

22

I was worried that not setting android:clickable="true" won't trigger the selector background, but after taking a look at the setOnClickListener() method from the View class, I saw that setClickable(true) is called:

public void setOnClickListener(OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
}

So, removing android:clickable="true" from the layout declaration and setting just the click listener for my custom button resolved the issue.

Andy Res
  • 15,963
  • 5
  • 60
  • 96
  • Good catch. But not really clear why it is so. `View.setOnClickListener()` sets clickable to true and listener is set in any case. – Taras Lozovyi Aug 22 '17 at 10:42
  • 2
    Stumbled upon this same problem and taking out the flag does the trick, true. The reason it doesn't work when you set the flags is that in your XML file if you set those flags, the `LinearLayout` (the child) gets the click callbacks whereas you are listening for clicks on the `CustomButton` (the parent). So by setting that flag, your `LinearLayout` eats up the click which you're not handling and are not propagated to the parent. Removing it makes the `LinearLayout` NOT clickable, so the click is passed to the parent `CustomButton` which is why it worked. – gsb Jul 11 '19 at 00:01