Android屏幕适配简单总结,PT适配方案

小编 2026-06-05 阅读:1297 评论:0
在Android开发中,由于Android碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,。因此,屏幕的适配是Android开发者不可缺少的一部分工作。 今天,记录的是今日头...

在Android开发中,由于Android碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,。因此,屏幕的适配是Android开发者不可缺少的一部分工作。

今天,记录的是今日头条的适配方案的总结,在学习适配前可阅读下面的文章了解适配:

一种极低成本的Android屏幕适配方式

Android 目前稳定高效的UI适配方案

骚年你的屏幕适配方式该升级了!-今日头条适配方案

传统dp适配方式的缺点

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

px = density * dp;

density = dpi / 160;

px = dp * (dpi / 160);

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

今日头条屏幕适配方案的核心原理在于,根据以下公式算出density

density = 当前设备屏幕总宽度(单位为像素)/ 设计图总宽度(单位为 dp) 

density 的意思就是 1 dp 占当前设备多少像素。

为什么要算出 density,这和屏幕适配有什么关系呢?

如图所示:

\"\"

通过系统的applyDimension()方法知道,无论你的布局文件中填写的是什么单位,最终都会转换为 px

所以我们常用的 px 转 dp 的公式 dp = px / density,就是根据上面的方法得来的,density 在公式的运算中扮演着至关重要的一步。

而今天总结的是PT的适配方案,所以需要转换成 pt 单位进行计算:公式如下:

PT = (sqrt(高^2+宽^2))/ 72

适配方案

此次适配只要是把设计图的宽度适配,而不是使用屏幕的宽度,举个栗子:

当前屏幕的宽是720 PX,设计图的宽度是375 PT,则我们就需要按照公式,按照屏幕的宽的值去转换,转换方法如下:

/**
 * 以pt为单位重新计算大小 designWidth 是设计图的宽度
 */
public static void resetDensity(Context context, float designWidth) {
    if (context == null)
        return;
    Point size = new Point();
    ((WindowManager) context.getSystemService(WINDOW_SERVICE)).getDefaultDisplay().getSize(size);
    Resources resources = context.getResources();
    resources.getDisplayMetrics().xdpi = size.x / designWidth * 72f;
    DisplayMetrics metrics = getMetricsOnMIUI(context.getResources());
    if (metrics != null)
        metrics.xdpi = size.x / designWidth * 72f;
}
/**
 * 解决MIUI屏幕适配问题
 *
 * @param resources
 * @return
 */
private static DisplayMetrics getMetricsOnMIUI(Resources resources) {
    if (\"MiuiResources\".equals(resources.getClass().getSimpleName()) || \"XResources\".equals(resources.getClass().getSimpleName())) {
        try {
            Field field = Resources.class.getDeclaredField(\"mTmpMetrics\");
            field.setAccessible(true);
            return (DisplayMetrics) field.get(resources);
        } catch (Exception e) {
            return null;
        }
    }
    return null;
}

单位的转换也就到此为止,下面开始屏幕适配,代码如下:

public class App  extends MultiDexApplication {
    private static final int DESIGN_WIDTH = 375;
    private static App mApp;
    private static Context mContext;

    public static App getInstance() {
        if (mApp == null) {
            synchronized (App.class) {
                if (mApp == null) {
                    mApp = new App();
                }
            }
        }
        return mApp;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
        final Context context = this;
        FormatStrategy mFormatStrategy = PrettyFormatStrategy.newBuilder()
                .showThreadInfo(false)  // (可选)是否显示线程信息。默认值true
//                .methodCount(5)         // (可选)要显示的方法行数。默认值2
//                .methodOffset(7)        // (可选)隐藏内部方法调用到偏移量。默认值5
//                .logStrategy() // (可选)更改要打印的日志策略。默认LogCat
                .tag(\"UIAdaptive\")   // (可选)每个日志的全局标记。默认PRETTY_LOGGER .build
                .build();
        //log日志打印框架Logger
        Logger.addLogAdapter(new AndroidLogAdapter(mFormatStrategy));
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //限制竖屏
                Log.e(\"TAG\",\"状态栏高度\"+getStatusBarHeight());
//                a(activity);
                resetDensity(context, DESIGN_WIDTH);
                resetDensity(activity, DESIGN_WIDTH);
                setImmersiveStatusBar(activity);
                Log.e(\"TAG\",\"状态栏高度\"+getStatusBarHeight());
                Logger.e(\"大小\"+getNotchSize(context).length);
                Logger.d(getNotchSize(context));


                if (activity instanceof IActivityBase) {
                    ((IActivityBase) activity).initView();
                    ((IActivityBase) activity).initData();
                }
            }

            @Override
            public void onActivityStarted(Activity activity) {
                setToolBar(activity);
                resetDensity(context, DESIGN_WIDTH);
                resetDensity(activity, DESIGN_WIDTH);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                resetDensity(context, DESIGN_WIDTH);
                resetDensity(activity, DESIGN_WIDTH);
            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }

    /**
     * 以pt为单位重新计算大小
     */
    public static void resetDensity(Context context, float designWidth) {
        if (context == null)
            return;
        Point size = new Point();
        ((WindowManager) context.getSystemService(WINDOW_SERVICE)).getDefaultDisplay().getSize(size);
        Resources resources = context.getResources();
        resources.getDisplayMetrics().xdpi = size.x / designWidth * 72f;
        DisplayMetrics metrics = getMetricsOnMIUI(context.getResources());
        if (metrics != null)
            metrics.xdpi = size.x / designWidth * 72f;
    }

    /**
     * 解决MIUI屏幕适配问题
     *
     * @param resources
     * @return
     */
    private static DisplayMetrics getMetricsOnMIUI(Resources resources) {
        if (\"MiuiResources\".equals(resources.getClass().getSimpleName()) || \"XResources\".equals(resources.getClass().getSimpleName())) {
            try {
                Field field = Resources.class.getDeclaredField(\"mTmpMetrics\");
                field.setAccessible(true);
                return (DisplayMetrics) field.get(resources);
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }


    /**
     * 设置状态栏
     *
     * @param activity
     */
    private void setImmersiveStatusBar(Activity activity) {
        if (activity instanceof IActivityStatusBar) {
            if (((IActivityStatusBar) activity).getStatusBarColor() != 0) {
                setTranslucentStatus(activity);
                addImmersiveStatusBar(activity, ((IActivityStatusBar) activity).getStatusBarColor());
            }
        }
    }

    /**
     * 设置状态栏为透明
     *
     * @param activity
     */
    private void setTranslucentStatus(Activity activity) {
        //******** 5.0以上系统状态栏透明 ********
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }

    /**
     * 添加自定义状态栏
     *
     * @param activity
     */
    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
    private void addImmersiveStatusBar(Activity activity, int color) {
        ViewGroup contentFrameLayout = activity.findViewById(android.R.id.content);
        View contentView = contentFrameLayout.getChildAt(0);
        if (contentView != null && Build.VERSION.SDK_INT >= 14) {
            contentView.setFitsSystemWindows(true);
        }

        View statusBar = new View(activity);
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        Window window=activity.getWindow();
        if (BangScreenUtil.getBangScreenInstance().hasBangScreen(window)){
            Logger.e(\"是刘海屏\");
            params.height=getNotchSize(activity)[1];
        }else {
            Logger.e(\"不是刘海屏\");
            params.height = getStatusBarHeight();
        }
//        params.height = getStatusBarHeight();
        statusBar.setLayoutParams(params);
        statusBar.setBackgroundColor(color);
        contentFrameLayout.addView(statusBar);
    }

    /**
     * 获取状态栏高度
     *
     * @return
     */
    private int getStatusBarHeight() {
        int statusBarHeight = 0;
        int resourceId = getResources().getIdentifier(\"status_bar_height\", \"dimen\", \"android\");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }
        return statusBarHeight;
    }

    /**
     * 设置ToolBar
     *
     * @param activity
     */
    private void setToolBar(final Activity activity) {
        if (activity.findViewById(R.id.tool_bar) != null && ((AppCompatActivity) activity).getSupportActionBar() == null) {
            Toolbar toolbar = activity.findViewById(R.id.tool_bar);
            if (!TextUtils.isEmpty(activity.getTitle())) {
                toolbar.setTitle(activity.getTitle());
            } else {
                toolbar.setTitle(\"\");
            }

            if (((IActivityStatusBar) activity).getStatusBarColor() != 0) {
                toolbar.setBackgroundColor(((IActivityStatusBar) activity).getStatusBarColor());
            } else {
                toolbar.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
            }

            ((AppCompatActivity) activity).setSupportActionBar(toolbar);
            ActionBar actionBar = ((AppCompatActivity) activity).getSupportActionBar();
            if (actionBar != null) {
                actionBar.setHomeButtonEnabled(true);
                actionBar.setDisplayHomeAsUpEnabled(true);
            }
            toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    activity.onBackPressed();
                }
            });
        }
    }

    //获取华为刘海的高宽
    public static int[] getNotchSize(Context context) {
        int[] ret = new int[]{0, 0};
        try {
            ClassLoader cl = context.getClassLoader();
            Class HwNotchSizeUtil = cl.loadClass(\"com.huawei.android.util.HwNotchSizeUtil\");
            Method get = HwNotchSizeUtil.getMethod(\"getNotchSize\");
            ret = (int[]) get.invoke(HwNotchSizeUtil);
        } catch (ClassNotFoundException e) {
            Log.e(\"haha\", \"getNotchSize ClassNotFoundException\");
        } catch (NoSuchMethodException e) {
            Log.e(\"haha\", \"getNotchSize NoSuchMethodException\");
        } catch (Exception e) {
            Log.e(\"haha\", \"getNotchSize Exception\");
        } finally {
            return ret;
        }
    }
}

注意:因为适配是按照设计图的宽度去进行适配的,而不是使用屏幕的适配,所以,还需要步骤,就是在每一次加班布局文件的时候,

setContentView(R.layout.activity_main);

总是需要放在

super.onCreate(savedInstanceState);之前

如图:

\"\"

到此,屏幕适配就完成了。

以上代码中还涉及到了刘海屏的适配,只是针对华为的刘海屏进行了适配,所以代码块只是使用了华为的刘海屏适配方案。下个博文进行总结。

 

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表