本文主要是介绍安卓java新拟态风格UI实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
继承drawable方式实现
如果有问题请多多指教
新2.0源码已更新,文章后面下载
文章最后由我提到的硬件加速问题,如不进行动态更新画布可绘制后转bitmap或在bitmap上绘制即可。(注意bitmap宽高,阴影是超出原有view大小绘制的)
更新了2.0版本在文章最后
更多玩法
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();}@Overridepublic 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;}@Overridepublic void setAlpha(int i) {paint.setAlpha(i);paint_right.setAlpha(i);paint_bottom.setAlpha(i);}@Overridepublic 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; }@Overridepublic 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);/**禁用硬件加速(必须);根布局设置ClipChildren=false(必须);如本体设置禁用硬件加速会导致ClipChildren失效所以把禁用硬件加速设置到它的父布局注意:如view重叠后阴影是不生效的两个必须的条件要满足如果需要叠加需要在它的后面放个父布局这个父布局的宽高一定要大于阴影辐射范围暂时以此为准**/v.post(new Runnable(){@Overridepublic 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轴阴影范围)
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;@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {int value = animation.getAnimatedValue();drawable6.setContainerdeltaLength(value);drawable6.invalidate();}});valueAnimator.addListener(new Animator.AnimatorListener(){@Overridepublic void onAnimationStart(Animator p1) {}@Overridepublic void onAnimationEnd(Animator p1) {valueAnimator1.start();}@Overridepublic void onAnimationCancel(Animator p1) {}@Overridepublic void onAnimationRepeat(Animator p1) {}});view1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {valueAnimator.start();}});}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns: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"><LinearLayoutandroid:layout_height="230dp"android:layout_width="230dp"android:orientation="vertical"android:id="@+id/view"android:gravity="center"><LinearLayoutandroid:layout_height="190dp"android:layout_width="190dp"android:orientation="vertical"android:gravity="center"><TextViewandroid:layout_width="150dp"android:layout_height="150dp"android:text="新拟态"android:id="@+id/text"android:gravity="center"/></LinearLayout></LinearLayout><LinearLayoutandroid:layout_height="130dp"android:layout_width="match_parent"android:orientation="horizontal"android:layout_marginTop="30dp"android:gravity="center"><TextViewandroid:layout_height="60dp"android:layout_width="60dp"android:text="Flat"android:gravity="center"android:id="@+id/text1"/><TextViewandroid:layout_height="60dp"android:layout_width="60dp"android:text="Concave"android:gravity="center"android:layout_marginLeft="25dp"android:id="@+id/text2"/><TextViewandroid:layout_height="60dp"android:layout_width="60dp"android:text="Convex"android:gravity="center"android:layout_marginLeft="25dp"android:id="@+id/text3"/><TextViewandroid:layout_height="60dp"android:layout_width="60dp"android:text="Pressed"android:gravity="center"android:layout_marginLeft="25dp"android:id="@+id/text4"/></LinearLayout><LinearLayoutandroid:layout_height="50dp"android:layout_width="100dp"android:orientation="vertical"android:layout_marginTop="20dp"android:id="@+id/view1"/></LinearLayout>
看完了不点赞关注?
捐赠:
捐赠后持续更新
支付宝:
点击跳转支付宝即可捐赠
新2.0已更新 源码蓝奏云下载
https://wwp.lanzoup.com/iKVLH02md0tc
1.自定义图形样式
2.图形叠加(多层阴影绘制)
…
更多功能
这篇关于安卓java新拟态风格UI实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!