Android智能平板应用,界面适配的另一种轻量级方法

2022-04-13 10:09:13 浏览数 (1)

Android的界面适配,很常见的需求。

各种设备种类和尺寸那么多,基于一种原型设计好的界面,换到另一种设备上去若不适配全乱套了。好在还是有很多方案的,这减少了不少的开发工作量。最流行的就是头条的方案了,使用也超级简单。然而,如果不想引入,还可以简单的一个工具类实现,原理类似于头条的方案。

这里简单介绍下,使用起来也很简单。

原理就是转换设备的显示像素密度Density。

头条的UI适配的低成本方案AndroidAutoSize也是基于这个原理,只是它封装的更好,稳定性也更好。

就是智能平板UI尺寸虽然不一样,但是长宽比例差不多。比如基于1920*1080的界面尺寸设计的应用,现在新的设备屏幕是1366x768。若不加适配肯定显示不全,若能等比例缩小0.71倍就好了,那么方法是有的。就一个类文件Density.java

代码语言:javascript复制
package com.newcapec.visitorsystem.utils;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;

public class Density {

    /**
     * 设置屏幕像素密度
     * 思路讲解,计算 B601的老固件像素密度1.5 ,新固件像素密度1 600*32/48 = 400
     * 首先我们需要获取当前机型的屏幕密度信息:appDensity,appScaleDensity
     *
     * 我们的设计尺寸会根据默认机型计算出一个固定的以dp为单位的宽度:WIDTH
     *
     * 比如:默认机型的宽高为1080*1920,该设备的屏幕密度为3
     * 那么WIDTH = 1080/3 = 360dp;因此所有适配机型的宽也就等于360dp。
     * 根据 屏幕宽度 / 屏幕密度=WIDTH公式,现在知道屏幕宽度和WIDTH,也就能求出:屏幕密度=屏幕宽度 / WIDHT;
     *
     * 现在屏幕宽度(dp):targetDensity = displayMetrics.widthPixels / WIDTH 求出。
     *
     * 接下来,就需要求出适配机型的scaleDensity
     *
     * appScaleDensity / appDensity = targetScaleDensity / targetDensity ;
     *
     * targetScaleDensity = targetDensity * (appScaleDensity / appDensity) ;
     *
     * densityDpi = density * 160 ;
     *
     * 最后,把获取到的数据,设置到activity的displayMetrics中。
     *
     * 计算出来后,我们需要在绘制view之前先设置好
     *
     * 在onCreate中的setContentView之前添加
     * ———————————————
     * 原文链接:https://blog.csdn.net/weixin_45365889/article/details/102660467
     */
    private final static float WIDTH = 1920;//适配机型的宽为1920dp,屏幕宽/屏幕密度=1920/1=1920
    private static float appDensity;
    private static float appScaleDensity;

    public static void setDensity(Context appcontx, Context contx) {
        //获取当前app的屏幕显示信息
        WindowManager wm = (WindowManager)appcontx.getSystemService(Context.WINDOW_SERVICE);
        int width = wm.getDefaultDisplay().getWidth();
        Log.d("Utils","app width:" width);
        DisplayMetrics displayMetrics = appcontx.getResources().getDisplayMetrics();
        appDensity = displayMetrics.density;
        appScaleDensity = displayMetrics.scaledDensity;
        Log.d("Utils","appDensity:" appDensity ",appScaleDensity:" appScaleDensity);
        //计算等比缩放后的density和scaleDensity
        //WIDTH相对于所有屏幕宽度都是相等的,它是用dp作为单位,所以 屏幕宽度/屏幕密度=WIDTH
        //targetDensity = targetWidht/WIDTH
        float targetDensity = displayMetrics.widthPixels / WIDTH;
        //appScaleDensity/appDensity=targetScaleDensity/targetDensity;
        float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
        int targetDensityDpi = (int) (targetDensity * 160);

        //替换activity的density,appdensity,densityDpi
        DisplayMetrics aDisplayMertics = contx.getResources().getDisplayMetrics();
        Log.d("Utils","presentDensity:" aDisplayMertics.density ",presentScaleDensity:" aDisplayMertics.scaledDensity);
        aDisplayMertics.density = targetDensity;
        aDisplayMertics.scaledDensity = targetScaleDensity;
        aDisplayMertics.densityDpi = targetDensityDpi;

        Log.d("Utils","targetDensity:" targetDensity ",targetScaleDensity:" targetScaleDensity);
    }
}

注意把里面的WIDTH配置为原设计模型的宽度。

原理介绍

首先我们需要获取当前机型的屏幕密度信息:appDensity,appScaleDensity

我们的设计尺寸会根据默认机型计算出一个固定的以dp为单位的宽度:WIDTH

比如:默认机型的宽高为1080*1920,该设备的屏幕密度为3 那么WIDTH = 1080/3 = 360dp;因此所有适配机型的宽也就等于360dp。 根据 屏幕宽度 / 屏幕密度=WIDTH公式,现在知道屏幕宽度和WIDTH,也就能求出:屏幕密度=屏幕宽度 / WIDHT;

现在屏幕宽度(dp):targetDensity = displayMetrics.widthPixels / WIDTH 求出。

接下来,就需要求出适配机型的scaleDensity

appScaleDensity / appDensity = targetScaleDensity / targetDensity ;

targetScaleDensity = targetDensity * (appScaleDensity / appDensity) ;

densityDpi = density * 160 ;

最后,把获取到的数据,设置到activity的displayMetrics中。

计算出来后,我们需要在绘制view之前先设置好。

使用方法

在BaseActivity的onCreate中调用一下就可以了。

Utils.setDensity(App.getContext(),this);

代码语言:javascript复制
package com.newcapec.visitorsystem.activity.frontscreen;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;

import androidx.annotation.Nullable;

import com.newcapec.visitorsystem.app.App;
import com.newcapec.visitorsystem.threads.ReadCardThread;
import com.newcapec.visitorsystem.utils.Utils;

public class BaseActivity extends Activity {
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Utils.setDensity(App.getContext(),this);
    }
    /*
     * 隐藏系统键盘
     */
    public void hideXtView(){
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        View view = this.getCurrentFocus();
        if (view != null) {
            InputMethodManager inputMethodManager = (InputMethodManager) this.getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        }
    }
}

以上只是一个简单的方法,可作为一个学习界面适配原理的例子。但是对于自定义view和listview,RecyclerView是失效的。

这里再介绍下基于头条的UI适配的低成本方案AndroidAutoSize

AndroidAutoSize对于自定义view,listview,和RecyclerView适配也完全没问题,原理类似。

对于RecyclerView只需在Adapter 的onCreateViewHolder里,调用一下:

代码语言:javascript复制
AutoSizeCompat.autoConvertDensityOfGlobal(context.getResources());

即可完美适配RecyclerView的UI显示。

AndroidAutoSize:

github地址

https://github.com/JessYanCoding/AndroidAutoSize

gradle引入

implementation 'me.jessyan:autosize:1.1.2'

manifase配置

如果只使用副单位 (pt、in、mm) 就可以直接以像素作为单位填写设计图的尺寸, 不需再把像素转化为 dp

代码语言:javascript复制
<!-- 如果只使用副单位 (pt、in、mm) 就可以直接以像素作为单位填写设计图的尺寸, 不需再把像素转化为 dp-->
<!-- 用mm副单位开发,这里配置和设计稿一样的尺寸px , 1920 x 1080px -->
<manifest>
    <application>            
        <meta-data
            android:name="design_width_in_dp"
            android:value="1080"/>
        <meta-data
            android:name="design_height_in_dp"
            android:value="1920"/>           
     </application>           
</manifest>

注意上面的meta-data是原始设计尺寸的宽,高。可不用设置成新设备的屏幕尺寸了。

以上基本满足你的基本需求了,若还有问题,可以参见这个贴,汇总了常见问题的解决办法:

https://github.com/JessYanCoding/AndroidAutoSize/issues/13

还有一种屏幕适配方案,原理跟上述的差不多,是AndroidScreenAdaptation。

AndroidScreenAdaptation:

地址:https://github.com/yatoooon/AndroidScreenAdaptation

AndroidScreenAdaptation库特点:

完全不用改变自己的布局编写习惯,你原先是怎么写布局,就怎么写布局.不用去继承适配类,不用在最外层包裹适配布局,不用新建茫茫多的分辨率适配文件夹,不要求强制使用px为单位,可以实时预览布局,全面屏或带虚拟按键手机适配也没问题。

使用:

1.添加依赖

代码语言:javascript复制
implementation 'me.yatoooon:screenadaptation:1.1.1'

2.初始化工具类

(1)创建自己的application继承Application

代码语言:javascript复制
public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ScreenAdapterTools.init(this);
    }
    
//如果应用屏幕固定了某个方向不旋转的话(比如qq和微信),下面可不写.
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        ScreenAdapterTools.getInstance().reset(this);
    }
}

(2)在AndroidManifest.xml文件中声明使用你自己创建的application并且添加meta-data数据,例子上标明了这些数据的代表的意义

代码语言:javascript复制
<application
        android:name=".App"
        .....
        <meta-data
            android:name="designwidth"
            android:value="1080" />  //设计图的宽,单位是像素,推荐用markman测量,量出来如果是750px那么请尽量去找ui设计师要一份android的设计图.
        <meta-data
            android:name="designdpi"
            android:value="480" />   //设计图对应的标准dpi,根据下面的那张图找到对应的dpi,比如1080就对应480dpi,如果拿到的是其他宽度的设计图,那么选择一个相近的dpi就好了
        <meta-data
            android:name="fontsize"
            android:value="1.0" />   //全局字体的大小倍数,有时候老板会觉得你的所有的字小了或者大了,你总不能一个一个去改吧
        <meta-data
            android:name="unit"
            android:value="px" />   //你的布局里面用的是px这就写px,你的布局里面用的是dp这就写dp,要统一,不要一会儿px一会儿dp,字体也用px或者dp,不要用sp,微信qq用的肯定不是sp.
</application>    

3. 开始使用

(1)在Activity中,找到setcontentview(R.layout.xxxxxx)

代码语言:javascript复制
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_dp);
        //在setContentView();后面加上下面这句话
        ScreenAdapterTools.getInstance().loadView((ViewGroup) getWindow().getDecorView());

    }
}

注: 自定义view的话,在 ScreenAdapterTools.getInstance().loadView((ViewGroup) view); 外面包裹一层判断如下,不然在使用自定义view编写布局文件时预览xml会有问题!但不影响真机运行效果.

代码语言:javascript复制
       if (!isInEditMode()) {
            ScreenAdapterTools.getInstance().loadView((ViewGroup) view);
        }    

原理

1. px是分辨率的单位 比如现在主流手机分辨率1080*1920. 2. dp是安卓开发专有的单位 在 不同的手机下 1dp = 不同的 px. 3. sp是字体大小(前面清单文件中要求字体也用dp或者px),sp随系统字体大小变化而变化,但据我观察,像微信qq这些app的字体是不随系统显示字体大小变化的. ### 本库是按照设计图的宽度和对应标准dpi来适配的(宽度增加或减少,高度同比例增加或减少),在不同的分辨率,不同ppi(手机屏幕密度,又称为dpi),不同最小宽度(有的人喜欢去调开发者选项下面的最小宽度,主流手机默认为360dp)的手机下都做到了适配。

引用:

今日头条屏幕适配方案终极版 AndroidAutoSize-玩Android - wanandroid.com安卓适配AutoSize详解_xxdw1992的博客-CSDN博客_me.jessyan:autosizeAndroid安卓中最棒的屏幕适配AndroidScreenAdaptation_快乐李同学的博客-CSDN博客_android screen

屏幕适配:修改屏幕像素密度,随便设dp_Android架构师丨小熊的博客-CSDN博客

Andoid屏幕适配终极手段(小编用过最得劲的dp适配)_奋斗的IT青年-CSDN博客_最小宽度多少dp让手机流畅

Android AutoLayout全新的适配方式 堪称适配终结者_Hongyang-CSDN博客_autolayout

0 人点赞