Android 可响应drawable点击事件的TextView
16lz
2021-01-23
改编自Trinea的CompoundDrawablesTextView,thanks
概述
在android的TextView中我们可以通过setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)
或者android:drawableLeft系列属性
来给TextView的周围设置Drawable,省去了在旁边写一个ImageView而造成的多了一层布局.
但是,并没有对外提供触控事件,而有时我们却需要给这样一个drawable设置点击事件
实现原理
要实现这样一个需求,其实思路很简单,就是重写onTouchEvent,并判定点击区域是否在图标范围内,然后回调相应的点击事件即可.
核心代码:
@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); } } }
本实例代码见:
CompoundDrawablesTextView@[Github]
原理剖析
rect对象所表示一个矩形区域,其有一个contains方法,只要我们把坐标传进去,就可以通过返回值来得到该坐标是否在该范围内 ,因此我们可以通过这个方法来进行判断我们所触摸的是哪一个Drawable或者是TextView的text
如下判断触摸的是否是左边的Drawable
/** 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,是用来扩大响应范围的,就是我们常说的热区。
Theme
这里我们通过theme来该改变控件的默认appearance,如果不了解通过theme改变控件属性可参考:
Android中的主题和样式
- attrs.xml
<attr name="Compound_Drawables_TextView_Style" format="reference" />
- style.xml
<?xml version="1.0" encoding="utf-8"?><resources> <style name="Theme.CompoundDrawablesTextViewStyleDefault" parent="android:Theme"> <item name="Compound_Drawables_TextView_Style">@style/Widget.cdtStyle style> <style name="Widget" /> <style name="Widget.cdtStyle"> <item name="android:gravity">centeritem> <item name="android:textColor">#fabitem> style>resources>
- 应用
public CompoundDrawablesTextView(Context context, AttributeSet attrs) { this(context, attrs, R.attr.Compound_Drawables_TextView_Style); }
测试
- customstyle.xml
<style name="AppTheme.NoActionBar.Compound_Default"><item name="Compound_Drawables_TextView_Style">@style/Widget.cdtStyle.CustomcdtStyle style> <style name="Widget.cdtStyle.CustomcdtStyle"> <item name="android:drawablePadding">10dpitem> <item name="android:text">"CDT"item> style>
- manifes.xml && layout.xml
<activity android:name="com.bobomee.blogdemos.ui.activity.CompoundDrawablesTextViewActivity"android:theme="@style/AppTheme.NoActionBar.Compound_Default" /> <?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"/>
- java code
@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; } } }
效果图:
更多相关文章
- Android 技巧 - listview 里面的图片不能响应onClick事件?
- android 已省内存方式把图片加载到内存
- Android 列表数据写入到本地Excel文件(包括图片)
- Android Retrofit 2.0框架上传图片,视频解决方案
- Android matrix 控制图片的旋转、缩放、移动
- IM-A820L限制GSM,WCDMA上网的原理(其他泛泰机型可参考)7.13
- android WebView 图片缩放功能小结
- Android 主流图片库Picasso Glide Fresco对比分析