继承drawable方式实现
刚好修改了下绘制范围,以及按钮动画播放
文字以及开关,拖动条进度条的实现可参考这个的实现方式,时间紧张就没弄
package com.android.view;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
public class DrawableSpan extends Drawable {
private float[] angle = new float[8];
private Draw draw;
private int width,height,color = 0xffeeeeee,color_right = 0xffeeeeee,color_bottom = 0xffeeeeee,shadowX = 40,shadowY = 40;
private Paint paint,paint_right,paint_bottom;
private Path path,path_right,path_bottom;
private DrawableSpan.Style style = DrawableSpan.Style.CIRCLE;
private DrawableSpan.Model model = DrawableSpan.Model.FLAT;
public DrawableSpan(Style s , int w, int h) {
init(s, w, h);
}
public DrawableSpan(int w, int h) {
init(Style.CIRCLE, w, h);
}
public DrawableSpan(DrawableSpan d) {
this(d, d.getStyle(), d.getModel());
}
public DrawableSpan(DrawableSpan d, Model m) {
this(d, d.getStyle(), m);
}
public DrawableSpan(DrawableSpan d, Style s, Model m) {
init(s, d.getIntrinsicWidth(), d.getIntrinsicHeight());
int[] con = d.getContainerdeltaLength();
setContainerdeltaLength(con[0], con[1]);
setRound(d.getRound());
setColor(d.getColor());
setModel(m);
setColorFilter(d.getColorFilter());
setAlpha(d.getAlpha());
}
private void init(Style s, int w, int h) {
style = s;
width = w;
height = h;
super.setBounds(0, 0, w , h);
paint = new Paint(ANTI_ALIAS_FLAG);
paint_right = new Paint(ANTI_ALIAS_FLAG);
paint_bottom = new Paint(ANTI_ALIAS_FLAG);
path = new Path();
path_right = new Path();
path_bottom = new Path();
}
@Override
public void draw(Canvas c) {
if (c.isHardwareAccelerated()) {
Log.w("DrawableSpan$onDraw{Canvas}", "Accelerated by hardware!");
}
c.clipRect(-(shadowX + 10), -(shadowY + 10), width + shadowX + 10, height + shadowY + 10);
if (model == Model.CONCAVE) {
c.clipPath(path);
c.drawPath(path, paint);
c.drawPath(path_bottom, paint_bottom);
c.drawPath(path_right, paint_right);
} else {
c.drawPath(path_bottom, paint_bottom);
c.drawPath(path_right, paint_right);
c.drawPath(path, paint);
}
try {
if (draw != null) {
draw.onDraw(this, c);
}
} catch (Exception e) {
Log.w("DrawableSpan$onDraw{Canvas}", e.toString());
}
}
public void setContainerdeltaLength(int x, int y) {
shadowX = (int) (x / 1.25);
shadowY = (int) (y / 1.25);
}
public void setContainerdeltaLength(int i) {
setContainerdeltaLength(i, i);
}
public void setStyle(Style s) {
if (s != null) {
style = s;
}
}
public void setModel(Model m) {
if (m != null) {
model = m;
}
}
public void setColor(int i) {
setColor(i, i);
}
public void setColor(int v, int i) {
color = v;
color_right = i;
color_bottom = i;
}
public void setRound(float[] r) {
if (r == null) {
return;
}
switch (r.length) {
case 1:
for (int i=0;i < 8;i++) {
angle[i] = r[0];
}
break;
case 4:
for (int i = 0,v = 0;i < 8;i++) {
if (i % 2 != 0) {
angle[i - 1] = r[v];
angle[i] = r[v];
v++;
}
}
break;
case 8:
for (int i=0;i < 8;i++) {
angle[i] = r[i];
}
break;
default:
for (int i=0;i < 8;i++) {
if (i < r.length) {
angle[i] = r[i];
} else {
angle[i] = 0;
}
}
break;
}
}
public DrawableSpan invalidate(int w, int h) {
width = w;
height = h;
super.setBounds(0, 0, w , h);
return invalidate();
}
public DrawableSpan invalidate() {
postColor(color, color_right, color_bottom);
postPath(width, height);
super.invalidateSelf();
return this;
}
public void setDraw(Draw d) {
draw = d;
}
@Override
public void setAlpha(int i) {
paint.setAlpha(i);
paint_right.setAlpha(i);
paint_bottom.setAlpha(i);
}
@Override
public void setColorFilter(ColorFilter f) {
paint.setColorFilter(f);
paint_right.setColorFilter(f);
paint_bottom.setColorFilter(f);
}
public Model getModel() {
return model;
}
public Style getStyle() {
return style;
}
public int getColor() {
return color;
}
public float[] getRound() {
return angle;
}
public int[] getContainerdeltaLength() {
return new int[]{(int)(shadowX * 1.25),(int)(shadowY * 1.25)};
}
@Override
public int getIntrinsicWidth() {
return super.getBounds().width() - shadowX;
}
@Override
public int getIntrinsicHeight() {
return super.getBounds().height() - shadowY;
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public enum Style {
RECTANGLE
, CIRCLE;
}
public enum Model {
FLAT
, CONCAVE
, CONVEX
, PRESSED;
}
public interface Draw {
void onDraw(DrawableSpan drawable, Canvas canvas);
}
private void postColor(int i, int r, int b) {
paint.setShader(null);
paint_right.setShadowLayer(0, 0, 0, 0);
paint_bottom.setShadowLayer(0, 0, 0, 0);
int rc = manipulateColor(r, 1.1f);
int bc = manipulateColor(b, 0.9f);
paint.setColor(i);
paint_right.setColor(rc);
paint_bottom.setColor(bc);
if (model == Model.CONCAVE || model == Model.PRESSED) {
LinearGradient linearGradient = new LinearGradient(0f, 0f
, width, height
, bc, rc
, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
} else if (model == Model.CONVEX) {
LinearGradient linearGradient = new LinearGradient(0f, 0f
, width, height
, rc, bc
, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
}
if (model != Model.CONCAVE || model != Model.CONVEX) {
paint_right.setShadowLayer(shadowX, -shadowX / 2, -shadowX / 2, rc);
paint_bottom.setShadowLayer(shadowY, shadowY / 2, shadowY / 2, bc);
}
}
private void postPath(int w, int h) {
if (path_right.isInverseFillType()) {
path_right.toggleInverseFillType();
}
if (path_bottom.isInverseFillType()) {
path_bottom.toggleInverseFillType();
}
path.reset();
path_right.reset();
path_bottom.reset();
if (style == Style.RECTANGLE) {
path.addRoundRect(0, 0, w , h , angle, Path.Direction.CW);
path_right.addRoundRect(0, 0, w , h , angle, Path.Direction.CCW);
path_bottom.addRoundRect(0, 0, w , h, angle, Path.Direction.CCW);
} else {
float radius = h < w ? h / 2 : w / 2;
path.addCircle(w / 2, h / 2, radius, Path.Direction.CW);
path_right.addCircle(w / 2, h / 2, radius, Path.Direction.CW);
path_bottom.addCircle(w / 2, h / 2, radius, Path.Direction.CW);
}
path.close();
path_right.close();
path_bottom.close();
if (model == Model.CONCAVE) {
if (!path_right.isInverseFillType()) {
path_right.toggleInverseFillType();
}
if (!path_bottom.isInverseFillType()) {
path_bottom.toggleInverseFillType();
}
}
}
public static int manipulateColor(int color, float factor) {
int a = Color.alpha(color);
int r = Math.round(Color.red(color) * factor);
int g = Math.round(Color.green(color) * factor);
int b = Math.round(Color.blue(color) * factor);
return Color.argb(a,
Math.min(r, 255),
Math.min(g, 255),
Math.min(b, 255));
}
public static void invalidateView(final DrawableSpan d, final View v) {
View view = (View) v.getParent();
if (view instanceof ViewGroup) {
((ViewGroup)view).setClipChildren(false);
}
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
v.post(new Runnable(){
@Override
public void run() {
v.setBackground(d.invalidate(v.getMeasuredWidth(), v.getMeasuredHeight()));
}
});
}
}
创建方法:
DrawableSpan(宽,高) –这里的宽高指初始化背景大小,后面可重新设置大小 DrawableSpan(图形样式Style,宽,高) DrawableSpan(使用旧DrawableSpan参数,图形样式Style,阴影样式Model) DrawableSpan(使用旧DrawableSpan参数,阴影样式Model) DrawableSpan(使用旧DrawableSpan参数)
自定义方法void:
setContainerdeltaLength(X轴阴影范围,Y轴阴影范围)–单位dp setContainerdeltaLength(阴影范围)–也就是x和y一样 –可以通过它改变阴影范围实现UI动态互交
setStyle(DrawableSpan.Style)–背景样式 DrawableSpan.Style.RECTANGLE 矩形 DrawableSpan.Style.CIRCLE 圆形
setModel(DrawableSpan.Model)–阴影样式 DrawableSpan.Model.FLAT 中间平 DrawableSpan.Model.CONCAVE 中间凹 DrawableSpan.Model.CONVEX 中间凸 DrawableSpan.Model.PRESSED 合并 平和凹
setColor(Color)–背景色0x setColor(Color,Color)–背景色,阴影色 –如白色不要直接0xffffffff这样阴影是显示不出来的 需要比0xffffffff暗一点即可,黑色同理
setRound(float[])–圆角(数组) –设置1~8个角 –参数{10} 所有角都圆角10 –参数{10,0,10,0} 左上,右上,右下,左下 –参数{10,10,10,10,10,10,10,10} 参考path.addRoundRect
setDraw(DrawableSpan.Draw) –设置画布回调,可在画布继续绘制
invalidate()–直接刷新画布 invalidate(宽,高)–更新画布宽高 上面那些set方法都不会主动更新画布 需要调用此方法更新 也可在id.setBackground(invalidate())
简单调用,简写方法
DrawableSpan.invalidateView(DrawableSpan,View) 第一个参数为创建好的DrawableSpan 第二参数为需要设置背景的view
需要注意:
—1.禁用硬件加速(必须) —2.根布局设置ClipChildren=false(必须);
如本体设置禁用硬件加速会导致ClipChildren失效 所以把禁用硬件加速设置到它的父布局
注意: 如view重叠后阴影是不生效的 两个必须的条件要满足 如果需要叠加要在它的后面放个父布局(给它弄个爹) 这个父布局(爹)的宽高一定要大于阴影辐射范围
暂时以此为准
MainActivity
package com.android.view;
import android.app.Activity;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.Log;
import android.widget.LinearLayout;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.graphics.Paint;
import android.widget.Toast;
import android.view.MotionEvent;
import android.animation.ValueAnimator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.animation.Animator;
public class MainActivity extends Activity {
private int current;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout view = findViewById(R.id.view);
LinearLayout view1 = findViewById(R.id.view1);
TextView text = findViewById(R.id.text);
TextView text1 = findViewById(R.id.text1);
TextView text2 = findViewById(R.id.text2);
TextView text3 = findViewById(R.id.text3);
TextView text4 = findViewById(R.id.text4);
DrawableSpan drawable = new DrawableSpan(DrawableSpan.Style.RECTANGLE, 30, 30);
drawable.setRound(new float[]{30});
drawable.setModel(DrawableSpan.Model.FLAT);
drawable.setColor(0xFFECECEC);
drawable.setContainerdeltaLength(20);
DrawableSpan.invalidateView(drawable, view);
final DrawableSpan drawable1 = new DrawableSpan(drawable, DrawableSpan.Style.CIRCLE, DrawableSpan.Model.FLAT);
DrawableSpan.invalidateView(drawable1, text);
DrawableSpan drawable2 = new DrawableSpan(drawable, DrawableSpan.Model.FLAT);
DrawableSpan drawable3 = new DrawableSpan(drawable, DrawableSpan.Model.CONCAVE);
DrawableSpan drawable4 = new DrawableSpan(drawable, DrawableSpan.Model.CONVEX);
DrawableSpan drawable5 = new DrawableSpan(drawable, DrawableSpan.Model.PRESSED);
DrawableSpan.invalidateView(drawable2, text1);
DrawableSpan.invalidateView(drawable3, text2);
DrawableSpan.invalidateView(drawable4, text3);
DrawableSpan.invalidateView(drawable5, text4);
final DrawableSpan.Model[] mode = new DrawableSpan.Model[]{
DrawableSpan.Model.FLAT,
DrawableSpan.Model.CONCAVE,
DrawableSpan.Model.CONVEX,
DrawableSpan.Model.PRESSED
};
current = 0;
text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
current = current < 3 ? current + 1 : 0;
drawable1.setModel(mode[current]);
drawable1.invalidate();
Toast.makeText(MainActivity.this, "当前" + drawable1.getModel().toString(), Toast.LENGTH_SHORT).show();
}
});
final DrawableSpan drawable6 = new DrawableSpan(drawable, DrawableSpan.Model.FLAT);
DrawableSpan.invalidateView(drawable6, view1);
final int[] length = drawable6.getContainerdeltaLength();
final ValueAnimator valueAnimator = ValueAnimator.ofInt(length[0], 0);
valueAnimator.setDuration(600);
valueAnimator.setInterpolator(new AccelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = animation.getAnimatedValue();
drawable6.setContainerdeltaLength(value);
drawable6.invalidate();
}
});
final ValueAnimator valueAnimator1 = ValueAnimator.ofInt(0, length[0]);
valueAnimator1.setDuration(300);
valueAnimator1.setInterpolator(new AccelerateInterpolator());
valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = animation.getAnimatedValue();
drawable6.setContainerdeltaLength(value);
drawable6.invalidate();
}
});
valueAnimator.addListener(new Animator.AnimatorListener(){
@Override
public void onAnimationStart(Animator p1) {
}
@Override
public void onAnimationEnd(Animator p1) {
valueAnimator1.start();
}
@Override
public void onAnimationCancel(Animator p1) {
}
@Override
public void onAnimationRepeat(Animator p1) {
}
});
view1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
valueAnimator.start();
}
});
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:clipChildren="false"
android:background="#FFECECEC"
android:orientation="vertical">
<LinearLayout
android:layout_height="230dp"
android:layout_width="230dp"
android:orientation="vertical"
android:id="@+id/view"
android:gravity="center">
<LinearLayout
android:layout_height="190dp"
android:layout_width="190dp"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="150dp"
android:layout_height="150dp"
android:text="新拟态"
android:id="@+id/text"
android:gravity="center"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_height="130dp"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_marginTop="30dp"
android:gravity="center">
<TextView
android:layout_height="60dp"
android:layout_width="60dp"
android:text="Flat"
android:gravity="center"
android:id="@+id/text1"/>
<TextView
android:layout_height="60dp"
android:layout_width="60dp"
android:text="Concave"
android:gravity="center"
android:layout_marginLeft="25dp"
android:id="@+id/text2"/>
<TextView
android:layout_height="60dp"
android:layout_width="60dp"
android:text="Convex"
android:gravity="center"
android:layout_marginLeft="25dp"
android:id="@+id/text3"/>
<TextView
android:layout_height="60dp"
android:layout_width="60dp"
android:text="Pressed"
android:gravity="center"
android:layout_marginLeft="25dp"
android:id="@+id/text4"/>
</LinearLayout>
<LinearLayout
android:layout_height="50dp"
android:layout_width="100dp"
android:orientation="vertical"
android:layout_marginTop="20dp"
android:id="@+id/view1"/>
</LinearLayout>
|