Android自定义Drawable—灵活多变的矩形背景
在安卓开发中,我们通常需要为不同的按钮设置不同的背景以实现不同的效果,有时还需要这些按钮根据实际情况进行变化。如果采用编写resource中xml文件的形式,就需要重复定义许多只有微小变动的资源文件。这使得整个工程的简洁性和可读性受到影响。本文将介绍一种基于java代码实现的矩形背景自定义工具,该工具是继承Drawable的基础上开发的,它具有如下功能:
- 自定义矩形的内部填充颜色和边框颜色;
- 自定义矩形四角的弧度,支持分别定义和整体定义;
- 自定义矩形的阴影(颜色、宽度及位置);
- 自定义矩形的触摸水波纹效果(颜色、显示速度);
- 自定义矩形的边框宽度(按比例/按固定宽度),并可与ObjectAnimator配合实现动画效果
综合展示如下:
直接上源码:
public class FlexibleRectDrawable extends Drawable {
private Paint paint_stroke;
private Paint paint_fill;
private Paint paint_ripple;
private RectF outerRect;
private RectF innerRect;
private RectF zeroRect;
//矩形内部颜色及边框颜色
private int solidColor;
private int strokeColor;
private int solidColorHolder;
private int strokeColorHolder;
//边框设置
private boolean hasStroke;
private float strokeWidth;
private float strokeWidthVariable;//可变动的边框宽度,用于生成后调整宽度
private float strokeInPercent;//0~1 边框与整体大小的占比
//圆角半径
private float rectRadius;
//圆角位置
private int corners;
//阴影设置
private boolean needShadow;
private float shadowRange;//阴影粗细
private float shadowDx;//阴影中心x轴偏移
private float shadowDy;//阴影中心y轴偏移
private int shadowColor;//阴影颜色
//阴影位置
private float offsetLeft;
private float offsetTop;
private float offsetRight;
private float offsetBottom;
//Ripple Effect
enum RippleAnimState{STATE_ENTER, STATE_EXIT, STATE_EXIT_LATER}
private RippleAnimState ripple_anim_state;
private ObjectAnimator ripple_alpha_animator;
private ObjectAnimator ripple_radius_animator;
private PointF currentPoint;
private PointF pressedPoint;
private Path ripple_bound_path;
private boolean needRipple;
private int rippleSpeed;//ms
private float maxRippleRadius;
private float rippleRadius;//属性动画
private int rippleColor;
private int maxRippleAlpha;
private int rippleAlpha;//属性动画
public static final int SQUARE_CORNER = 0;
public static final int CORNER_TOP_LEFT = 1;
public static final int CORNER_TOP_RIGHT = 1 << 1;
public static final int CORNER_BOTTOM_LEFT = 1 << 2;
public static final int CORNER_BOTTOM_RIGHT = 1 << 3;
public static final int CORNER_HALF_LEFT = CORNER_TOP_LEFT | CORNER_BOTTOM_LEFT;
public static final int CORNER_HALF_RIGHT = CORNER_TOP_RIGHT | CORNER_BOTTOM_RIGHT;
public static final int CORNER_ALL = CORNER_TOP_LEFT | CORNER_TOP_RIGHT | CORNER_BOTTOM_LEFT | CORNER_BOTTOM_RIGHT;
public enum RectType{BORDER_ONLY,SOLID_BLOCK,BORDERED_BLOCK,NOT_DEFINED}
private RectType type;
public FlexibleRectDrawable() {
//默认值
this.type = RectType.NOT_DEFINED;
this.solidColor = 0;//透明色
this.strokeColor = 0;//透明色
this.solidColorHolder = 0;
this.strokeColorHolder = 0;
this.hasStroke = false;
this.strokeWidth = 0;
this.strokeWidthVariable = 0;
this.corners = SQUARE_CORNER;
this.rectRadius = 0;
this.needShadow = false;
this.shadowRange = 0;
this.shadowDx = 0;
this.shadowDy = 0;
this.shadowColor = Color.parseColor("#aa000000");
this.offsetBottom = 0;
this.offsetLeft = 0;
this.offsetTop = 0;
this.offsetRight = 0;
this.currentPoint = new PointF();
this.pressedPoint = new PointF();
this.rippleColor = Color.parseColor("#21000000");
this.maxRippleAlpha = this.rippleColor>>24 & 0xFF;
this.ripple_bound_path = new Path();
}
public void setupPainters() {
paint_stroke = new Paint();
paint_stroke.setAntiAlias(true);
paint_stroke.setFilterBitmap(true);
paint_stroke.setDither(true);
paint_stroke.setStyle(Paint.Style.FILL);
paint_stroke.setColor(strokeColor);
//设置阴影
if(needShadow)
paint_stroke.setShadowLayer(shadowRange, shadowDx, shadowDy, shadowColor);
paint_fill = new Paint();
paint_fill.setAntiAlias(true);
paint_fill.setFilterBitmap(true);
paint_fill.setDither(true);
paint_fill.setStyle(Paint.Style.FILL);
paint_fill.setColor(solidColor);
//设置水波纹效果
paint_ripple = new Paint();
paint_ripple.setAntiAlias(true);
paint_ripple.setStyle(Paint.Style.FILL);
paint_ripple.setColor(rippleColor);
invalidateSelf();
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
if (bounds.right - bounds.left > 0 && bounds.bottom - bounds.top > 0) {
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
outerRect = new RectF(offsetLeft, offsetTop, width - offsetRight, height - offsetBottom);
innerRect = new RectF(offsetLeft + strokeWidth, offsetTop + strokeWidth,
width - offsetRight - strokeWidth, height - offsetBottom - strokeWidth);
zeroRect = new RectF(width/2.0f, height/2.0f, width/2.0f, height/2.0f);
invalidateSelf();
}
}
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public void draw(Canvas canvas) {
float[] Radii = {0,0,0,0,0,0,0,0};
if ((corners & CORNER_TOP_LEFT) != 0) {
Radii[0] = rectRadius;
Radii[1] = rectRadius;
}
if ((corners & CORNER_TOP_RIGHT) != 0) {
Radii[2] = rectRadius;
Radii[3] = rectRadius;
}
if ((corners & CORNER_BOTTOM_RIGHT) != 0) {
Radii[4] = rectRadius;
Radii[5] = rectRadius;
}
if ((corners & CORNER_BOTTOM_LEFT) != 0) {
Radii[6] = rectRadius;
Radii[7] = rectRadius;
}
switch(type){
case BORDER_ONLY:
canvas.drawDoubleRoundRect(outerRect, Radii, innerRect, Radii, paint_stroke);
break;
case SOLID_BLOCK:
canvas.drawDoubleRoundRect(outerRect,Radii,zeroRect,Radii, paint_fill);
break;
case BORDERED_BLOCK:
canvas.drawDoubleRoundRect(outerRect, Radii, innerRect, Radii, paint_stroke);
canvas.drawDoubleRoundRect(innerRect,Radii,zeroRect,Radii, paint_fill);
break;
case NOT_DEFINED:
throw new RuntimeException("RectType undefined");
default:
}
//draw ripple
canvas.save();
ripple_bound_path.addRoundRect(innerRect,Radii,Path.Direction.CW);
canvas.clipPath(ripple_bound_path);
if(ripple_anim_state == STATE_ENTER){
paint_ripple.setAlpha(rippleAlpha);
canvas.drawCircle(pressedPoint.x, pressedPoint.y, rippleRadius, paint_ripple);
}else if(ripple_anim_state == STATE_EXIT){
paint_ripple.setAlpha(rippleAlpha);
canvas.drawDoubleRoundRect(innerRect,Radii,zeroRect,Radii, paint_ripple);
}
canvas.restore();
}
@Override
protected boolean onStateChange(int[] stateSet) {
boolean enable = false;
boolean pressed = false;
for (int st : stateSet) {
switch (st) {
case android.R.attr.state_pressed:
pressed = true;
break;
case android.R.attr.state_enabled:
enable = true;
break;
}
}
if (!enable) return false;
if (!needRipple)return false;
if (pressed) {
startRippleAnimation();
return true;
} else if (ripple_anim_state == STATE_ENTER) {
exitRippleAnimation();
return true;
} else {
return false;
}
}
private void startRippleAnimation() {
ripple_anim_state = STATE_ENTER;
pressedPoint.set(currentPoint);
maxRippleRadius = Math.max(innerRect.width(), innerRect.height());
if(ripple_radius_animator != null && ripple_radius_animator.isRunning()){
ripple_radius_animator.cancel();
}
ripple_radius_animator = new ObjectAnimator();
ripple_radius_animator.setTarget(this);
ripple_radius_animator.setPropertyName("rippleRadius");
ripple_radius_animator.setInterpolator(new LinearInterpolator());
ripple_radius_animator.setDuration(rippleSpeed);
ripple_radius_animator.setFloatValues(0,maxRippleRadius);
ripple_radius_animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if(ripple_anim_state == STATE_EXIT_LATER){
ripple_anim_state = STATE_EXIT;
exitRippleAnimation();
}
}
});
ripple_radius_animator.start();
}
private void exitRippleAnimation() {
ripple_alpha_animator = new ObjectAnimator();
ripple_alpha_animator.setTarget(this);
ripple_alpha_animator.setPropertyName("rippleAlpha");
ripple_alpha_animator.setInterpolator(new LinearInterpolator());
ripple_alpha_animator.setDuration(300);
ripple_alpha_animator.setIntValues(maxRippleAlpha,0);
ripple_alpha_animator.start();
}
@Override
public boolean isStateful() {
return true;
}
@Override
public void setHotspot(float x, float y) {
currentPoint.set(x,y);
}
public float getOffsetLeft() {
return offsetLeft;
}
public void setOffsetLeft(float offsetLeft) {
this.offsetLeft = offsetLeft;
}
public float getOffsetTop() {
return offsetTop;
}
public void setOffsetTop(float offsetTop) {
this.offsetTop = offsetTop;
}
public float getOffsetRight() {
return offsetRight;
}
public void setOffsetRight(float offsetRight) {
this.offsetRight = offsetRight;
}
public float getOffsetBottom() {
return offsetBottom;
}
public void setOffsetBottom(float offsetBottom) {
this.offsetBottom = offsetBottom;
}
public float getRectRadius() {
return rectRadius;
}
public void setRectRadius(float rectRadius) {
this.rectRadius = rectRadius;
}
public void setCorners(int corners) {
this.corners = corners;
}
public FlexibleRectDrawable setColor(int color) {
paint_stroke.setColor(color);
return this;
}
public int getSolidColor() {
return solidColor;
}
public void setSolidColor(int solidColor) {
this.solidColor = solidColor;
}
public int getStrokeColor() {
return strokeColor;
}
public void setStrokeColor(int strokeColor) {
this.strokeColor = strokeColor;
}
public boolean isHasStroke() {
return hasStroke;
}
public void setHasStroke(boolean hasStroke) {
this.hasStroke = hasStroke;
}
public boolean isNeedShadow() {
return needShadow;
}
public void setNeedShadow(boolean needShadow) {
this.needShadow = needShadow;
}
public float getShadowRange() {
return shadowRange;
}
public void setShadowRange(float shadowRange) {
this.shadowRange = shadowRange;
}
public float getShadowDx() {
return shadowDx;
}
public void setShadowDx(float shadowDx) {
this.shadowDx = shadowDx;
}
public float getShadowDy() {
return shadowDy;
}
public void setShadowDy(float shadowDy) {
this.shadowDy = shadowDy;
}
public int getShadowColor() {
return shadowColor;
}
public void setShadowColor(int shadowColor) {
this.shadowColor = shadowColor;
}
float getStrokeWidth() {
return strokeWidth;
}
void setStrokeWidth(float strokeWidth) {
this.strokeWidth = strokeWidth;
this.strokeWidthVariable = strokeWidth;
}
public float getStrokeWidthVariable() {
if(type==RectType.SOLID_BLOCK)
return Math.min((getBounds().width() - offsetRight),(getBounds().height() - offsetBottom));
return strokeWidthVariable;
}
public void setStrokeWidthVariable(float strokeWidthVariable) {
this.strokeWidthVariable = strokeWidthVariable;
int width = getBounds().width();
int height = getBounds().height();
System.out.println("type= " + type.name() + " stroke = " + strokeWidthVariable+" width = "+width+" height = "+height);
if((width - offsetRight)<=strokeWidthVariable||
(height - offsetBottom)<=strokeWidthVariable){
//边框宽大到可以认为是纯色块
if(type == RectType.BORDERED_BLOCK && this.solidColor!=0){
this.solidColorHolder = this.solidColor;
this.solidColor = this.strokeColor;
}
//若内部无色,则用边框颜色作为填充
if(type == RectType.BORDER_ONLY && this.strokeColor!=0) {
this.solidColor = this.strokeColor;
}
type = RectType.SOLID_BLOCK;
}
else{
if(type==RectType.SOLID_BLOCK){
this.strokeColor = this.solidColor;
if(this.solidColorHolder == 0){
type = RectType.BORDER_ONLY;//纯色块转变为仅带边框的块
}
else {
this.solidColor = this.solidColorHolder;
type = RectType.BORDERED_BLOCK;
}
}
innerRect.set(offsetLeft + strokeWidthVariable, offsetTop + strokeWidthVariable,
width - offsetRight - strokeWidthVariable,
height - offsetBottom - strokeWidthVariable);
}
setupPainters();//重设画笔,并重绘
}
public void setStrokeInPercent(@FloatRange(from= 0.0f,to= 1.0f) float strokeInPercent) {
if(strokeColor==0)throw new IllegalArgumentException("setStrokeInPercent函数仅适用于带边框的Drawable");
this.strokeInPercent = strokeInPercent;
float delta_width = outerRect.width()*strokeInPercent/2;
float delta_height = outerRect.height()*strokeInPercent/2;
//System.out.println("delta_height= " + delta_height + " delta_width= " + delta_width);
innerRect.set(outerRect.left+delta_width, outerRect.top+delta_height,
outerRect.right-delta_width,outerRect.bottom-delta_height);
if(this.strokeInPercent>0 && this.strokeInPercent<1){
//带边框的块
if(this.solidColor==this.strokeColor && this.solidColorHolder == 0)
this.type = RectType.BORDER_ONLY;//转变为仅带边框的块
else{
if(this.solidColorHolder!=0){
this.solidColor = this.solidColorHolder;
this.solidColorHolder = 0;
}
this.type = RectType.BORDERED_BLOCK;
}
}
else if(this.strokeInPercent == 0){
//内部填充的纯色块
this.type = RectType.SOLID_BLOCK;
if(solidColor == 0)Log.e("FlexibleRectDrawable","Drawable被绘制为透明色");
}
else if(this.strokeInPercent == 1){
//边框填充的纯色块
this.type = RectType.SOLID_BLOCK;
if(this.solidColorHolder==0 && this.solidColor!=this.strokeColor){
this.solidColorHolder = this.solidColor;
this.solidColor = this.strokeColor;
}
}
setupPainters();
}
public float getStrokeInPercent() {
float inner_width = innerRect.width();
float outer_width = outerRect.width();
return (1-inner_width/outer_width);
}
public RectType getType() {
return type;
}
public void setType(RectType type) {
this.type = type;
}
public float getRippleRadius() {
return rippleRadius;
}
public void setRippleRadius(float rippleRadius) {
this.rippleRadius = rippleRadius;
invalidateSelf();
}
public int getRippleColor() {
return rippleColor;
}
public void setRippleColor(int rippleColor) {
this.rippleColor = rippleColor;
}
public int getMaxRippleAlpha() {
return maxRippleAlpha;
}
public void setMaxRippleAlpha(int maxRippleAlpha) {
this.maxRippleAlpha = maxRippleAlpha;
}
public int getRippleAlpha() {
return rippleAlpha;
}
public void setRippleAlpha(int rippleAlpha) {
this.rippleAlpha = rippleAlpha;
invalidateSelf();
}
public boolean isNeedRipple() {
return needRipple;
}
public void setNeedRipple(boolean needRipple) {
this.needRipple = needRipple;
}
public float getMaxRippleRadius() {
return maxRippleRadius;
}
public void setMaxRippleRadius(float maxRippleRadius) {
this.maxRippleRadius = maxRippleRadius;
}
public int getRippleSpeed() {
return rippleSpeed;
}
public void setRippleSpeed(int rippleSpeed) {
this.rippleSpeed = rippleSpeed;
}
@Override
public void setAlpha(int i) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
此外,设置一个Builder让自定义构建变得更容易:
public static class Builder{
private FlexibleRectDrawable drawable;
public Builder() {
this.drawable = new FlexibleRectDrawable();
}
public static Builder create(){
return new Builder();
}
public Builder setSolidFill(@ColorInt int color){
this.drawable.setSolidColor(color);
switch(this.drawable.getType()){
case BORDER_ONLY:
this.drawable.setType(RectType.BORDERED_BLOCK);
break;
case SOLID_BLOCK:case BORDERED_BLOCK:
Log.i("DrawableBuilder","cover solid color");
break;
case NOT_DEFINED:
this.drawable.setType(RectType.SOLID_BLOCK);
break;
default:
}
return this;
}
public Builder setStroke(float width,@ColorInt int color){
this.drawable.setHasStroke(true);
this.drawable.setStrokeColor(color);
this.drawable.setStrokeWidth(width);
switch(this.drawable.getType()){
case BORDER_ONLY:case BORDERED_BLOCK:
Log.i("DrawableBuilder","cover solid color");
break;
case SOLID_BLOCK:
this.drawable.setType(RectType.BORDERED_BLOCK);
break;
case NOT_DEFINED:
this.drawable.setType(RectType.BORDER_ONLY);
break;
default:
}
return this;
}
public Builder setShadow(float shadowRange,@ColorInt int color){
this.drawable.setNeedShadow(true);
this.drawable.setShadowRange(shadowRange);
this.drawable.setShadowColor(color);
return this;
}
public Builder setShadowOffset(float top, float bottom, float left, float right){
if(!this.drawable.isNeedShadow())throw new IllegalArgumentException("必须先调用setShadow,再设置阴影位置");
this.drawable.setOffsetTop(top);
this.drawable.setOffsetBottom(bottom);
this.drawable.setOffsetLeft(left);
this.drawable.setOffsetRight(right);
return this;
}
public Builder setShadowOffsetCenter(float offset){
if(!this.drawable.isNeedShadow())throw new IllegalArgumentException("必须先调用setShadow,再设置阴影位置");
this.drawable.setOffsetTop(offset);
this.drawable.setOffsetBottom(offset);
this.drawable.setOffsetLeft(offset);
this.drawable.setOffsetRight(offset);
return this;
}
public Builder setCorners(int radius,int corner_type){
this.drawable.setRectRadius(radius);
this.drawable.setCorners(corner_type);
return this;
}
public Builder setRipple(int color, int speed_millisecond){
this.drawable.setNeedRipple(true);
int check = color >>24;
if (check==-1)throw new IllegalArgumentException("ripple颜色必须具有透明色");
this.drawable.setRippleColor(color);
this.drawable.setMaxRippleAlpha(color>>24 & 0xFF);
this.drawable.setRippleSpeed(speed_millisecond);
return this;
}
public FlexibleRectDrawable build(){
this.drawable.setupPainters();
return this.drawable;
}
}
辅助函数:dp转px
public int dp2Px(float dpValue) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
使用方法
1. 一个普通的圆角按钮
FlexibleRectDrawable drawable1 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3), Color.parseColor("#4682B4"))
.setSolidFill(Color.parseColor("#DAA520"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_ALL)
.build();
LinearLayout view1 = findViewById(R.id.frd_view1);
view1.setBackground(drawable1);
2. 一个带水波纹效果的按钮
实心/空心:
//实心按钮
FlexibleRectDrawable drawabled1 = FlexibleRectDrawable.Builder.create()
.setSolidFill(Color.parseColor("#4682B4"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_ALL)
.setRipple(Color.parseColor("#22FFFFFF"),300)
.build();
//空心按钮
FlexibleRectDrawable drawable2 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3), Color.parseColor("#4682B4"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_ALL)
.setRipple(Color.parseColor("#22000000"),300)
.build();
3. 一个带阴影的按钮
实心/空心:
//空心
FlexibleRectDrawable drawabled2 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3),Color.parseColor("#1E90FF"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_ALL)
.setShadow(dp2Px(7), Color.parseColor("#fe00FFFF"))
.setShadowOffsetCenter(dp2Px(5))
.build();
//实心
FlexibleRectDrawable drawable2 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3), Color.parseColor("#4682B4"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_ALL)
.setSolidFill(Color.parseColor("#FFFFFF"))
.setShadow(dp2Px(5), Color.parseColor("#FEA9A9A9"))
.setShadowOffsetCenter(dp2Px(5))
.build();
4. 两个左右合并的按钮
布局文件(.xml):
<LinearLayout
android:id="@+id/frd_view_d3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="10dp"
android:layout_below="@id/frd_view_d2">
<Button
android:id="@+id/frd_btn1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="btn1"/>
<Button
android:id="@+id/frd_btn2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="btn2"
android:textColor="@color/white"/>
</LinearLayout>
对应java代码:
FlexibleRectDrawable drawable_btn_left = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3),Color.parseColor("#1E90FF"))
.setSolidFill(Color.parseColor("#1E90FF"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_HALF_LEFT)
.setRipple(Color.parseColor("#33000000"),300)
.build();
FlexibleRectDrawable drawable_btn_right = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3),Color.parseColor("#1E90FF"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_HALF_RIGHT)
.setRipple(Color.parseColor("#33FFFFFF"),300)
.build();
Button btn_left = findViewById(R.id.frd_btn1);
btn_left.setBackground(drawable_btn_left);
Button btn_right = findViewById(R.id.frd_btn2);
btn_right.setBackground(drawable_btn_right);
5. 按固定宽度设置矩形边框
改变边框的宽度和颜色
private boolean on_view3 = false;
...
FlexibleRectDrawable drawable3 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3), Color.parseColor("#4682B4"))
.setSolidFill(Color.parseColor("#131313"))
.setCorners(dp2Px(50), FlexibleRectDrawable.CORNER_ALL)
.build();
LinearLayout view3 = findViewById(R.id.frd_view3);
view3.setBackground(drawable3);
view3.setOnClickListener(v -> {
on_view3 = !on_view3;
if(on_view3){
drawable3.setStrokeColor(Color.RED);
drawable3.setStrokeWidthVariable(dp2Px(5));
}
else{
drawable3.setStrokeColor(Color.parseColor("#4682B4"));
drawable3.setStrokeWidthVariable(dp2Px(3));
}
});
通过属性动画实现按钮状态切换
private boolean on_view5 = false;
...
FlexibleRectDrawable drawabled5 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3),Color.parseColor("#4682B4"))
.setSolidFill(Color.parseColor("#3CB371"))
.setCorners(dp2Px(5), FlexibleRectDrawable.CORNER_ALL)
.build();
LinearLayout viewd5 = findViewById(R.id.frd_view_d5);
viewd5.setBackground(drawabled5);
viewd5.setOnClickListener(v -> {
on_view5 = !on_view5;
if(on_view5){
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(drawabled5);
animator.setPropertyName("strokeWidthVariable");
animator.setDuration(1000);
animator.setFloatValues(drawabled5.getStrokeWidthVariable(),dp2Px(300));
animator.start();
}
else {
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(drawabled5);
animator.setPropertyName("strokeWidthVariable");
animator.setDuration(1000);
animator.setFloatValues(drawabled5.getStrokeWidthVariable(),dp2Px(3));
animator.start();
}
});
6. 按所占百分比设置矩形边框
private boolean on_view6 = false;
private float origin_percent = 0.0f;
...
FlexibleRectDrawable drawabled6 = FlexibleRectDrawable.Builder.create()
.setStroke(dp2Px(3),Color.parseColor("#4682B4"))
.setCorners(dp2Px(5), FlexibleRectDrawable.CORNER_ALL)
.build();
LinearLayout viewd6 = findViewById(R.id.frd_view_d6);
viewd6.setBackground(drawabled6);
viewd6.setOnClickListener(v -> {
on_view6 = !on_view6;
if (on_view6) {
origin_percent = drawabled6.getStrokeInPercent();
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(drawabled6);
animator.setPropertyName("strokeInPercent");
animator.setDuration(500);
animator.setFloatValues(origin_percent, 1f);
animator.start();
}
else {
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(drawabled6);
animator.setPropertyName("strokeInPercent");
animator.setDuration(500);
animator.setFloatValues(drawabled6.getStrokeInPercent(), origin_percent);
animator.start();
}
});