改编自Trinea的CompoundDrawablesTextView,thanks
在android的TextView中为我们提供了很方便的在TextView的周围画Drawable
setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)
或者
android:drawableLeft=””
android:drawableTop=””
android:drawableRight=””
android:drawableBottom=”“
但是,并没有对外提供触控事件,而我们需要其点击事件,对于这种情况,就需要了解Android的事件分发机制了
参考:(Trinea)https://github.com/android-cn/android-open-project-analysis/tree/master/tech/touch-event
事件传递过程Activity.dispatchTouchEvent() -> ViewGroup.dispatchTouchEvent() -> View.onTouchEvent()
因此我们只需要重写onTouchEvent()即可。
/** * 设置OnClickListener为当前的listener,即调用{@link CompoundDrawablesTextView#onClick(View)}函数 **/
private void init() {
super.setOnClickListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 在event为actionDown时标记用户点击是否在相应的图片范围内
if (event.getAction() == MotionEvent.ACTION_DOWN) {
resetTouchStatus();
if (mDrawableClickListener != null) {
mIsLeftTouched = touchLeftDrawable(event);
mIsTopTouched = touchTopDrawable(event);
mIsRightTouched = touchRightDrawable(event);
mIsBottomTouched = touchBottomDrawable(event);
}
}
return super.onTouchEvent(event);
}
@Override
public void onClick(View v) {
/** * 按照左上右下的顺序响应第一个点击范围内的Drawable */
if (mDrawableClickListener != null) {
if (mIsLeftTouched) {
mDrawableClickListener.onClick(DrawableClickListener.DrawablePosition.LEFT);
} else if (mIsTopTouched) {
mDrawableClickListener.onClick(DrawableClickListener.DrawablePosition.TOP);
} else if (mIsRightTouched) {
mDrawableClickListener.onClick(DrawableClickListener.DrawablePosition.RIGHT);
} else if (mIsBottomTouched) {
mDrawableClickListener.onClick(DrawableClickListener.DrawablePosition.BOTTOM);
} else {
mDrawableClickListener.onClick(DrawableClickListener.DrawablePosition.TEXT);
}
}
}
Rect对象rect有一个contains方法,只要我们把坐标传进去,就可以通过返回值来得到该坐标是否在该
rect对象所表示的矩形区域了。因此我们可以通过这个方法来进行判断我们所触摸的是哪一个Drawable或者是TextView的text
ps:left drawale
/** * touch左边的Drawable * * @param event * @return 是否在touch范围内 */
private boolean touchLeftDrawable(MotionEvent event) {
if (mLeftDrawable == null) {
return false;
}
// 计算图片点击可响应的范围,计算方法见http://trinea.iteye.com/blog/1562388
int drawHeight = mLeftDrawable.getIntrinsicHeight();
int drawWidth = mLeftDrawable.getIntrinsicWidth();
int topBottomDis = (mTopDrawable == null ? 0 : mTopDrawable.getIntrinsicHeight())
- (mBottomDrawable == null ? 0 : mBottomDrawable.getIntrinsicHeight());
double imageCenterY = 0.5 * (this.getHeight() + topBottomDis);
Rect imageBounds = new Rect(this.getCompoundDrawablePadding() - mLazyX,
(int) (imageCenterY - 0.5 * drawHeight - mLazyY), this.getCompoundDrawablePadding()
+ drawWidth + mLazyX,
(int) (imageCenterY + 0.5 * drawHeight + mLazyY));
return imageBounds.contains((int) event.getX(), (int) event.getY());
}
这里面有两个变量mLazyX,mLazyY,是用来扩大响应范围的。
这里用了一个自定义的接口来处理点击事件 的回调
/** * 图片点击的监听器 * * @author Trinea 2012-5-3 下午11:45:41 */
public interface DrawableClickListener {
/** * 图片的位置 */
enum DrawablePosition {
/** * 图片在TextView的左部 **/
LEFT,
/** * 图片在TextView的上部 **/
TOP,
/** * 图片在TextView的右部 **/
RIGHT,
/** * 图片在TextView的底部 **/
BOTTOM,
/** * 点击的是文字 */
TEXT
}
这里我们通过theme来该改变控件的默认appearance,如果不了解通过theme改变控件属性可参见:
深入解析Android declare-styleable attr style theme(中)
<!--CompoundDrawablesTextView的style-->
<attr name="Compound_Drawables_TextView_Style" format="reference" />
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.CompoundDrawablesTextViewStyleDefault" parent="android:Theme"> <item name="Compound_Drawables_TextView_Style">@style/Widget.cdtStyle</item> </style>
<style name="Widget" /> <style name="Widget.cdtStyle"> <item name="android:gravity">center</item> <item name="android:textColor">#fab</item> </style>
</resources>
public CompoundDrawablesTextView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.Compound_Drawables_TextView_Style);
}
<style name="AppTheme.NoActionBar.Compound_Default"> <item name="Compound_Drawables_TextView_Style">@style/Widget.cdtStyle.CustomcdtStyle</item> </style>
<style name="Widget.cdtStyle.CustomcdtStyle"> <item name="android:drawablePadding">10dp</item> <item name="android:text">"CDT"</item> </style>
<activity android:name="com.bobomee.blogdemos.ui.activity.CompoundDrawablesTextViewActivity" android:theme="@style/AppTheme.NoActionBar.Compound_Default" />
<!--layout.xml-->
<?xml version="1.0" encoding="utf-8"?>
<com.bobomee.commonlibrary.widget.CompoundDrawablesTextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:id="@+id/textWithImage" android:drawableLeft="@mipmap/ic_launcher" android:layout_height="wrap_content"/>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.compound_drawables_textview_layout);
CompoundDrawablesTextView textWithImage = (CompoundDrawablesTextView)this.findViewById(R.id.textWithImage);
textWithImage.setDrawableClickListener(new ImageClickListener());
}
class ImageClickListener implements CompoundDrawablesTextView.DrawableClickListener {
@Override
public void onClick(DrawablePosition position) {
switch (position) {
case LEFT:
// 左边图片被点击的响应
Toast.makeText(CompoundDrawablesTextViewActivity.this, "left", Toast.LENGTH_SHORT).show();
break;
case RIGHT:
// 右边图片被点击的响应
Toast.makeText(CompoundDrawablesTextViewActivity.this, "right", Toast.LENGTH_SHORT).show();
break;
case BOTTOM:
// 底部图片被点击的响应
Toast.makeText(CompoundDrawablesTextViewActivity.this, "bottom", Toast.LENGTH_SHORT).show();
break;
case TOP:
// 上边图片被点击的响应
Toast.makeText(CompoundDrawablesTextViewActivity.this, "top", Toast.LENGTH_SHORT).show();
break;
case TEXT:
Toast.makeText(CompoundDrawablesTextViewActivity.this, "TEXT", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
}
CompoundDrawablesTextView.java@[Github]
完整demo :
CompoundDrawablesTextViewActivity.java@[Github]