Compartilhando minha solução generalizada para lidar com eventos de clique e toque desenhados com compostos do TextView.
Primeiro, precisamos de um manipulador de eventos de toque:
/**
* Handles compound drawable touch events.
* Will intercept every event that happened inside (calculated) compound drawable bounds, extended by fuzz.
* @see TextView#getCompoundDrawables()
* @see TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)
*/
public abstract class CompoundDrawableTouchListener implements View.OnTouchListener {
private final String LOG_TAG = "CmpDrawableTouch";
private final int fuzz;
public static final int LEFT = 0;
public static final int TOP = 1;
public static final int RIGHT = 2;
public static final int BOTTOM = 3;
private static final int[] DRAWABLE_INDEXES = {LEFT, TOP, RIGHT, BOTTOM};
/**
* Default constructor
*/
public CompoundDrawableTouchListener() {
this(0);
}
/**
* Constructor with fuzz
* @param fuzz desired fuzz in px
*/
public CompoundDrawableTouchListener(int fuzz) {
this.fuzz = fuzz;
}
@Override
public boolean onTouch(View view, MotionEvent event) {
if (!(view instanceof TextView)) {
Log.e(LOG_TAG, "attached view is not instance of TextView");
return false;
}
TextView textView = (TextView) view;
Drawable[] drawables = textView.getCompoundDrawables();
int x = (int) event.getX();
int y = (int) event.getY();
for (int i : DRAWABLE_INDEXES) {
if (drawables[i] == null) continue;
Rect bounds = getRelativeBounds(i, drawables[i], textView);
Rect fuzzedBounds = addFuzz(bounds);
if (fuzzedBounds.contains(x, y)) {
MotionEvent relativeEvent = MotionEvent.obtain(
event.getDownTime(),
event.getEventTime(),
event.getAction(),
event.getX() - bounds.left,
event.getY() - bounds.top,
event.getMetaState());
return onDrawableTouch(view, i, bounds, relativeEvent);
}
}
return false;
}
/**
* Calculates compound drawable bounds relative to wrapping view
* @param index compound drawable index
* @param drawable the drawable
* @param view wrapping view
* @return {@link Rect} with relative bounds
*/
private Rect getRelativeBounds(int index, @NonNull Drawable drawable, View view) {
Rect drawableBounds = drawable.getBounds();
Rect bounds = new Rect();
switch (index) {
case LEFT:
bounds.offsetTo(view.getPaddingLeft(),
view.getHeight() / 2 - bounds.height() / 2);
break;
case TOP:
bounds.offsetTo(view.getWidth() / 2 - bounds.width() / 2,
view.getPaddingTop());
break;
case RIGHT:
bounds.offsetTo(view.getWidth() - view.getPaddingRight() - bounds.width(),
view.getHeight() / 2 - bounds.height() / 2);
break;
case BOTTOM:
bounds.offsetTo(view.getWidth() / 2 - bounds.width() / 2,
view.getHeight() - view.getPaddingBottom() - bounds.height());
break;
}
return bounds;
}
/**
* Expands {@link Rect} by given value in every direction relative to its center
* @param source given {@link Rect}
* @return result {@link Rect}
*/
private Rect addFuzz(Rect source) {
Rect result = new Rect();
result.left = source.left - fuzz;
result.right = source.right + fuzz;
result.top = source.top - fuzz;
result.bottom = source.bottom + fuzz;
return result;
}
/**
* Compound drawable touch-event handler
* @param v wrapping view
* @param drawableIndex index of compound drawable which recicved the event
* @param drawableBounds {@link Rect} with compound drawable bounds relative to wrapping view.
* Fuzz not included
* @param event event with coordinated relative to wrapping view - i.e. within {@code drawableBounds}.
* If using fuzz, may return negative coordinates.
*/
protected abstract boolean onDrawableTouch(View v, int drawableIndex, Rect drawableBounds, MotionEvent event);
}
Agora você pode processar qualquer evento de toque em qualquer desenho composto de qualquer TextView que você gosta desta maneira:
textView1.setOnTouchListener(new CompoundDrawableTouchListener() {
@Override
protected void onDrawableTouch(View v, int drawableIndex, Rect drawableBounds, MotionEvent event) {
switch(v.getId()) {
case R.id.textView1:
switch(drawableIndex) {
case CompoundDrawableTouchListener.RIGHT:
doStuff();
break;
}
break;
}
}
});
Interessado apenas em cliques? Basta filtrar pela ação MotionEvent:
/**
* Handles compound drawable click events.
* @see TextView#getCompoundDrawables()
* @see TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)
* @see CompoundDrawableTouchListener
*/
public abstract class CompoundDrawableClickListener extends CompoundDrawableTouchListener {
/**
* Default constructor
*/
public CompoundDrawableClickListener() {
super();
}
/**
* Constructor with fuzz
* @param fuzz desired fuzz in px
*/
public CompoundDrawableClickListener(int fuzz) {
super(fuzz);
}
@Override
protected void onDrawableTouch(View v, int drawableIndex, Rect drawableBounds, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) onDrawableClick(v, drawableIndex);
return true;
}
/**
* Compound drawable touch-event handler
* @param v wrapping view
* @param drawableIndex index of compound drawable which recicved the event
*/
protected abstract void onDrawableClick(View v, int drawableIndex);
}
Novamente, podemos manipular cliques com facilidade em qualquer drawable composto de qualquer TextView:
textView1.setOnTouchListener(new CompoundDrawableClickListener() {
@Override
protected void onDrawableClick(View v, int drawableIndex) {
switch(v.getId()) {
case R.id.textView1:
switch(drawableIndex) {
case CompoundDrawableTouchListener.RIGHT:
doStuff();
break;
}
break;
}
}
});
Espero que você tenha gostado como eu. Vou tentar mantê-lo atualizado aqui e na essência relacionada, se alguma coisa mudar.