安卓java新拟态风格UI实现

2023-10-25 07:59
文章标签 java 实现 ui 风格 安卓 拟态

本文主要是介绍安卓java新拟态风格UI实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

继承drawable方式实现

如果有问题请多多指教

新2.0源码已更新,文章后面下载

文章最后由我提到的硬件加速问题,如不进行动态更新画布可绘制后转bitmap或在bitmap上绘制即可。(注意bitmap宽高,阴影是超出原有view大小绘制的)

更新了2.0版本在文章最后
更多玩法

Flat
Concave
Convex
Pressed
整体
点击UI互交

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实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/281125

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu