Android学习小Demo(22)带删除按钮的TextView

很多时候,会有一些很简单的需求,比如你利用一个Button弹出某个页面,选择了某个对象之后,你会将对象的某些属性,比如名称之类,显示在按钮上。

而紧跟着,又会想着,能不能把刚选择的对象给清掉,比如把按钮上的文字给去掉,这个时候,你就会希望,要是按钮后面还能够有多一个图标,一点击,就把当前控件的文字等清除掉就好了,并且还会对应的回调函数,让我们多处理一些事情,那多好。

很可惜,Android并没有提供现成的控件供我们这样使用,但换个角度想想,这又根本不可惜,因为我们可以自己来实现这样的效果呀,这是多有意思的一件事情啊。

即使有时候,实现很简单,只是做多一点点事情,但当我们把它实现了,满足的感觉依然会油然而生啊,对吧!

现在想做的事情就很简单,想自定义一个控件,实现功能如下:

1)实现像Button一样的点击效果

2)又能够在后面添加一个删除小图标,可删除此控件上的内容

3)还要有一个响应函数,能够在删除之后做一些操作。

我们知道,自定义控件有多种方式,我们可以直接继承View,自己去画各种各样的UI,也可以简单地继承已存在的控件,比如Button就是继承TextView的一个简单的控件。

提到Button就是继承TextView的,而TextView又有CompoundDrawable的概念,那很显然,我们就可以利用TextView来实现我们的功能了。

太啰嗦了,还是先上代码吧

public class ClearableTextView extends TextView {

    public interface OnTextClearListener {
        public void onTextClear(ClearableTextView v);
    }

    private Context mContext;
    private Drawable mDrawableRight;
    private Rect mBounds;

    private OnTextClearListener mOnTextClearListener;

    public ClearableTextView(Context context) {
        super(context);
        initialize(context);
    }

    public ClearableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize(context);
    }

    private void initialize(Context context) {
        mContext = context;
        mDrawableRight = mContext.getResources().getDrawable(R.drawable.ic_delete);
        mDrawableRight.setBounds(0, 0, mDrawableRight.getMinimumWidth(), mDrawableRight.getMinimumWidth());
        setClickable(true);
        setMinWidth(120);
        setGravity(Gravity.CENTER_VERTICAL);
        setPadding(8, 8, 8, 8);
        setCompoundDrawablePadding(8);
    }

    @Override
    public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        if (right != null) {
            mDrawableRight = right;
        }
        super.setCompoundDrawables(left, top, mDrawableRight, bottom);
    }

    public boolean onTouchEvent(MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_UP && mDrawableRight != null) {

            mBounds = mDrawableRight.getBounds();

            final int x = (int) event.getX();
            final int y = (int) event.getY();

            if (x >= (this.getWidth() - mBounds.width()) && x <= (this.getWidth() - this.getPaddingRight())
                    && y >= this.getPaddingTop() && y <= (this.getHeight() - this.getPaddingBottom())) {
                clear();
                event.setAction(MotionEvent.ACTION_CANCEL);
            }
        }

        return super.onTouchEvent(event);
    }

    public void setTextClearable(CharSequence text) {
        setText(text);
        if (text == null || text.length() == 0) {
            super.setCompoundDrawables(null, null, null, null);
        } else {
            super.setCompoundDrawables(null, null, mDrawableRight, null);
        }
    }

    private void clear() {
        setTextClearable("");
        if (mOnTextClearListener != null) {
            mOnTextClearListener.onTextClear(this);
        }
        super.setCompoundDrawables(null, null, null, null);
    }

    public void setOnTextClearListener(OnTextClearListener onTextClearListener) {
        mOnTextClearListener = onTextClearListener;
    }

    public void finalize() throws Throwable {
        mDrawableRight = null;
        mBounds = null;
        super.finalize();
    }
}

我们创建了一个ClearableTextView,继承于TextView,关键的代码如下:

1)设置CompoundDrawables。

我们知道在xml布局中,对于TextView,我们可以利用DrawableLeft, DrawableRight, DrawableTop, DrawableBottom等属性为TextView设置分别处于左右上下的图标,如下:

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableBottom="@drawable/ic_delete"
        android:drawableLeft="@drawable/ic_delete"
        android:drawableRight="@drawable/ic_delete"
        android:drawableTop="@drawable/ic_delete" />

所以在我们的初始化中,我们可以自己先定义一个默认的图标,也可以从xml布局中获取。

当在xml布局中设置了上面对应的属性之后,函数setCompoundDrawables就会被调用,我们可在里面处理重新设置我们的mDrawableRight。

    @Override
    public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        if (right != null) {
            mDrawableRight = right;
        }
        super.setCompoundDrawables(left, top, mDrawableRight, bottom);
    }

2)必须重载onTouchEvent方法。

这是因为在TextView中设置四边的图标时,并没有提供其对应的点击事件,所以这就需要我们自己去拦截用户的点击事件,判断用户的点击区域,人工的去做对应的处理,如下:

    public boolean onTouchEvent(MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_UP && mDrawableRight != null) {

            mBounds = mDrawableRight.getBounds();

            final int x = (int) event.getX();
            final int y = (int) event.getY();

            if (x >= (this.getWidth() - mBounds.width()) && x <= (this.getWidth() - this.getPaddingRight())
                    && y >= this.getPaddingTop() && y <= (this.getHeight() - this.getPaddingBottom())) {
                clear();
                event.setAction(MotionEvent.ACTION_CANCEL);
            }
        }

        return super.onTouchEvent(event);
    }

逻辑很简单,首先要获取图标的矩形区域,同时获取用户点击的点,只要此点落在此矩形区域内,我们就可以认为用户点击了这个按钮,进行我们自己的逻辑处理,然后将此事件的Action设置为Cancel,再利用系统onTouchEvent去处理这个Cancel事件。

代码中的clear方法就是我们自己去处理的逻辑,这个时候,我们就要考虑了,只是单纯做一些操作就可以,还是需要跟外部进行交互呢?

3)定义一个接口,用来由外部实现,可在图标点击事件时触发

    public interface OnTextClearListener {
        public void onTextClear(ClearableTextView v);
    }
...

    private void clear() {
        setTextClearable("");
        if (mOnTextClearListener != null) {
            mOnTextClearListener.onTextClear(this);
        }
        super.setCompoundDrawables(null, null, null, null);
    }

所以,我们在clear方法里面,要判断有没有这样一个接口被实现并传给当前控件,如果存在,我们要调用其方法。

4)为了让文字不存在的时候,删除按钮不出现,我们可以自己定义一个setTextClearable方法,里面包装了TextView的setText方法,但是我们会根据参数内容来判断,要不要显示删除图标。

    public void setTextClearable(CharSequence text) {
        setText(text);
        if (text == null || text.length() == 0) {
            super.setCompoundDrawables(null, null, null, null);
        } else {
            super.setCompoundDrawables(null, null, mDrawableRight, null);
        }
    }

5)当然,最后,在按钮不再存在上下文,即被销毁的时候,我们需要将对应的资源也清空,如下:

    public void finalize() throws Throwable {
        mDrawableRight = null;
        mBounds = null;
        super.finalize();
    }

到这里,整个的实现就讲解完了,虽然很简单,但是还是有学习的地方的,比如接口定义,很多方法的交互,我们可以通过消息传递,也可以通过回调函数来实现,具体就得考虑工作场景了。

好,接下来,就是在外面放置这样一个控件,来看看效果了。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.clearabletextviewdemo.MainActivity$PlaceholderFragment" >

    <com.example.clearabletextviewdemo.ClearableTextView
        android:id="@+id/ctvTest"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

MainActivity中的代码:

		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			View rootView = inflater.inflate(R.layout.fragment_main, container,
					false);
			mCtvTest = (ClearableTextView) rootView.findViewById(R.id.ctvTest);
			mCtvTest.setTextClearable("Hello World");

			mCtvTest.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					mCtvTest.setTextClearable("Hello World!");
				}
			});

			mCtvTest.setOnTextClearListener(new OnTextClearListener() {

				@Override
				public void onTextClear(ClearableTextView v) {
					Toast.makeText(getActivity(), "On Text Cleared ", Toast.LENGTH_SHORT).show();
				}
			});

			return rootView;
		}
	}

最后,当然就是效果图了,如下:

结束!

时间: 2024-10-19 02:49:45

Android学习小Demo(22)带删除按钮的TextView的相关文章

Android学习小Demo(20)关于Fragment的应用

Android在3.0之后引入了Fragment的概念,我猜测其想法可能只是想更好地兼容大屏幕或者平板的开发,因为大屏幕可以展示更多的内容,而内容一多,逻辑有可能就乱,而利用Fragment,则可以将不同的逻辑封装进不同的Fragment中,但是展现呢,还是在同一个Activity中,在同一个屏幕上显示.而对于屏幕并不大的手机来说,如果一个页面展示的东西并不多,那么其实将逻辑直接写在Activity,利用多个Activity实现多个页面的展示,我觉得也是可以接受的,毕竟用Activity还是用F

Android学习小Demo(19)利用Loader来实时接收短信

之前写过一篇文章<Android学习小Demo(13)Android中关于ContentObserver的使用>,在里面利用ContentOberver去监测短信URI内容的变化.我们先来回顾一下,是如何利用ContentOberver来监测短信内容的变化的. 1)要自定义一个类,比如SmsContentObserver,继承ContentObserver,并且实现其onChange方法. 2)在onChange方法中去查询对应Uri,比如短信收件箱的内容,并将对应的记录利用Handler发送

Android学习小Demo(23)Aidl实现进程间通信

我们知道,Android是靠Binder机制来实现进程间的通信,而上一篇文章中,我们利用AIDL,简单地从代码方面的角度讲解了在服务端中的Binder的存在形式,是以服务的实现存在的,而在客户端,则是以代理的形式,实现存在的只是一个关于服务端的Binder实现的引用. 理论上的东西我们要去学习掌握,但是也不能忽略了实际的动手能力,对吧. 今天,我们就一步一步地利用我们所了解地关于AIDL的知识来实现一个跨进程通信的例子. 在Android的上层应用中,每一个App都是一个单独的进程,所以,要实现

Android学习小Demo(21)ListView的联动选择

在日常的App开发中,尤其是在开发生活服务的应用上,非常多时候,我们会须要联动地展现省市区的数据等,需求大概例如以下: 1)展现全部省份 2)当点击某省份的时候,在二级菜单上展现此省份以下所属的城市列表 3)选中返回,显示我们选中的城市 4)当又一次进入选择页面的时候,标识出我们上一次选中(或者说当前已经选择)的值 下图是一个类似的ListView联动选择控件. 1)首先定义一个Layout,左右各放置一个ListView,大概界面例如以下: 2)自己定义一个控件(ValuePicker),在控

Android--&gt;轻松打造带删除按钮的输入框(EditText),附Emoji表情过滤

输入框带删除按钮, 此乃标配, 实现起来方法也很多, 网上开源也很多. 但是, 没事就喜欢瞎折腾. 上图说话. 只是在原生的基础上加了扩展. 相对来说入侵非常少, 使用方法和原生的一模一样.无任何阉割. 完整代码: public class ExEditText extends AppCompatEditText { Rect clearRect = new Rect(); public ExEditText(Context context) { super(context); } public

35.Android之带删除按钮EditText学习

今天实现Android里自定义带删除功能的EditText,效果如下: 当输入内容时,EditText变为带有一个删除功能按钮的编辑框,如图: 实现代码很简单,直接上代码, 布局文件xml: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 an

(Android学习系列)一,用按钮实现时间的显示

我们先用AndroidStudio新建一个项目,选择空白模板,然后像其中拖入两个Button,将他们的id分别命名为btDate(显示日期),btTime(显示时间),他的模板XML代码很简单 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3

带删除按钮的EditText

在使用输入框的时候,常常需要在输入框后带有一键清除输入内容的按钮.采用自定义View的方式是复用性较高的方法.另一方面也可以采用控件"控件+监听"的较为简单的方法来实现. 布局文件: <LinearLayout     android:layout_width="match_parent"     android:layout_height="0dp"     android:layout_weight="1">

Android实战--小DEMO(JAVA关键字学习之语音合成TTS的实现)一

接着上一节讨论的问题,本DEMO中会用到TTS语音合成,我们下面介绍一个同样原理的小例子 看一下布局文件很简单: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" androi