Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配AndroidAutoSize框架 1.链接https://github.com/JessYanCoding/AndroidAutoSize 2.使用 2.1.添加Gradle配置implementation’me.jessyan:autosize:1.1.2′  2.2.添加AndroidManifest配置<manifest>…

大家好,又见面了,我是你们的朋友全栈君。

AndroidAutoSize框架

 

1.链接

https://github.com/JessYanCoding/AndroidAutoSize

 

2.使用

 

2.1.添加Gradle配置

implementation 'me.jessyan:autosize:1.1.2'

 

 

2.2.添加AndroidManifest配置

<manifest>
    <application>    

        ...

        
        <meta-data
            android:name="design_width_in_dp"
            android:value="360"/>
        <meta-data
            android:name="design_height_in_dp"
            android:value="640"/>      

        ...

     
     </application>           
</manifest>

 

 

2.3.使用

 

2.3.1.Activity代码

public class AndroidAutoSizeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_androidautosize);
    }
}

 

2.3.2.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:orientation="vertical">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        
        <Button
            android:layout_width="180dp"
            android:layout_height="100dp"
            android:background="@color/colorAccent"
            android:text="stop"
            android:textColor="#fff"
            android:textSize="30sp" />

        <Button
            android:layout_width="180dp"
            android:layout_height="100dp"
            android:background="#0d0"
            android:text="restart"
            android:textColor="#fff"
            android:textSize="30sp" />

    </LinearLayout>

    <Button
        android:layout_width="360dp"
        android:layout_height="100dp"
        android:background="#fd0"
        android:text="CustomAdapt(internal)"
        android:textColor="#fff"
        android:textSize="25sp" />

    <Button
        android:layout_width="360dp"
        android:layout_height="100dp"
        android:background="#d00"
        android:text="CustomAdapt(external)"
        android:textColor="#fff"
        android:textSize="25sp" />


    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="250dp"
            android:layout_height="100dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:text="W-250dp(total:360dp)"
            android:textColor="#0d0"
            android:textSize="20sp" />

        <TextView
            android:layout_width="1dp"
            android:layout_height="100dp" />

        <TextView
            android:layout_width="109dp"
            android:layout_height="100dp"
            android:background="#d0a"
            android:gravity="center"
            android:text="W-109dp"
            android:textColor="#0d0"
            android:textSize="20sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <TextView
            android:layout_width="120dp"
            android:layout_height="match_parent"
            android:background="#aef"
            android:gravity="center"
            android:text="W-120dp"
            android:textColor="#0d0"
            android:textSize="20sp" />

        <TextView
            android:layout_width="90dp"
            android:layout_height="match_parent"
            android:background="#acf"
            android:gravity="center"
            android:text="W-90dp"
            android:textColor="#0d0"
            android:textSize="20sp" />

        <TextView
            android:layout_width="150dp"
            android:layout_height="match_parent"
            android:background="#fcf"
            android:gravity="center"
            android:text="W-150dp"
            android:textColor="#0d0"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

 

 

 

2.4.效果

 

2.4.1.1080×1920 480dpi 设备

 

适配前

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

适配后

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

2.4.2.480×800 240dpi 设备

 

适配前

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

适配后

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

2.4.3.720×1280 320dpi 设备

 

适配前

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

适配后

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

 

2.5.说明

 

design_width_in_dp 和 design_height_in_dp 的单位必须是 dp,如果设计师给你的设计图,只标注了 px 尺寸 ,那请自行根据公式  dp = px / (DPI / 160)  将 px 尺寸转换为 dp 尺寸(px 尺寸转换为 sp一样)。

 

你在 AndroidManifest.xml 中怎么把设计图的 px 尺寸转换为 dp 尺寸,那在布局时,每个控件的大小也需要以同样的方式将设计图上标注的 px 尺寸转换为 dp 尺寸,千万不要在 AndroidManifest.xml 中填写的是 dp 尺寸,却在布局中继续填写设计图上标注的 px 尺寸(px 尺寸转换为 sp一样)。

 

 

附:

 

链接1:https://www.jianshu.com/p/55e0fca23b4f

 

链接2:https://www.jianshu.com/p/2aded8bb6ede

 

链接3:https://www.jianshu.com/p/4aa23d69d481

 

 

 

 

 

 

附 :

 

头条官方文档详解

 

 

1.链接

https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

 

 

2.说明

 

在Android开发中,由于Android碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,适配成本越来越高。虽然Android官方提供了dp单位来适配,但其在各种奇怪分辨率下表现却不尽如人意,因此下面探索一种简单且低侵入的适配方式。

 

 

2.1.传统dp适配方式的缺点

android中的dp在渲染前会将dp转为px,计算公式

px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);

 

而dpi是根据屏幕真实的分辨率和尺寸来计算的,每个设备都可能不一样的。

 

屏幕尺寸、分辨率、像素密度三者关系

通常情况下,一部手机的分辨率是宽x高,屏幕大小是以寸为单位,那么三者的关系是:

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

举个例子:屏幕分辨率为:1080*1920,屏幕尺寸为5吋的话,那么dpi为440。

 

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

2.2.这样会存在什么问题呢

假设我们UI设计图是按屏幕宽度为360dp来设计的,那么在上述设备上,屏幕宽度其实为1080/(440/160)=392.7dp,也就是上述设备屏幕是比设计图要宽的。这种情况下, 即使使用dp也是无法在不同设备上显示为同样效果的。 同时还存在部分设备屏幕宽度不足360dp,这时就会导致按360dp宽度来开发实际显示不全的情况。

而且上述屏幕尺寸、分辨率和像素密度的关系,很多设备并没有按此规则来实现, 因此dpi的值非常乱,没有规律可循,从而导致使用dp适配效果差强人意。

 

 

2.3.探索新的适配方式

 

 

2.3.1.梳理需求

首先来梳理下我们的需求,一般我们设计图都是以固定的尺寸来设计的。比如以分辨率1920px * 1080px来设计,以density为3来标注,也就是屏幕其实是640dp * 360dp。如果我们想在所有设备上显示完全一致,其实是不现实的,因为屏幕高宽比不是固定的,16:9、4:3甚至其他宽高比层出不穷,宽高比不同,显示完全一致就不可能了。但是通常下,我们只需要以宽或高一个维度去适配,比如我们Feed是上下滑动的,只需要保证在所有设备中宽的维度上显示一致即可,再比如一个不支持上下滑动的页面,那么需要保证在高这个维度上都显示一致,尤其不能存在某些设备上显示不全的情况。同时考虑到现在基本都是以dp为单位去做的适配,如果新的方案不支持dp,那么迁移成本也非常高。

 

因此,总结下大致需求如下:

   1.支持以宽或者高一个维度去适配,保持该维度上和设计图一致。

   2.支持dp和sp单位,控制迁移成本到最小。

 

 

2.3.2.找兼容突破口

从dp和px的转换公式 :px = dp * density 

可以看出,如果设计图宽为360dp,想要保证在所有设备计算得出的px值都正好是屏幕宽度的话,我们只能修改 density 的值。

 

通过阅读源码,我们可以得知,density 是 DisplayMetrics 中的成员变量,而 DisplayMetrics 实例通过 Resources#getDisplayMetrics 可以获得,而Resouces通过Activity或者Application的Context获得。

 

先来熟悉下 DisplayMetrics 中和适配相关的几个变量:

DisplayMetrics#density 就是上述的density。

DisplayMetrics#densityDpi 就是上述的dpi。

DisplayMetrics#scaledDensity 字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值。

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

那么是不是所有的dp和px的转换都是通过 DisplayMetrics 中相关的值来计算的呢

 

 

2.4.布局文件中dp的转换

布局文件中dp的转换,最终都是调用 TypedValue#applyDimension(int unit, float value, DisplayMetrics metrics) 来进行转换:

 

    public static float applyDimension(int unit, float value, DisplayMetrics metrics) {
        switch (unit) {
            case COMPLEX_UNIT_PX:
                return value;
            case COMPLEX_UNIT_DIP:
                return value * metrics.density;
            case COMPLEX_UNIT_SP:
                return value * metrics.scaledDensity;
            case COMPLEX_UNIT_PT:
                return value * metrics.xdpi * (1.0f / 72);
            case COMPLEX_UNIT_IN:
                return value * metrics.xdpi;
            case COMPLEX_UNIT_MM:
                return value * metrics.xdpi * (1.0f / 25.4f);
        }
        return 0;
    }

这里用到的DisplayMetrics正是从Resources中获得的。

 

 

2.5.图片的decode

再看看图片的decode,BitmapFactory#decodeResourceStream方法:

    public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, BitmapFactory.Options opts) {
        validate(opts);
        if (opts == null) {
            opts = new BitmapFactory.Options();
        }

        if (opts.inDensity == 0 && value != null) {
            final int density = value.density;
            if (density == TypedValue.DENSITY_DEFAULT) {
                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            } else if (density != TypedValue.DENSITY_NONE) {
                opts.inDensity = density;
            }
        }

        if (opts.inTargetDensity == 0 && res != null) {
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }

        return decodeStream(is, pad, opts);
    }

可见也是通过 DisplayMetrics 中的值来计算的。

 

当然还有些其他dp转换的场景,基本都是通过 DisplayMetrics 来计算的,这里不再详述。因此,想要满足上述需求,我们只需要修改 DisplayMetrics 中和 dp 转换相关的变量即可。

 

 

2.6.最终方案

 

下面假设设计图宽度是360dp,以宽维度来适配。

那么适配后的 density = 设备真实宽(单位px) / 360,接下来只需要把我们计算好的 density 在系统中修改下即可,代码实现如下:

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

同时在 Activity#onCreate 方法中调用下。代码比较简单,也没有涉及到系统非公开api的调用,因此理论上不会影响app稳定性。

于是修改后上线灰度测试了一版,稳定性符合预期,没有收到由此带来的crash,但是收到了很多字体过小的反馈:

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

原因是在上面的适配中,我们忽略了DisplayMetrics#scaledDensity的特殊性,将DisplayMetrics#scaledDensity和DisplayMetrics#density设置为同样的值,从而某些用户在系统中修改了字体大小失效了,但是我们还不能直接用原始的scaledDensity,直接用的话可能导致某些文字超过显示区域,因此我们可以通过计算之前scaledDensity和density的比获得现在的scaledDensity,方式如下:

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

但是测试后发现另外一个问题,就是如果在系统设置中切换字体,再返回应用,字体并没有变化。于是还得监听下字体切换,调用 Application#registerComponentCallbacks 注册下 onConfigurationChanged 监听即可。

 

因此最终方案如下:

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

当然以上代码只是以设计图宽360dp去适配的,如果要以高维度适配,可以再扩展下代码即可。

 

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

 

3.使用代码

 

3.1.工具类

package wjn.com.imwithdemo.utils;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks;
import android.content.res.Configuration;
import android.util.DisplayMetrics;

public class MyDisplayMetrics {

    private static float sNoncompatDensity;
    private static float sNoncompatScaledDensity;

    public static void setCustomDensity(Activity activity, final Application application){
        final DisplayMetrics appDisplayMetrics=application.getResources().getDisplayMetrics();
        if(sNoncompatDensity==0){
            sNoncompatDensity=appDisplayMetrics.density;
            sNoncompatScaledDensity=appDisplayMetrics.scaledDensity;
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if(null!=newConfig&&newConfig.fontScale>0){
                        sNoncompatScaledDensity=application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }

        final float targetDensity=appDisplayMetrics.widthPixels/360f;
        final float targetScaledDensity=targetDensity*(sNoncompatScaledDensity/sNoncompatDensity);
        final int targetDensityDpi= (int) (160*targetDensity);
        appDisplayMetrics.density=targetDensity;
        appDisplayMetrics.scaledDensity=targetScaledDensity;
        appDisplayMetrics.densityDpi=targetDensityDpi;

        final DisplayMetrics activityDisplayMetrics=activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density=targetDensity;
        activityDisplayMetrics.scaledDensity=targetScaledDensity;
        activityDisplayMetrics.densityDpi=targetDensityDpi;
    }

}

 

 

3.2.Activity使用

package wjn.com.imwithdemo.activity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;

import com.yatoooon.screenadaptation.ScreenAdapterTools;

import wjn.com.imwithdemo.R;
import wjn.com.imwithdemo.utils.MyDisplayMetrics;

public class ScreenActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyDisplayMetrics.setCustomDensity(this,getApplication());
        setContentView(R.layout.activity_screen);
    }

}

 

 

3.3.布局

<?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:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:text="文本一"
        android:gravity="center"
        android:textColor="#FFFFFF"
        android:textSize="14sp"
        android:background="@color/btn_login_pressed"/>


    <TextView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:text="文本二"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:textColor="#FFFFFF"
        android:textSize="16sp"
        android:background="@color/btn_answer_normal"/>

</LinearLayout>

 

 

3.4.效果

Android 屏幕适配之框架(AndroidAutoSize)(今日头条)适配

 

 

3.5.GitHub链接

https://github.com/weiyanjie/SmartLayout

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/135721.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MySQL时间戳转日期

    MySQL时间戳转日期使用FROM_UNIXTIME函数,具体如下:FROM_UNIXTIME(unix_timestamp,format)返回表示Unix时间标记的一个字符串,根据format字符串格式化。format可以包含与DATE_FORMAT()函数列出的条目同样的修饰符。下列修饰符可以被用在format字符串中:%M月名字(January……December)%W星期名字(Sunda

    2022年6月21日
    31
  • 网约技师APP详细设计说明书「建议收藏」

    网约技师APP详细设计说明书「建议收藏」目录1引言31.1编写目的31.2背景31.3定义31.4参考资料42程序系统的结构43登录程序Login()设计说明53.1程序描述53.2功能63.3性能63.4输人项63.5输出项73.6算法73.7流程逻辑73.8接口83.9存储分配83.10注释设计

    2022年6月4日
    32
  • 查看linux虚拟机版本,查看CentOS版本方法

    查看linux虚拟机版本,查看CentOS版本方法查看 CentOS 版本方法有以下命令可以查看 lsb release aLSBVersion core 3 1 ia32 core 3 1 noarch graphics 3 1 ia32 graphics 3 1 noarchDistri CentOSDescri CentOSreleas 4 Final Release

    2025年7月30日
    2
  • agrs php zf2_AGRS币是什么东西,投资理财区块链数字货币

    agrs php zf2_AGRS币是什么东西,投资理财区块链数字货币AGRS币是什么东西,投资理财区块链数字货币如何高回报投zi数字货币,最后一波10~100倍财富回报盛宴,错过了就不再有!大家好,七年虚拟货币玩家,大牛谈不上,但是也带着上万名学生做了几年投zi了,没错过比特币,没错过以太坊,没错过AGRS币,回报在1000倍左右了从2013年年前开始带学生投zi区块链,学生的平均回报也在200倍左;欢迎大家来一起交流,互相学习!如何在AGRS币牛市和熊市的切换中…

    2022年5月30日
    32
  • 两种方法在VMware中安装VMware Tools

    两种方法在VMware中安装VMware ToolsVMwareTools的主要作用是虚拟硬件的驱动,鼠标的无缝移出移入,剪贴板共享,共享主机上的文件夹等功能。 方法一1.打开文件夹-Ubuntu17.10amd64(根据自己版本来看)。2.在VMwareWorkstation主界面点击菜单“虚拟机”。会弹出提示框,点击是。3.保持网络状态,等待后台下载,下载后会出现一个压缩包。在下载过程中可能会出现DVD图标…

    2022年5月26日
    36
  • SQL Server 2008 评估期已过解决方法

    SQLServer2008有180天的试用期,过期后会提示“评估期已过”的提示。1、进入SQLServer安装中心:2、选择“维护”-“版本升级”3、输入密钥:其他的根据提示操作。附S

    2021年12月23日
    58

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号