Android自定义View之点击效果

来源:互联网 发布:主播过度劳累猝死知乎 编辑:程序博客网 时间:2024/05/06 00:53

最近在做新版本,各种UI效果都需要自定义,而自定义View点击效果问题一直困扰着我。各种找资料也没有找到自己想要的东西,可能是我关键字打的不对吧。最后在查看TextView的源码时解决了我的问题,由于源码功能太多,不易查询,特此提取记录。

UI效果

默认效果默认效果
点击效果点击效果

ClickEffectView代码

public class ClickEffectView extends View {    private ColorStateList mTextColorList = ColorStateList.valueOf(Color.GRAY);    private String mContextText;    private Drawable mBgDrawable;    private int mCurTextColor;    private Paint mTextPaint;    private Rect mRect;    public ClickEffectView(Context context) {        this(context,null);    }    public ClickEffectView(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public ClickEffectView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        /*绑定StyleAble自定义属性,使ClickEffectView支持XML赋值属性值*/        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ClickEffectView);        ColorStateList color = null;        color = typedArray.getColorStateList(R.styleable.ClickEffectView_cev_TextColor);        if (color != null) {            setTextColor(color);        }        String text = typedArray.getString(R.styleable.ClickEffectView_cev_Text);        setText(text);        mBgDrawable = typedArray.getDrawable(R.styleable.ClickEffectView_cev_Background);        typedArray.recycle();        mTextPaint = createPaint();        mTextPaint.setTextSize(30);        mRect = new Rect();    }    protected Paint createPaint() {        Paint paint = new Paint();        paint.setAntiAlias(true);        paint.setDither(true);        paint.setFilterBitmap(true);        paint.setXfermode(null);        return paint;    }    @Override    protected void drawableStateChanged() {        super.drawableStateChanged();        updateState();    }    @Override    protected void onDraw(Canvas canvas) {        int width = canvas.getWidth();        int height = canvas.getHeight();        if(mBgDrawable != null){            mBgDrawable.setBounds(0,0,width,height);            mBgDrawable.draw(canvas);        }        //在绘制之前,重新设置颜色,以保证是根据getDrawableState()得到的值        mTextPaint.setColor(mCurTextColor);        mRect.set(0 , 0, width, height);        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt() ;        int baseline = (mRect. bottom + mRect.top - fontMetrics.bottom - fontMetrics.top ) / 2;        // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()        mTextPaint.setTextAlign(Paint.Align. CENTER);        canvas.drawText(TextUtils. isEmpty( mContextText ) ? "" : mContextText ,                mRect.centerX() , baseline, mTextPaint);    }    public void setTextColor(ColorStateList textColor) {        this.mTextColorList = textColor;        updateState();    }    public void setText(String text) {        if(TextUtils.isEmpty(text)){            this.mContextText = "";        }        this.mContextText = text;    }    private void updateState() {        boolean invalidate = false;        //获取View当前状态        int[] states = getDrawableState();        //获取状态对应的颜色        int textColor = mTextColorList.getColorForState(states, 0);        if(textColor != mCurTextColor){            mCurTextColor = textColor;            invalidate = true;        }        if(mBgDrawable != null){            mBgDrawable.setState(states);            invalidate = true;        }        if(invalidate){            invalidate();        }    }}

自定义属性

/res/values/attrs.xml

<declare-styleable name="ClickEffectView">  <!--文字颜色-->  <attr name="cev_TextColor" format="color"/>  <!--文本内容-->  <attr name="cev_Text" format="string"/>  <!--背景-->  <attr name="cev_Background" format="reference"/></declare-styleable>

思考

上面View是在按下时改变View的背景以及文字颜色来实现的,要想达到上面的效果,那么问题来了。

1.怎么侦听View的点击状态呢?

答:侦听View的点击状态可以实现View的drawableStateChanged()函数,当View的状态改变时都会调用这个函数来通知View。比如说:按下状态(pressed),聚焦状态(focused),选中状态(selected)。

2.这么多状态都会通知drawableStateChanged()函数,那怎么知道当前View是什么状态呢?

答:View通过 int[] getDrawableState() 函数来获取获取当前的状态。

3.那么我们要怎么去存储View的这些动态UI特征,又怎么通过一个int[]的View状态信息来读取这些特征?

答:Android已经为我们提供了方法了,像颜色的动态特征存储及读取可使用ColorStateList类,而背景的动态特征存储及读取则可使用Drawable类。
ColorStateList
存储,在/res/color目录下创建资源文件

<?xml version="1.0" encoding="utf-8"?><?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <!--按下后的绿色-->    <item android:color="#00ff00" android:state_pressed="true"/>    <!--默认的灰色-->    <item android:color="#666666"/></selector>

读取:

//获取View当前状态int[] states = getDrawableState();//获取状态对应的颜色int textColor = mTextColorList.getColorForState(states, 0);

Drawable
存储,在/res/drawable目录下创建资源文件

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <!--按下的绿色矩形-->    <item android:drawable="@drawable/shape_rectangle_bg_green_border_green_press"        android:state_pressed="true" />    <!--默认的灰色矩形-->    <item android:drawable="@drawable/shape_rectangle_bg_gray_border_gray_normal"/></selector>

读取:

//获取View当前状态int[] states = getDrawableState();//Drawable会根据当前状态来改变mBgDrawable.setState(states);

4.状态侦听及信息存储都已搞定了,那么View怎么去及时的更新View呢?

答:我们只要在drawableStateChanged()函数中通过getDrawableState(),去改变我们想要改变的属性,然后调用View的invalidate()函数,则会调用onDraw()去重绘View了,不过不要忘了在onDraw()函数中绘制时,将这些动态属性赋值,这样才能保证每次重绘的时候拿到的是根据getDrawableStae()获取的值。

5.怎么在XML文件中设置这些属性?

答:要想在XML中设置这些属性,则需要根据属性绑定styleable自定义属性。这里就不详细讲了。

源码地址

0 0
原创粉丝点击