Android原生实现控件圆角方案(API28及以上)

2023-10-09 22:45

本文主要是介绍Android原生实现控件圆角方案(API28及以上),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android控件的圆角效果的实现方式有很多种,这里介绍一下另一种Android原生的圆角实现方案(API28及以上)。

我们利用ShapeAppearanceModel、MaterialShapeDrawable来实现一个圆角/切角的Button。

实现效果如下图
在这里插入图片描述
我们在为控件添加阴影的基础上实现为控件添加shape的功能.

属性

增加自定义属性:

        <attr name="carbon_cornerRadiusTopStart" /><attr name="carbon_cornerRadiusTopEnd" /><attr name="carbon_cornerRadiusBottomStart" /><attr name="carbon_cornerRadiusBottomEnd" /><attr name="carbon_cornerRadius" /><attr name="carbon_cornerCutTopStart" /><attr name="carbon_cornerCutTopEnd" /><attr name="carbon_cornerCutBottomStart" /><attr name="carbon_cornerCutBottomEnd" /><attr name="carbon_cornerCut" />

创建ShapeModelView接口

创建通用的shape接口

public interface ShapeModelView {void setShapeModel(ShapeAppearanceModel shapeModel);ShapeAppearanceModel getShapeModel();void setCornerCut(float cornerCut);void setCornerRadius(float cornerRadius);
}

为ShapeButton 实现ShapeModeView接口:

public class ShapeButton extends AppCompatButton implements ShadowView, ShapeModelView {
//.....private static int[] elevationIds = new int[]{R.styleable.shape_button_carbon_elevation,R.styleable.shape_button_carbon_elevationShadowColor,R.styleable.shape_button_carbon_elevationAmbientShadowColor,R.styleable.shape_button_carbon_elevationSpotShadowColor};private static int[] cornerCutRadiusIds = new int[]{R.styleable.shape_button_carbon_cornerRadiusTopStart,R.styleable.shape_button_carbon_cornerRadiusTopEnd,R.styleable.shape_button_carbon_cornerRadiusBottomStart,R.styleable.shape_button_carbon_cornerRadiusBottomEnd,R.styleable.shape_button_carbon_cornerRadius,R.styleable.shape_button_carbon_cornerCutTopStart,R.styleable.shape_button_carbon_cornerCutTopEnd,R.styleable.shape_button_carbon_cornerCutBottomStart,R.styleable.shape_button_carbon_cornerCutBottomEnd,R.styleable.shape_button_carbon_cornerCut};private void initButton(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.shape_button, defStyleAttr, defStyleRes);// 初始化阴影相关属性Carbon.initElevation(this, a, elevationIds);// 初始化圆角相关属性Carbon.initCornerCutRadius(this,a,cornerCutRadiusIds);a.recycle();}// -------------------------------// shadow// -------------------------------.....// -------------------------------// shape// -------------------------------private ShapeAppearanceModel shapeModel = new ShapeAppearanceModel();private MaterialShapeDrawable shadowDrawable = new MaterialShapeDrawable(shapeModel);@Overridepublic void setShapeModel(ShapeAppearanceModel shapeModel) {this.shapeModel = shapeModel;shadowDrawable = new MaterialShapeDrawable(shapeModel);if (getWidth() > 0 && getHeight() > 0)updateCorners();if (!Carbon.IS_LOLLIPOP_OR_HIGHER)postInvalidate();}// View的轮廓形状private RectF boundsRect = new RectF();// View的轮廓形状形成的Path路径private Path cornersMask = new Path();/*** 更新圆角*/private void updateCorners() {if (Carbon.IS_LOLLIPOP_OR_HIGHER) {// 如果不是矩形,裁剪View的轮廓if (!Carbon.isShapeRect(shapeModel, boundsRect)){setClipToOutline(true);}//该方法返回一个Outline对象,它描述了该视图的形状。setOutlineProvider(new ViewOutlineProvider() {@Overridepublic void getOutline(View view, Outline outline) {if (Carbon.isShapeRect(shapeModel, boundsRect)) {outline.setRect(0, 0, getWidth(), getHeight());} else {shadowDrawable.setBounds(0, 0, getWidth(), getHeight());shadowDrawable.setShadowCompatibilityMode(MaterialShapeDrawable.SHADOW_COMPAT_MODE_NEVER);shadowDrawable.getOutline(outline);}}});}// 拿到圆角矩形的形状boundsRect.set(shadowDrawable.getBounds());// 拿到圆角矩形的PathshadowDrawable.getPathForSize(getWidth(), getHeight(), cornersMask);}@Overridepublic ShapeAppearanceModel getShapeModel() {return this.shapeModel;}@Overridepublic void setCornerCut(float cornerCut) {shapeModel = ShapeAppearanceModel.builder().setAllCorners(new CutCornerTreatment(cornerCut)).build();setShapeModel(shapeModel);}@Overridepublic void setCornerRadius(float cornerRadius) {shapeModel = ShapeAppearanceModel.builder().setAllCorners(new RoundedCornerTreatment(cornerRadius)).build();setShapeModel(shapeModel);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);if (!changed)return;if (getWidth() == 0 || getHeight() == 0)return;updateCorners();}}

绘制轮廓

@Overridepublic void draw(Canvas canvas) {boolean c = !Carbon.isShapeRect(shapeModel, boundsRect);if (Carbon.IS_PIE_OR_HIGHER) {if (spotShadowColor != null)super.setOutlineSpotShadowColor(spotShadowColor.getColorForState(getDrawableState(), spotShadowColor.getDefaultColor()));if (ambientShadowColor != null)super.setOutlineAmbientShadowColor(ambientShadowColor.getColorForState(getDrawableState(), ambientShadowColor.getDefaultColor()));}// 判断如果不是圆角矩形,需要使用轮廓Path,重新绘制一下Path,不然显示会很奇怪if (getWidth() > 0 && getHeight() > 0 && ((c && !Carbon.IS_LOLLIPOP_OR_HIGHER) || !shapeModel.isRoundRect(boundsRect))) {int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);super.draw(canvas);paint.setXfermode(Carbon.CLEAR_MODE);if (c) {cornersMask.setFillType(Path.FillType.INVERSE_WINDING);canvas.drawPath(cornersMask, paint);}canvas.restoreToCount(saveCount);paint.setXfermode(null);}else{super.draw(canvas);}}

在Carbon中添加方法:

public static void initCornerCutRadius(ShapeModelView shapeModelView, TypedArray a, int[] ids) {int carbon_cornerRadiusTopStart = ids[0];int carbon_cornerRadiusTopEnd = ids[1];int carbon_cornerRadiusBottomStart = ids[2];int carbon_cornerRadiusBottomEnd = ids[3];int carbon_cornerRadius = ids[4];int carbon_cornerCutTopStart = ids[5];int carbon_cornerCutTopEnd = ids[6];int carbon_cornerCutBottomStart = ids[7];int carbon_cornerCutBottomEnd = ids[8];int carbon_cornerCut = ids[9];float cornerRadius = Math.max(a.getDimension(carbon_cornerRadius, 0), 0.1f);float cornerRadiusTopStart = a.getDimension(carbon_cornerRadiusTopStart, cornerRadius);float cornerRadiusTopEnd = a.getDimension(carbon_cornerRadiusTopEnd, cornerRadius);float cornerRadiusBottomStart = a.getDimension(carbon_cornerRadiusBottomStart, cornerRadius);float cornerRadiusBottomEnd = a.getDimension(carbon_cornerRadiusBottomEnd, cornerRadius);float cornerCut = a.getDimension(carbon_cornerCut, 0);float cornerCutTopStart = a.getDimension(carbon_cornerCutTopStart, cornerCut);float cornerCutTopEnd = a.getDimension(carbon_cornerCutTopEnd, cornerCut);float cornerCutBottomStart = a.getDimension(carbon_cornerCutBottomStart, cornerCut);float cornerCutBottomEnd = a.getDimension(carbon_cornerCutBottomEnd, cornerCut);ShapeAppearanceModel model = ShapeAppearanceModel.builder().setTopLeftCorner(cornerCutTopStart >= cornerRadiusTopStart ? new CutCornerTreatment(cornerCutTopStart) : new RoundedCornerTreatment(cornerRadiusTopStart)).setTopRightCorner(cornerCutTopEnd >= cornerRadiusTopEnd ? new CutCornerTreatment(cornerCutTopEnd) : new RoundedCornerTreatment(cornerRadiusTopEnd)).setBottomLeftCorner(cornerCutBottomStart >= cornerRadiusBottomStart ? new CutCornerTreatment(cornerCutBottomStart) : new RoundedCornerTreatment(cornerRadiusBottomStart)).setBottomRightCorner(cornerCutBottomEnd >= cornerRadiusBottomEnd ? new CutCornerTreatment(cornerCutBottomEnd) : new RoundedCornerTreatment(cornerRadiusBottomEnd)).build();shapeModelView.setShapeModel(model);}public static boolean isShapeRect(ShapeAppearanceModel model, RectF bounds) {return model.getTopLeftCornerSize().getCornerSize(bounds) <= 0.2f &&model.getTopRightCornerSize().getCornerSize(bounds) <= 0.2f &&model.getBottomLeftCornerSize().getCornerSize(bounds) <= 0.2f &&model.getBottomRightCornerSize().getCornerSize(bounds) <= 0.2f;}

使用

        <com.chinatsp.demo1.shadow.ShapeButtonandroid:id="@+id/show_dialog_btn"android:layout_width="56dp"android:layout_height="56dp"android:layout_margin="@dimen/carbon_padding"android:background="#ffffff"android:stateListAnimator="@null"android:text="TOM"app:carbon_cornerRadius="10dp"android:elevation="2dp"app:carbon_elevationShadowColor="@color/carbon_red_700" /><com.chinatsp.demo1.shadow.ShapeButtonandroid:id="@+id/show_date_dialog_btn"android:layout_width="56dp"android:layout_height="56dp"android:layout_margin="@dimen/carbon_padding"android:background="#ffffff"android:stateListAnimator="@null"android:text="TOM"app:carbon_cornerCut="10dp"android:elevation="10dp"app:carbon_elevationShadowColor="@color/carbon_grey_700" />

完整代码:


public class ShapeButton extends AppCompatButton implements ShadowView, ShapeModelView {public ShapeButton(@NonNull Context context) {super(context);initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);}public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);initButton(attrs, android.R.attr.buttonStyle, R.style.carbon_Button);}public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initButton(attrs, defStyleAttr, R.style.carbon_Button);}public ShapeButton(Context context, String text, OnClickListener listener) {super(context);initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);setText(text);setOnClickListener(listener);}private static int[] elevationIds = new int[]{R.styleable.shape_button_carbon_elevation,R.styleable.shape_button_carbon_elevationShadowColor,R.styleable.shape_button_carbon_elevationAmbientShadowColor,R.styleable.shape_button_carbon_elevationSpotShadowColor};private static int[] cornerCutRadiusIds = new int[]{R.styleable.shape_button_carbon_cornerRadiusTopStart,R.styleable.shape_button_carbon_cornerRadiusTopEnd,R.styleable.shape_button_carbon_cornerRadiusBottomStart,R.styleable.shape_button_carbon_cornerRadiusBottomEnd,R.styleable.shape_button_carbon_cornerRadius,R.styleable.shape_button_carbon_cornerCutTopStart,R.styleable.shape_button_carbon_cornerCutTopEnd,R.styleable.shape_button_carbon_cornerCutBottomStart,R.styleable.shape_button_carbon_cornerCutBottomEnd,R.styleable.shape_button_carbon_cornerCut};protected TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);private void initButton(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.shape_button, defStyleAttr, defStyleRes);Carbon.initElevation(this, a, elevationIds);Carbon.initCornerCutRadius(this,a,cornerCutRadiusIds);a.recycle();}// -------------------------------// shadow// -------------------------------private float elevation = 0;private float translationZ = 0;private ColorStateList ambientShadowColor, spotShadowColor;@Overridepublic float getElevation() {return elevation;}@Overridepublic void setElevation(float elevation) {if (Carbon.IS_PIE_OR_HIGHER) {super.setElevation(elevation);super.setTranslationZ(translationZ);} else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {if (ambientShadowColor == null || spotShadowColor == null) {super.setElevation(elevation);super.setTranslationZ(translationZ);} else {super.setElevation(0);super.setTranslationZ(0);}} else if (elevation != this.elevation && getParent() != null) {((View) getParent()).postInvalidate();}this.elevation = elevation;}@Overridepublic float getTranslationZ() {return translationZ;}public void setTranslationZ(float translationZ) {if (translationZ == this.translationZ)return;if (Carbon.IS_PIE_OR_HIGHER) {super.setTranslationZ(translationZ);} else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {if (ambientShadowColor == null || spotShadowColor == null) {super.setTranslationZ(translationZ);} else {super.setTranslationZ(0);}} else if (translationZ != this.translationZ && getParent() != null) {((View) getParent()).postInvalidate();}this.translationZ = translationZ;}@Overridepublic ColorStateList getElevationShadowColor() {return ambientShadowColor;}@Overridepublic void setElevationShadowColor(ColorStateList shadowColor) {ambientShadowColor = spotShadowColor = shadowColor;setElevation(elevation);setTranslationZ(translationZ);}@Overridepublic void setElevationShadowColor(int color) {ambientShadowColor = spotShadowColor = ColorStateList.valueOf(color);setElevation(elevation);setTranslationZ(translationZ);}@Overridepublic void setOutlineAmbientShadowColor(ColorStateList color) {ambientShadowColor = color;if (Carbon.IS_PIE_OR_HIGHER) {super.setOutlineAmbientShadowColor(color.getColorForState(getDrawableState(), color.getDefaultColor()));} else {setElevation(elevation);setTranslationZ(translationZ);}}@Overridepublic void setOutlineAmbientShadowColor(int color) {setOutlineAmbientShadowColor(ColorStateList.valueOf(color));}@Overridepublic int getOutlineAmbientShadowColor() {return ambientShadowColor.getDefaultColor();}@Overridepublic void setOutlineSpotShadowColor(int color) {setOutlineSpotShadowColor(ColorStateList.valueOf(color));}@Overridepublic void setOutlineSpotShadowColor(ColorStateList color) {spotShadowColor = color;if (Carbon.IS_PIE_OR_HIGHER) {super.setOutlineSpotShadowColor(color.getColorForState(getDrawableState(), color.getDefaultColor()));} else {setElevation(elevation);setTranslationZ(translationZ);}}@Overridepublic int getOutlineSpotShadowColor() {return ambientShadowColor.getDefaultColor();}@Overridepublic void draw(Canvas canvas) {boolean c = !Carbon.isShapeRect(shapeModel, boundsRect);if (Carbon.IS_PIE_OR_HIGHER) {if (spotShadowColor != null)super.setOutlineSpotShadowColor(spotShadowColor.getColorForState(getDrawableState(), spotShadowColor.getDefaultColor()));if (ambientShadowColor != null)super.setOutlineAmbientShadowColor(ambientShadowColor.getColorForState(getDrawableState(), ambientShadowColor.getDefaultColor()));}// 判断如果不是圆角矩形,需要使用轮廓Path,绘制一下Path,不然显示会很奇怪if (getWidth() > 0 && getHeight() > 0 && ((c && !Carbon.IS_LOLLIPOP_OR_HIGHER) || !shapeModel.isRoundRect(boundsRect))) {int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);super.draw(canvas);paint.setXfermode(Carbon.CLEAR_MODE);if (c) {cornersMask.setFillType(Path.FillType.INVERSE_WINDING);canvas.drawPath(cornersMask, paint);}canvas.restoreToCount(saveCount);paint.setXfermode(null);}else{super.draw(canvas);}}// -------------------------------// shape// -------------------------------private ShapeAppearanceModel shapeModel = new ShapeAppearanceModel();private MaterialShapeDrawable shadowDrawable = new MaterialShapeDrawable(shapeModel);@Overridepublic void setShapeModel(ShapeAppearanceModel shapeModel) {this.shapeModel = shapeModel;shadowDrawable = new MaterialShapeDrawable(shapeModel);if (getWidth() > 0 && getHeight() > 0)updateCorners();if (!Carbon.IS_LOLLIPOP_OR_HIGHER)postInvalidate();}// View的轮廓形状private RectF boundsRect = new RectF();// View的轮廓形状形成的Path路径private Path cornersMask = new Path();/*** 更新圆角*/private void updateCorners() {if (Carbon.IS_LOLLIPOP_OR_HIGHER) {// 如果不是矩形,裁剪View的轮廓if (!Carbon.isShapeRect(shapeModel, boundsRect)){setClipToOutline(true);}//该方法返回一个Outline对象,它描述了该视图的形状。setOutlineProvider(new ViewOutlineProvider() {@Overridepublic void getOutline(View view, Outline outline) {if (Carbon.isShapeRect(shapeModel, boundsRect)) {outline.setRect(0, 0, getWidth(), getHeight());} else {shadowDrawable.setBounds(0, 0, getWidth(), getHeight());shadowDrawable.setShadowCompatibilityMode(MaterialShapeDrawable.SHADOW_COMPAT_MODE_NEVER);shadowDrawable.getOutline(outline);}}});}// 拿到圆角矩形的形状boundsRect.set(shadowDrawable.getBounds());// 拿到圆角矩形的PathshadowDrawable.getPathForSize(getWidth(), getHeight(), cornersMask);}@Overridepublic ShapeAppearanceModel getShapeModel() {return this.shapeModel;}@Overridepublic void setCornerCut(float cornerCut) {shapeModel = ShapeAppearanceModel.builder().setAllCorners(new CutCornerTreatment(cornerCut)).build();setShapeModel(shapeModel);}@Overridepublic void setCornerRadius(float cornerRadius) {shapeModel = ShapeAppearanceModel.builder().setAllCorners(new RoundedCornerTreatment(cornerRadius)).build();setShapeModel(shapeModel);}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);if (!changed)return;if (getWidth() == 0 || getHeight() == 0)return;updateCorners();}
}

这篇关于Android原生实现控件圆角方案(API28及以上)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到