Android 可响应drawable点击事件的TextView

改编自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

<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" />
           
           
<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 可响应drawable点击事件的TextView_第1张图片

你可能感兴趣的:(自定义控件)