最近在做新版本,各种UI效果都需要自定义,而自定义View点击效果问题一直困扰着我。各种找资料也没有找到自己想要的东西,可能是我关键字打的不对吧。最后在查看TextView的源码时解决了我的问题,由于源码功能太多,不易查询,特此提取记录。
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
"ClickEffectView">
"cev_TextColor" format="color"/>
"cev_Text" format="string"/>
"cev_Background" format="reference"/>
上面View是在按下时改变View的背景以及文字颜色来实现的,要想达到上面的效果,那么问题来了。
答:侦听View的点击状态可以实现View的drawableStateChanged()函数,当View的状态改变时都会调用这个函数来通知View。比如说:按下状态(pressed),聚焦状态(focused),选中状态(selected)。
答:View通过 int[] getDrawableState() 函数来获取获取当前的状态。
答:Android已经为我们提供了方法了,像颜色的动态特征存储及读取可使用ColorStateList类,而背景的动态特征存储及读取则可使用Drawable类。
ColorStateList
存储,在/res/color目录下创建资源文件
"1.0" encoding="utf-8"?>
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android">
- "#00ff00" android:state_pressed="true"/>
- "#666666"/>
读取:
//获取View当前状态
int[] states = getDrawableState();
//获取状态对应的颜色
int textColor = mTextColorList.getColorForState(states, 0);
Drawable
存储,在/res/drawable目录下创建资源文件
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android">
- "@drawable/shape_rectangle_bg_green_border_green_press"
android:state_pressed="true" />
- "@drawable/shape_rectangle_bg_gray_border_gray_normal"/>
读取:
//获取View当前状态
int[] states = getDrawableState();
//Drawable会根据当前状态来改变
mBgDrawable.setState(states);
答:我们只要在drawableStateChanged()函数中通过getDrawableState(),去改变我们想要改变的属性,然后调用View的invalidate()函数,则会调用onDraw()去重绘View了,不过不要忘了在onDraw()函数中绘制时,将这些动态属性赋值,这样才能保证每次重绘的时候拿到的是根据getDrawableStae()获取的值。
答:要想在XML中设置这些属性,则需要根据属性绑定styleable自定义属性。这里就不详细讲了。