用什么软件快速做网站,wordpress 笑话模板,湖北省公共资源交易中心,银川网站优化Android自定义Drawable—灵活多变的矩形背景
在安卓开发中#xff0c;我们通常需要为不同的按钮设置不同的背景以实现不同的效果#xff0c;有时还需要这些按钮根据实际情况进行变化。如果采用编写resource中xml文件的形式#xff0c;就需要重复定义许多只有微小变动的资源…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 Effectenum 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;//msprivate 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.rippleColor24 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();}Overrideprotected 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)Overridepublic 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 ripplecanvas.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();}Overrideprotected 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() {Overridepublic 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();}Overridepublic boolean isStateful() {return true;}Overridepublic 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(typeRectType.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(typeRectType.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(strokeColor0)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.leftdelta_width, outerRect.topdelta_height,outerRect.right-delta_width,outerRect.bottom-delta_height);if(this.strokeInPercent0 this.strokeInPercent1){//带边框的块if(this.solidColorthis.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.solidColorHolder0 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;}Overridepublic void setAlpha(int i) {}Overridepublic void setColorFilter(ColorFilter colorFilter) {}Overridepublic 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(color24 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)
LinearLayoutandroid:idid/frd_view_d3android:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:orientationhorizontalandroid:layout_margin10dpandroid:layout_belowid/frd_view_d2Buttonandroid:idid/frd_btn1android:layout_width0dpandroid:layout_heightwrap_contentandroid:layout_weight1android:textbtn1/Buttonandroid:idid/frd_btn2android:layout_width0dpandroid:layout_heightwrap_contentandroid:layout_weight1android:textbtn2android:textColorcolor/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();}});