轻量级的安卓百分比屏幕适配方案SimpleAutoSize

news/2024/11/20 1:22:39/

AutoSize是基于今日头条的适配方案,但它有一些缺点,比如代码侵入性较强,在使用第三方的View框架时,可能会出现不兼容的情况。

我目前的sdk项目不能使用这样的框架,于是自己做了一个简单的工具类,也能够满足基本需求。

适配方案

以设计图的960 x 540 dp作为基准,在代码中获取屏幕宽高px,遇到需要适配的view时,调用autoSizeLayout()方法自动调整。

  1. autoSizeLayout方法会遍历viewGroup下的所有根节点,获取当前view的内外边距的px

  2. px除以屏幕密度得到当前view在布局文件中设置的dp值

  3. 使用该dp值根据横向或纵向来除以960或540,得出其在设计图中所占比例

  4. 比例乘以当前屏幕的宽高px,即为应该设置的新的px值

优点

  • 代码中只需封装一次,以后布局需要支持的话,都调用autoSizeLayout()方法进行处理即可

  • xml中的dp值可以完全以设计图为准,具体值会在代码中进行计算

  • 集成该框架无需改动现有项目的代码

缺点:

  • xml中的shape资源无法适配,需要手动在代码中处理

public class SimpleAutoSize {/*** 屏幕宽高px*/private int screenWidth = 0;private int screenHeight = 0;/*** 设计图基准dp*/private int basisWidthDp = 0;private int basisHeightDp = 0;private float density = 0;private onAutoSizeViewListener onAutoSizeViewListener;/*** 可以直接使用View的宽高px值,乘以该比例,得出相对于960*540屏幕的百分比宽高* <p>* 计算步骤:* 1、布局中的16dp,跑到了1080*1920的屏幕上,成为了24px* 2、先24除以屏幕密度1.5,得到布局文件中的dp值16* 3、计算16占540的比重* 4、结果乘以1080,得到32px,所以16dp在当前屏幕上,按照百分比计算应该是32px*/private float widthRatio = 0;private float heightRatio = 0;private float screenRatio = 0;private static class SingletonHolder {private static final SimpleAutoSize instance = new SimpleAutoSize();}public static SimpleAutoSize getInstance() {return SingletonHolder.instance;}public interface onAutoSizeViewListener {public void onAutoSize(View view, float screenRatio);}public void init(Context context, int basisWidthDp, int basisHeightDp) {this.basisWidthDp = basisWidthDp;this.basisHeightDp = basisHeightDp;calc(context);}private void calc(Context context){WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = windowManager.getDefaultDisplay();Point screenSize = new Point();display.getSize(screenSize);screenWidth = screenSize.x;screenHeight = screenSize.y;this.density = context.getResources().getDisplayMetrics().density;widthRatio = (float) screenWidth / basisWidthDp / density; // 再除以屏幕密度,方便后续一步求出结果heightRatio = (float) screenHeight / basisHeightDp / density;screenRatio = (float) screenWidth / basisWidthDp;}public SimpleAutoSize.onAutoSizeViewListener getOnAutoSizeViewListener() {return onAutoSizeViewListener;}public void setOnAutoSizeViewListener(SimpleAutoSize.onAutoSizeViewListener onAutoSizeViewListener) {this.onAutoSizeViewListener = onAutoSizeViewListener;}public int getScreenWidth() {return screenWidth;}public int getScreenHeight() {return screenHeight;}public int getBasisWidthDp() {return basisWidthDp;}public int getBasisHeightDp() {return basisHeightDp;}public float getDensity() {return density;}public float getWidthRatio() {return widthRatio;}public float getHeightRatio() {return heightRatio;}public boolean hasChange(Context context){WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);Display display = windowManager.getDefaultDisplay();Point screenSize = new Point();display.getSize(screenSize);if (screenSize.x != screenWidth || screenSize.y != screenHeight){return true;}if (density != context.getResources().getDisplayMetrics().density){return true;}return false;}public void autoSizeLayout(ViewGroup viewGroup) {autoSizeLayout(viewGroup, null);}/*** 按照比例自动调整布局,使控件在不同密度下显示一致** @param viewGroup*/public void autoSizeLayout(ViewGroup viewGroup, onAutoSizeViewListener onAutoSizeViewListener) {// 判断屏幕分辨率,和density是否发生变化,如果发生变化,则重新计算if (hasChange(viewGroup.getContext())) {calc(viewGroup.getContext());}int childCount = viewGroup.getChildCount();for (int i = 0; i < childCount; i++) {View childAt = viewGroup.getChildAt(i);if (onAutoSizeViewListener != null) {onAutoSizeViewListener.onAutoSize(childAt, screenRatio);}ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) childAt.getLayoutParams();
//                Log.i("XmlBaseAdView", "处理前的Margin: " + layoutParams.topMargin + " " + layoutParams.bottomMargin + " " + layoutParams.leftMargin + " " + layoutParams.rightMargin);layoutParams.topMargin = (int) (layoutParams.topMargin * heightRatio);layoutParams.bottomMargin = (int) (layoutParams.bottomMargin * heightRatio);layoutParams.leftMargin = (int) (layoutParams.leftMargin * widthRatio);layoutParams.rightMargin = (int) (layoutParams.rightMargin * widthRatio);
//                Log.i("XmlBaseAdView", "处理后的Margin: " + layoutParams.topMargin + " " + layoutParams.bottomMargin + " " + layoutParams.leftMargin + " " + layoutParams.rightMargin);childAt.setLayoutParams(layoutParams);//            Log.i("XmlBaseAdView", "处理前的Padding: " + childAt.getPaddingTop() + " " + childAt.getPaddingBottom() + " " + childAt.getPaddingLeft() + " " + childAt.getPaddingRight());float padTop = childAt.getPaddingTop() * heightRatio;float padBottom = childAt.getPaddingBottom() * heightRatio;float padLeft = childAt.getPaddingLeft() * widthRatio;float padRight = childAt.getPaddingRight() * widthRatio;childAt.setPadding((int) padLeft, (int) padTop, (int) padRight, (int) padBottom);
//            Log.i("XmlBaseAdView", "处理后的Padding: " + childAt.getPaddingTop() + " " + childAt.getPaddingBottom() + " " + childAt.getPaddingLeft() + " " + childAt.getPaddingRight());if (childAt instanceof TextView) {// 如果是TextView,还需要处理字体大小TextView textView = (TextView) childAt;float textSize = textView.getTextSize();textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize /density * screenRatio);}if (childAt instanceof ViewGroup) {autoSizeLayout((ViewGroup) childAt);}}}

初始化:

非常简单,只需要一行代码

SimpleAutoSize.getInstance().init(context, 960, 540);

使用:

第一个参数传入要适配的view,一般是根view

        SimpleAutoSize.getInstance().autoSizeLayout(baseAdView, (view, screenRatio) -> {Resources resources = context.getResources();if (view.getId() == R.id.ll_countdown_tips) {view.setBackground(SimpleAutoSize.createRectangleDrawable(resources.getColor(R.color.textBg), resources.getDimension(R.dimen.dimen_15) /resources.getDisplayMetrics().density * screenRatio));}if (view.getId() == R.id.tv_show_advert_tag) {view.setBackground(SimpleAutoSize.createRectangleDrawable(resources.getColor(R.color.textBg), resources.getDimension(R.dimen.dimen_3) /resources.getDisplayMetrics().density * screenRatio));}});

autoSizeLayout第二个参数可传可不传,如果需要对view做一下特殊处理的话,可以传。

我这里是因为shape资源无法适配,所以在代码中进行处理。

大家如需使用的话可以把以下工具方法也添加进去。

    public static GradientDrawable createRectangleDrawable(int color, float radius) {return createRectangleDrawable(color, 0, 0, radius);}/*** 创建背景颜色** @param color       填充色* @param strokeColor 线条颜色* @param strokeWidth 线条宽度  单位px* @param radius      角度  px*/public static GradientDrawable createRectangleDrawable(int color, int strokeColor, int strokeWidth, float radius) {try {GradientDrawable radiusBg = new GradientDrawable();//设置Shape类型radiusBg.setShape(GradientDrawable.RECTANGLE);//设置填充颜色radiusBg.setColor(color);//设置线条粗心和颜色,pxradiusBg.setStroke(strokeWidth, strokeColor);//设置圆角角度,如果每个角度都一样,则使用此方法radiusBg.setCornerRadius(radius);return radiusBg;} catch (Exception e) {return new GradientDrawable();}}/*** 设置Selector。*/public static StateListDrawable newSelector(int focusColor, int normalColor, float radius) {StateListDrawable bg = new StateListDrawable();// View.PRESSED_ENABLED_STATE_SETbg.addState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}, createRectangleDrawable(focusColor, radius));// View.ENABLED_FOCUSED_STATE_SETbg.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, createRectangleDrawable(focusColor, radius));// View.ENABLED_STATE_SETbg.addState(new int[]{android.R.attr.state_enabled}, createRectangleDrawable(normalColor, radius));// View.FOCUSED_STATE_SETbg.addState(new int[]{android.R.attr.state_focused}, createRectangleDrawable(focusColor, radius));// View.WINDOW_FOCUSED_STATE_SETbg.addState(new int[]{android.R.attr.state_window_focused}, createRectangleDrawable(focusColor, radius));// View.EMPTY_STATE_SETbg.addState(new int[]{}, createRectangleDrawable(normalColor, radius));return bg;}public static GradientDrawable getBgDrawable() {return DrawableUtils.createRectangleDrawable(Color.argb(255, 67, 81, 115), 20);}


http://www.ppmy.cn/news/63473.html

相关文章

操作系统考试复习——第四章 对换 分页存储管理方式

对换技术也成为交换技术&#xff0c;由于当时计算机的内存都非常小&#xff0c;为了使该系统能分时运行多个用户程序而引入了对换技术。 1.对换的引入&#xff1a; 所谓“对换”&#xff0c;是指把内存中暂时不能运行的进程或者暂时不用的程序和数据调出到外存上&#xff0c;…

MATLAB 点云非均匀体素下采样 (8)

MATLAB 点云非均匀体素下采样的不同参数效果测试 (8) 一、实现效果二、算法介绍三、函数说明3.1 函数3.2 参数四、实现代码(详细注释!)五、与固定步长采样法比较5.1 代码5.2 效果一、实现效果 不同参数调整下的非均匀体素下采样结果如下图所示,后续代码复制黏贴即可: 可…

Android 自定义View 之 简易输入框

简易输入框 前言正文① 构造方法② XML样式③ 测量④ 绘制1. 绘制方框2. 绘制文字 ⑤ 输入1. 键盘布局2. 键盘接口3. 键盘弹窗4. 显示键盘5. 相关API 四、使用自定义View五、源码 前言 在日常工作开发中&#xff0c;我们时长会遇到各种各样的需求&#xff0c;不部分需求是可以通…

weblogic ssrf 漏洞复现

一.前言 Weblogic中存在一个SSRF漏洞&#xff0c;利用该漏洞可以发送任意HTTP请求&#xff0c;进而攻击内网中redis、fastcgi等脆弱组件。 二.环境搭建 在docker中开启环境 sudo docker-compose up -d sudo docker-compose ps #查看状态访问http://your-ip:7001/uddiexpl…

做SSM项目的步骤和优化

SSM框架整合 这里说的SSM整合&#xff0c;主要说的是Spring和mybatis之间的整合。因为spring和springMVC都是spring生态系统中的框架&#xff0c;所以spring和springMVC之间的整合是无缝的整合&#xff0c;即&#xff0c;我们在不知不觉中&#xff0c;其实spring和springMVC已…

AI数字人系统搭建源码

AI数字人系统的功能可以根据具体应用场景而定&#xff0c;以下是一些可能的功能&#xff1a; 语音识别和合成&#xff1a;将自然语言转换为机器可读的文本&#xff0c;或将机器生成的文本转换为自然语言的语音输出。 面部表情捕捉&#xff1a;利用摄像头等设备获取用户…

【Docker】使用 Docker 部署 Maven 仓库

在本文中&#xff0c;将介绍如何使用 Docker 部署一个 Maven 本地私服。Maven 私服可以帮助我们管理和共享本地的 Maven 依赖&#xff0c;提高开发效率。本文将使用 Sonatype Nexus 作为 Maven 私服&#xff0c;并使用 Docker Compose 来部署和管理容器。 准备工作 在开始之前…

<微服务架构项目>权限控制案例分享

权限控制总结四部分&#xff08;后端为前三部分&#xff0c;前端为第四部分&#xff09;&#xff1a; 1.根据当前登录人信息进行数据过滤&#xff1a;如&#xff1a;登录人工号等&#xff1b; 当前登录人信息一般从token中获取&#xff1b; 当前登录人信息可包括&#xff1a;域…