1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Android 屏幕适配方案(多分辨率适配)

Android 屏幕适配方案(多分辨率适配)

时间:2022-01-17 11:28:36

相关推荐

Android 屏幕适配方案(多分辨率适配)

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主威威喵原创,请多支持与指教。

本文首发于此 博主:威威喵|博客主页:/smile_running

由于 Android 手机五花八门,手机厂商较多,所以导致的一个问题就是屏幕分辨率各有千秋,诸如:320*480、540*960、768*1280、1080*1920 等等,因为屏幕分辨率不同,导致一个难题就是如何将我们开发的应用适配这些分辨率,这就是本文要引出的一个屏幕适配的问题。

在早期的做法呢,由于 Android project 给我们提供了不同的 drawable res 文件,如 drawable-ldpi、drawable-mdpi、drawable-hdpi、drawable-xhdpi 等等,分别对应不同的分辨率,从低分辨率到高分辨率。我们通过设计和制作不同的分辨率图片资源,放置在不同的 res 下,这样在 Android 系统加载时,会自动的去找对应 res 下的资源文件,这种方法可以做到适配效果。

可是,这样的话会导致一个比较严重的问题,因为每个 res 下都放置了图片资源文件,如果图片过多的话,会增大 apk 的体积,导致 apk 包过于庞大。还有一个是图片制作也比较费时,虽然是不同的分辨率的同一张图,修改起来也麻烦。

如今,这种做法渐渐的被放弃使用了,因为它的缺点比较明显,接下来,我们来看本文要讲的适配方案。

我们做过 Android 开发的都知道,要尽量的将控件的宽度设置成 wrap_content 或者 match_content,将大小设置为一个 dp 值,而不是具体的 px 值,这样能够有效的适配不同的分辨率。但是呢,难免一下控件需要占用屏幕的一个比例值,比如在 768 * 1280 的分辨率下,TextView 需要占一半效果,那么它的 width 就是 384 个 px;然而在 1080 * 1920 的分辨率下,这个TextView 的 width 就变成了 540 个 px 了。

那么,要适配这样的一种方式,需要如何做呢?接下来,我们来一起看看本文的适配方案吧!

一、引入 Android SDK 的 percent library

implementation 'com.android.support:percent:28.+'

接下来,我们要想让TextView占用屏幕的一半宽度,就可以通过设置一个百分比即可。布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?><android.support.percent.PercentRelativeLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/colorAccent"android:gravity="center"android:text="博主:威威喵"android:textColor="#ffffff"app:layout_heightPercent="50%"app:layout_widthPercent="50%" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/tv"android:layout_alignParentRight="true"android:background="@color/colorAccent"android:gravity="center"android:text="博客:/smile_Running"android:textColor="#ffffff"app:layout_heightPercent="50%"app:layout_widthPercent="50%" /></android.support.percent.PercentRelativeLayout>

为了更好的证明它能够适配所有的分辨率,我这里开启了两个不同分辨率的模拟器,一个是 768*1280,一个是 1080*1920 的分辨率,通过对比,可以看到它们的显示效果是一致的,如下图

这种方式是最简单的一种,通过引入 percent 控件,设置 layout_widthPercent 以及 layout_heightPercent 即可。

二、通过修改 px 缩放控件大小

(1)屏幕分辨率:720 * 1280

(2)屏幕分辨率:1080 * 1920

若红色(TextView)的宽度占用屏幕的一半,要想进行适配,在 720 * 1280 的分辨率中,它的宽度是 360 px,而在 1080 * 1920 的分辨率中,它的宽度是 540 px

例如,当前的设计稿像素大小为 720 * 1280 ,其中 720px 是已知的设计稿宽度,而通过代码可以获取设备的宽度,根据比例公式计算可得:1080 / 720 * 360 = 540 px 。通过封装屏幕缩放比例工具类,可以计算出当前设备宽高与设计稿的一个比例值,代码如下:

package nd.no.xww.screenadapter;import android.content.Context;import android.content.res.Resources;import android.util.DisplayMetrics;import android.view.WindowManager;/*** @author xww* @desciption : 采用 px 来适配全屏幕* @date /1/18* @time 19:10*/public class ScreenPixels {private static ScreenPixels INSTANCE;private Context context;// design pixels on a prototype diagram(must float)private static final float DESIGN_WIDTH = 1080f;private static final float DESIGN_HEIGHT = 1920f;private int screenWidth;private int screenHeight;private ScreenPixels(Context context) {this.context = context;final WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);if (windowManager != null) {DisplayMetrics displayMetrics = new DisplayMetrics();windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);if (displayMetrics.widthPixels > displayMetrics.heightPixels) {screenWidth = displayMetrics.heightPixels;screenHeight = displayMetrics.widthPixels;} else {screenWidth = displayMetrics.widthPixels;screenHeight = displayMetrics.heightPixels;}}}public static ScreenPixels getInstance(Context context) {if (INSTANCE == null) {INSTANCE = new ScreenPixels(context.getApplicationContext());}return INSTANCE;}private int getStatusBarHeight() {Resources resources = context.getResources();if (resources != null) {int resId = resources.getIdentifier("status_bar_height", "dimen", "android");return resources.getDimensionPixelSize(resId);}return 0;}public int getScreenWidth() {return screenWidth;}public int getScreenHeight() {return screenHeight;}public float getScaleWidth() {return getScreenWidth() / DESIGN_WIDTH;}public float getScaleHeight() {return getScreenHeight() / DESIGN_HEIGHT;}}

获取到这个像素缩放比例,然后通过对 View 的大小缩放,达到适配的目的。具体可以通过自定义 RelativeLayout 或其它 ViewGroup,覆盖 onMeasure() 方法,对每一个 childView 的width、height 以及 margin 进行缩放,代码如下:

package nd.no.xww.screenadapter;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.widget.RelativeLayout;/*** @author xww* @desciption : 通过缩放比设置控件的宽高、缩进等* @date /1/18* @time 19:48*/public class AdaptRelativeLayout extends RelativeLayout {private static final String TAG = "AdaptRelativeLayout";private boolean flag = true;public AdaptRelativeLayout(Context context) {super(context);}public AdaptRelativeLayout(Context context, AttributeSet attrs) {super(context, attrs);}public AdaptRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (flag) { // just measure oncefloat scaleWidth = ScreenPixels.getInstance(getContext()).getScaleWidth();float scaleHeight = ScreenPixels.getInstance(getContext()).getScaleHeight();final int count = getChildCount();LayoutParams params;View childView;for (int i = 0; i < count; i++) {childView = getChildAt(i);params = (LayoutParams) childView.getLayoutParams();params.width = (int) (params.width * scaleWidth);params.height = (int) (params.height * scaleHeight);params.leftMargin = (int) (params.leftMargin * scaleWidth);params.rightMargin = (int) (params.rightMargin * scaleWidth);params.topMargin = (int) (params.topMargin * scaleHeight);params.bottomMargin = (int) (params.bottomMargin * scaleHeight);}flag = false;}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}}

这里的 onMeasure() 方法会进行测量两次,所以在缩放的时候,需要进行一个判断,如果重复测量的话,控件的大小将缩放两倍,会导致偏差。布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?><nd.no.xww.screenadapter.AdaptRelativeLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:layout_width="540px"android:layout_height="360px"android:background="@color/colorAccent"android:gravity="center"android:layout_marginTop="10px"android:layout_marginLeft="10px"android:text="博主:威威喵"android:textColor="#ffffff" /><TextViewandroid:layout_width="540px"android:layout_height="360px"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:background="@color/colorAccent"android:gravity="center"android:layout_marginBottom="10px"android:layout_marginRight="10px"android:text="博客:/smile_Running"android:textColor="#ffffff" /></nd.no.xww.screenadapter.AdaptRelativeLayout>

在布局文件中,我们应该写明当前控件的 px 值,相对于屏幕分辨率的一半。最后,我们的适配效果如下:

三、通过修改 density、scaleDensity、densityApi 值进行适配

首先呢,通过获取 application 的 density、scaleDensity、densityApi,对当前 Activity density、scaleDensity、进行缩放,以达到适配效果。代码如下:

package nd.no.xww.screenadapter;import android.app.Activity;import android.app.Application;import ponentCallbacks;import android.content.res.Configuration;import android.util.DisplayMetrics;/*** @author xww* @desciption : 根据 dp 值来适配* @date /1/19* @time 17:54*/public class ScreenDensity {// 设计稿的屏幕宽度 dp 值private static final float DESIGN_DENSITY = 360f;private static float appScaleDensity;public static void setDensity(Application application, Activity activity) {// 获取 application 的 DisplayMetricsDisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();float appDensity = appDisplayMetrics.density;appScaleDensity = appDisplayMetrics.scaledDensity;//监听字体大小变化,重新获取变化后的 appScaleDensity,适配到应用中application.registerComponentCallbacks(new ComponentCallbacks() {@Overridepublic void onConfigurationChanged(Configuration newConfig) {if (newConfig != null && newConfig.fontScale > 0) {appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;}}@Overridepublic void onLowMemory() {}});// 获取 activity 的 DisplayMetricsDisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();// 计算缩放比例(设备屏幕宽度 / 设计稿宽度)float targetDensity = appDisplayMetrics.widthPixels / DESIGN_DENSITY;float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);int targetDensityApi = (int) (targetDensity * 160);activityDisplayMetrics.density = targetDensity;activityDisplayMetrics.scaledDensity = targetScaleDensity;activityDisplayMetrics.densityDpi = targetDensityApi;}}

若没有字体大小适配的话,可以不必监听系统字体大小的变化。

这里有必要解释一下,density 表示屏幕的一个物理密度,scaleDensity 表示字体显示大小的一个密度值,通常情况下都是与 density 相等的,而 densityApi 则表示它相对于屏幕密度的一个比例值,就是 dots-per-inch。

注意:在 Activity 的 setContentView 之前进行设置 density

package nd.no.xww.screenadapter;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ScreenDensity.setDensity(getApplication(), this);setContentView(R.layout.activity_main);}}

布局文件代码:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv"android:layout_width="180dp"android:layout_height="180dp"android:background="@color/colorAccent"android:gravity="center"android:text="博主:威威喵"android:textColor="#ffffff" /><TextViewandroid:layout_width="180dp"android:layout_height="180dp"android:layout_below="@+id/tv"android:layout_alignParentRight="true"android:background="@color/colorAccent"android:gravity="center"android:text="博客:/smile_Running"android:textColor="#ffffff" /></RelativeLayout>

它的一个适配最终效果如下:

如果要设置全局的一个适配,可以有两种方式,一种是抽到 BaseActivity 中进行适配每一个 Activity。第二中方式是在自己的 Application 中,监听每一个 Activity 的生命周期回调情况,代码如下:

package nd.no.xww.screenadapter;import android.app.Activity;import android.app.Application;import android.os.Bundle;/*** @author xww* @desciption :* @date /1/19* @time 19:44*/public class App extends Application {@Overridepublic void onCreate() {super.onCreate();registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {ScreenDensity.setDensity(App.this, activity);}@Overridepublic void onActivityStarted(Activity activity) {}@Overridepublic void onActivityResumed(Activity activity) {}@Overridepublic void onActivityPaused(Activity activity) {}@Overridepublic void onActivityStopped(Activity activity) {}@Overridepublic void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Overridepublic void onActivityDestroyed(Activity activity) {}});}}

最后,在 xml 文件中记得换成我们自己的 App name 即可。

好了,如上提供的三种适配方案都能较好的解决当前屏幕适配问题,至于如何抉择,就看你的使用场景如何了。第一种方案比较简单,如果不是自定义 View 的话,在适配起来会简单很多,第二种比较适合在自定义 View 中做统一的大小处理,第三种是一个全局的适配方案,目前这种方法也在很多 App 中运用,所以比较推荐第三种。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。