Gianni Costanzi
Gianni Costanzi

Reputation: 6104

Implement onClick only for a TextView compound drawable

I need to have some text with a drawable on the left and I want to execute some code when the user clicks/touches the image (only the image, not the text), so I used a LinearLayout with a TextView and an ImageView which is clickable and launches an onClick event. The XML parser suggests me to replace this with a TextView with a compound drawable, which would draw the same thing with far less lines of XML.. My question is "can I specify I want to handle an onClick event only on the drawable of the TextView and not on the TextView itself? I've seen some solutions which involves writing your own extension of TextView, but I'm only interested in being able to do it within the layout resource, if possible, otherwise I'll keep the following XML code:

<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="bottom"
            android:paddingTop="10dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="@string/home_feedback_title"
            android:textColor="@android:color/primary_text_dark"
            android:textStyle="bold" 
            android:paddingBottom="4dp"/>


        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/action_feedback" 
            android:clickable="true"
            android:onClick="onClickFeedback"
            android:contentDescription="@string/action_feedback_description"/>
</LinearLayout>

Upvotes: 30

Views: 20843

Answers (5)

Swathi
Swathi

Reputation: 1684

Since getRaw() and getRight() both returns in regards with the entire screen coordinates, this will not work if your views are not on the left edge of the screen. The below solution can be used anywhere regardless of layout position. This is for right side drawable touch.

  textView.setOnTouchListener(OnTouchListener { _, event ->
                if (event.action == MotionEvent.ACTION_UP)
                {
                    if (event.x >= textView.width - textView.totalPaddingEnd)
                    {
                        <!---DO YOUR ACTIONS HERE--->
                        return@OnTouchListener true
                    }
                }
                true
            })

PS: Kotlin code

Upvotes: 0

Takamitsu Mizutori
Takamitsu Mizutori

Reputation: 767

@Vishnuvathsan's answer is almost perfect, but getRaw() returns an absolute x position of the touch point. If the textview is located not on the left edge of the view, you should compare with the absolute position of the textview by using getLocationOnScreen. Code below is an example to check both left drawable tap and right drawable tap.

textView.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            int[] textLocation = new int[2];
            textView.getLocationOnScreen(textLocation);

            if (event.getRawX() <=  textLocation[0] + textView.getTotalPaddingLeft()) {

                // Left drawable was tapped

                return true;
            }


            if (event.getRawX() >= textLocation[0] + textView.getWidth() - textView.getTotalPaddingRight()){

                // Right drawable was tapped

                return true;
            }
        }
        return true;
    }
});

Upvotes: 8

Dishant Kawatra
Dishant Kawatra

Reputation: 648

final int DRAWABLE_LEFT = 0;
final int DRAWABLE_TOP = 1;
final int DRAWABLE_RIGHT = 2;
final int DRAWABLE_DOWN = 3;

This click listener is getting in on touch listener

 if (event.getAction() == MotionEvent.ACTION_DOWN) 
if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (event.getX() >= (tvFollow.getTop() - tvFollow.getCompoundDrawables()[DRAWABLE_TOP].getBounds().width())) {
                   // your action here
                return true; } }

Upvotes: 0

Vishnuvathsan
Vishnuvathsan

Reputation: 635

Its very simple. Lets say you have a drawable on left side of your TextView 'txtview'. Following will do the trick.

TextView txtview = (TextView) findViewById(R.id.txtview);
txtview.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP) {
            if(event.getRawX() <= txtview.getTotalPaddingLeft()) {
                // your action for drawable click event

             return true;
            }
        }
        return true;
    }
});

If you want for right drawable change the if statement to:

if(event.getRawX() >= txtview.getRight() - txtview.getTotalPaddingRight())

Similarly, you can do it for all compound drawables.

txtview.getTotalPaddingTop();
txtview.getTotalPaddingBottom();

This method call returns all the padding on that side including any drawables. You can use this even for TextView, Button etc.

Click here for reference from android developer site.

Upvotes: 52

Jeremy Edwards
Jeremy Edwards

Reputation: 14740

You can go either way. Using the compound drawable is faster though because it was intended to be an optimization. It uses less ram because you reduce 3 views into 1 and it's faster layout because you lose 1 depth.

If I were you I'd consider stepping back to see if both the text and the image intercepting the touch to do whatever action is possibly a good thing. In general having a larger touch region makes it easier to press. Some users may actually be inclined to touch the text instead of the image.

Lastly if you go that route of merging the 2 you might want to consider using a Button instead of a TextView. You can style the button to not have the rectangle around it. They call it a borderless button. It's nice because you get visual feedback that you clicked on a actionable item where as an ImageView or TextView normally aren't actionable.

How to Create Borderless Buttons in Android

Upvotes: 9

Related Questions