概述
自定义圆角背景TextView
我尽量不打错别字,用词准确,不造成阅读障碍
有一段时间没写博客了,因为一些个人原因,以后会慢慢找回状态
我在实际开发中喜欢将TextView代替Button使用(感觉不是一个好习惯),需求中经常会出现圆角的点击按钮,这个时候其实有很多方案:
1.最传统的就是写一个drawable(也就是shape)的xml文件,但是你也不可能遇到不同圆角就写一个xml,遇到不同背景就写一个xml,遇到不同stroke就写一个xml, 这也太麻烦了。
2.写CardView,将TextView嵌套其中,但是写布局不是力求减少布局嵌套吗?这样真的合适吗?
3.自定义TextView
其实我一直都在用第三种方法,github上也有很多开源的组件,今天主要是介绍一个我认为扩展性比较强的,因为我个人比较看重扩展性
先看效果:
基本满足一般需求吧,才发现,好像stroke绘制的不是很好?
一.定义属性
<attr name="rv_backgroundColor" format="reference|color"/>
<attr name="rv_radius" format="dimension|reference"/>
<attr name="rv_strokeWidth" format="dimension|reference"/>
<attr name="rv_strokeColor" format="reference|color"/>
<attr name="rv_textColor" format="reference|color"/>
<attr name="rv_topLeftRadius" format="dimension|reference"/>
<attr name="rv_topRightRadius" format="dimension|reference"/>
<attr name="rv_bottomLeftRadius" format="dimension|reference"/>
<attr name="rv_bottomRightRadius" format="dimension|reference"/>
定义几个简单属性,这里之所以要定义一个backgroundColor,后面再解释吧,要不然解释起来很空洞。
二.创建一个类,继承自TextView
public class RadiusTextView extends TextView {
private RadiusViewDelegate delegate;
public RadiusTextView(Context context) {
this(context, null);
}
public RadiusTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RadiusTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.delegate = new RadiusViewDelegate(this, context, attrs);
}
public RadiusViewDelegate getDelegate() {
return this.delegate;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
this.delegate.setBgSelector(); //这里是唯一的调用
}
}
看起来有点简单,我们没有像传统的自定义布局一样,直接在onMeasure、onLayout、onDraw方法里面做很多绘制工作,而是另写了一个类——RadiusViewDelegate,将本类(this)通过构造方法传进RadiusViewDelegate中,借助onLayout(…)方法,调用this.delegate.setBgSelector();从而进行绘制操作。这也是这种写法具有很好扩展性的原因。
三.进行RadiusViewDelegate的编写
首先要先将所有自定义属性接收:
//view 就是外面穿进来的RadiusTextView
public RadiusViewDelegate(View view, Context context, AttributeSet attrs) {
this.view = view;
this.context = context;
this.obtainAttributes(context, attrs);
}
private void obtainAttributes(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.radiusTextView);
this.backgroundColor = ta.getColor(R.styleable.radiusTextView_rv_backgroundColor, 0);
this.radius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_radius, 0);
this.strokeWidth = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_strokeWidth, 0);
this.strokeColor = ta.getColor(R.styleable.radiusTextView_rv_strokeColor, 0);
this.textColor = ta.getColor(R.styleable.radiusTextView_rv_textColor, 2147483647);
this.textPressedColor = ta.getColor(R.styleable.radiusTextView_rv_textPressedColor, 2147483647);
this.textEnabledColor = ta.getColor(R.styleable.radiusTextView_rv_textEnabledColor, 2147483647);
this.topLeftRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_topLeftRadius, 0);
this.topRightRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_topRightRadius, 0);
this.bottomLeftRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_bottomLeftRadius, 0);
this.bottomRightRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_bottomRightRadius, 0);
this.isRippleEnable = ta.getBoolean(R.styleable.radiusTextView_rv_rippleEnable, false);
ta.recycle();
}
其实外面只调用了一个方法——setBgSelector(), 所以我们所有的重点都在这里面
private GradientDrawable gdBackground = new GradientDrawable();
public void setBgSelector() {
StateListDrawable bg = new StateListDrawable();
this.setDrawable(this.gdBackground, this.backgroundColor, this.strokeColor);
if (this.view.isEnabled()) {
bg.addState(new int[]{-16842919, -16842913}, this.gdBackground);
}
if (Build.VERSION.SDK_INT >= 16) {
this.view.setBackground(bg);
} else {
this.view.setBackgroundDrawable(bg);
}
}
最后都会走到this.view.setBackground(bg); 所以最重要是这个bg,在这之前首先是创建了一个新的对象StateListDrawable———即bg; 其实这个drawable类对应的是xml里面的selector标签,shap标签对应的是GradientDrawable类,但是在这里,在简单情况下,两者的显示效果是一样的,作者的其实是为了其它功能的原因使用了StateListDrawable,这里先不管。然后调用bg.setDrawable(…)方法,这个方法是设置圆角的。这里的gdBackground是类初始化的时候新new的一个GradientDrawable对象,后面全部代码贴出时会看到,具体看代码:
private void setDrawable(GradientDrawable gd, int color, int strokeColor) {
gd.setColor(color);
if (this.topLeftRadius <= 0 && this.topRightRadius <= 0 && this.bottomRightRadius <= 0 && this.bottomLeftRadius <= 0) {
gd.setCornerRadius((float)this.radius);
} else {
this.radiusArr[0] = (float)this.topLeftRadius;
this.radiusArr[1] = (float)this.topLeftRadius;
this.radiusArr[2] = (float)this.topRightRadius;
this.radiusArr[3] = (float)this.topRightRadius;
this.radiusArr[4] = (float)this.bottomRightRadius;
this.radiusArr[5] = (float)this.bottomRightRadius;
this.radiusArr[6] = (float)this.bottomLeftRadius;
this.radiusArr[7] = (float)this.bottomLeftRadius;
gd.setCornerRadii(this.radiusArr);
}
gd.setStroke(this.strokeWidth, strokeColor);
}
代码很清晰,没啥好解释的,就是设置各种属性;可以看到第一个参数是GradientDrawable,也就是shape,回到上一个代码,接下来是bg.addState(…),这个方法的调用就很重要了,直白的说就是把我们的shap标签加入到selector标签中,就像这样:
<selector>
<item>
<shape>
</shape>
</item>
</selector>
有经验的同学应该知道这样其实效果和只写一个shape标前是一样的,因为简单嘛~,所以最后的this.view.setBackground(bg);和我们平常用的设置背景的方法是一样的。这样整个逻辑就很简单了,其实就是我们代码完成了drawable的XML文件编写,这也解释了为什么定义了一个rv_backgroundColor属性,因为属性顶掉了,我们就是在xml设置background啊,因为view的background属性就被我们顶掉了,如果我们不设置background,那么view就没有颜色了,笑哭~
RadiusViewDelegate完整代码如下:(我删了一部分代码)
package com.study.longl.myselfviewdemo.Views.radiusview;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import com.study.longl.myselfviewdemo.R;
public class RadiusViewDelegate {
private View view;
private Context context;
private GradientDrawable gdBackground = new GradientDrawable();
private int backgroundColor;
private int radius;
private int topLeftRadius;
private int topRightRadius;
private int bottomLeftRadius;
private int bottomRightRadius;
private int strokeWidth;
private int strokeColor;
private int textColor;
private int textPressedColor;
private int textEnabledColor;
private boolean isRadiusHalfHeight;
private boolean isWidthHeightEqual;
private boolean isRippleEnable;
private float[] radiusArr = new float[8];
public RadiusViewDelegate(View view, Context context, AttributeSet attrs) {
this.view = view;
this.context = context;
this.obtainAttributes(context, attrs);
}
private void obtainAttributes(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.radiusTextView);
this.backgroundColor = ta.getColor(R.styleable.radiusTextView_rv_backgroundColor, 0);
this.radius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_radius, 0);
this.strokeWidth = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_strokeWidth, 0);
this.strokeColor = ta.getColor(R.styleable.radiusTextView_rv_strokeColor, 0);
this.textColor = ta.getColor(R.styleable.radiusTextView_rv_textColor, 2147483647);
this.textPressedColor = ta.getColor(R.styleable.radiusTextView_rv_textPressedColor, 2147483647);
this.textEnabledColor = ta.getColor(R.styleable.radiusTextView_rv_textEnabledColor, 2147483647);
this.topLeftRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_topLeftRadius, 0);
this.topRightRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_topRightRadius, 0);
this.bottomLeftRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_bottomLeftRadius, 0);
this.bottomRightRadius = ta.getDimensionPixelSize(R.styleable.radiusTextView_rv_bottomRightRadius, 0);
this.isRippleEnable = ta.getBoolean(R.styleable.radiusTextView_rv_rippleEnable, false);
ta.recycle();
}
public void setBackgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
this.setBgSelector();
}
public void setRadius(int radius) {
this.radius = this.dp2px((float)radius);
this.setBgSelector();
}
public void setStrokeWidth(int strokeWidth) {
this.strokeWidth = this.dp2px((float)strokeWidth);
this.setBgSelector();
}
public void setStrokeColor(int strokeColor) {
this.strokeColor = strokeColor;
this.setBgSelector();
}
public void setTextColor(int textColor) {
this.textColor = textColor;
this.setBgSelector();
}
public void setEadiusHalfHeightEnable(boolean isRadiusHalfHeight) {
this.isRadiusHalfHeight = isRadiusHalfHeight;
this.setBgSelector();
}
public void setWidthHeightEqualEnable(boolean isWidthHeightEqual) {
this.isWidthHeightEqual = isWidthHeightEqual;
this.setBgSelector();
}
public void setTopLeftRadius(int topLeftRadius) {
this.topLeftRadius = topLeftRadius;
this.setBgSelector();
}
public void setTopRightRadius(int topRightRadius) {
this.topRightRadius = topRightRadius;
this.setBgSelector();
}
public void setBottomLeftRadius(int bottomLeftRadius) {
this.bottomLeftRadius = bottomLeftRadius;
this.setBgSelector();
}
public void setBottomRightRadius(int bottomRightRadius) {
this.bottomRightRadius = bottomRightRadius;
this.setBgSelector();
}
public int getBackgroundColor() {
return this.backgroundColor;
}
public int getRadius() {
return this.radius;
}
public int getStrokeWidth() {
return this.strokeWidth;
}
public int getStrokeColor() {
return this.strokeColor;
}
public boolean getRadiusHalfHeightEnable() {
return this.isRadiusHalfHeight;
}
public boolean getWidthHeightEqualEnable() {
return this.isWidthHeightEqual;
}
public int gettopLeftRadius() {
return this.topLeftRadius;
}
public int gettopRightRadius() {
return this.topRightRadius;
}
public int getbottomLeftRadius() {
return this.bottomLeftRadius;
}
public int getbottomRightRadius() {
return this.bottomRightRadius;
}
protected int dp2px(float dp) {
float scale = this.context.getResources().getDisplayMetrics().density;
return (int)(dp * scale + 0.5F);
}
protected int sp2px(float sp) {
float scale = this.context.getResources().getDisplayMetrics().scaledDensity;
return (int)(sp * scale + 0.5F);
}
private void setDrawable(GradientDrawable gd, int color, int strokeColor) {
gd.setColor(color);
if (this.topLeftRadius <= 0 && this.topRightRadius <= 0 && this.bottomRightRadius <= 0 && this.bottomLeftRadius <= 0) {
gd.setCornerRadius((float)this.radius);
} else {
this.radiusArr[0] = (float)this.topLeftRadius;
this.radiusArr[1] = (float)this.topLeftRadius;
this.radiusArr[2] = (float)this.topRightRadius;
this.radiusArr[3] = (float)this.topRightRadius;
this.radiusArr[4] = (float)this.bottomRightRadius;
this.radiusArr[5] = (float)this.bottomRightRadius;
this.radiusArr[6] = (float)this.bottomLeftRadius;
this.radiusArr[7] = (float)this.bottomLeftRadius;
gd.setCornerRadii(this.radiusArr);
}
gd.setStroke(this.strokeWidth, strokeColor);
}
public void setBgSelector() {
StateListDrawable bg = new StateListDrawable();
this.setDrawable(this.gdBackground, this.backgroundColor, this.strokeColor);
if (Build.VERSION.SDK_INT >= 21 && this.isRippleEnable && this.view.isEnabled()) {
RippleDrawable rippleDrawable = new RippleDrawable(this.getColorSelector(this.backgroundColor, this.backgroundColor, this.backgroundColor == 2147483647 ? this.backgroundColor : this.backgroundColor), this.gdBackground, null);
this.view.setBackground(rippleDrawable);
} else {
if (this.view.isEnabled()) {
bg.addState(new int[]{-16842919, -16842913}, this.gdBackground);
}
if (Build.VERSION.SDK_INT >= 16) {
this.view.setBackground(bg);
} else {
this.view.setBackgroundDrawable(bg);
}
}
}
@TargetApi(11)
private ColorStateList getColorSelector(int normalColor, int pressedColor, int enabledColor) {
return new ColorStateList(new int[][]{{16842908}, {16843518}, {16842919}, {-16842910}, new int[0]}, new int[]{pressedColor, pressedColor, pressedColor, enabledColor, normalColor});
}
}
逻辑非常简单,代码量也小,完全没必要引入三方依赖,而且这种写法最闪亮的点在于扩展方便,TextView换成ImageView也可以,换成ReleativeLayout也可以,尤其是kotlin代码,复制粘贴不要太爽。
代码地址:
https://github.com/longlong-2l/MySelfViewDemo
最后
以上就是单薄戒指为你收集整理的自定义圆角背景TextView自定义圆角背景TextView的全部内容,希望文章能够帮你解决自定义圆角背景TextView自定义圆角背景TextView所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复