当前位置:   article > 正文

Android 屏幕适配方案(多分辨率适配)_android 判断分辨率 setcontentview之前

android 判断分辨率 setcontentview之前

博主声明:

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

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/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  占用屏幕的一半宽度,就可以通过设置一个百分比即可。布局文件代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <TextView
  9. android:id="@+id/tv"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:background="@color/colorAccent"
  13. android:gravity="center"
  14. android:text="博主:威威喵"
  15. android:textColor="#ffffff"
  16. app:layout_heightPercent="50%"
  17. app:layout_widthPercent="50%" />
  18. <TextView
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:layout_below="@+id/tv"
  22. android:layout_alignParentRight="true"
  23. android:background="@color/colorAccent"
  24. android:gravity="center"
  25. android:text="博客:https://blog.csdn.net/smile_Running"
  26. android:textColor="#ffffff"
  27. app:layout_heightPercent="50%"
  28. app:layout_widthPercent="50%" />
  29. </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 。通过封装屏幕缩放比例工具类,可以计算出当前设备宽高与设计稿的一个比例值,代码如下:

  1. package nd.no.xww.screenadapter;
  2. import android.content.Context;
  3. import android.content.res.Resources;
  4. import android.util.DisplayMetrics;
  5. import android.view.WindowManager;
  6. /**
  7. * @author xww
  8. * @desciption : 采用 px 来适配全屏幕
  9. * @date 2020/1/18
  10. * @time 19:10
  11. */
  12. public class ScreenPixels {
  13. private static ScreenPixels INSTANCE;
  14. private Context context;
  15. // design pixels on a prototype diagram(must float)
  16. private static final float DESIGN_WIDTH = 1080f;
  17. private static final float DESIGN_HEIGHT = 1920f;
  18. private int screenWidth;
  19. private int screenHeight;
  20. private ScreenPixels(Context context) {
  21. this.context = context;
  22. final WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  23. if (windowManager != null) {
  24. DisplayMetrics displayMetrics = new DisplayMetrics();
  25. windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
  26. if (displayMetrics.widthPixels > displayMetrics.heightPixels) {
  27. screenWidth = displayMetrics.heightPixels;
  28. screenHeight = displayMetrics.widthPixels;
  29. } else {
  30. screenWidth = displayMetrics.widthPixels;
  31. screenHeight = displayMetrics.heightPixels;
  32. }
  33. }
  34. }
  35. public static ScreenPixels getInstance(Context context) {
  36. if (INSTANCE == null) {
  37. INSTANCE = new ScreenPixels(context.getApplicationContext());
  38. }
  39. return INSTANCE;
  40. }
  41. private int getStatusBarHeight() {
  42. Resources resources = context.getResources();
  43. if (resources != null) {
  44. int resId = resources.getIdentifier("status_bar_height", "dimen", "android");
  45. return resources.getDimensionPixelSize(resId);
  46. }
  47. return 0;
  48. }
  49. public int getScreenWidth() {
  50. return screenWidth;
  51. }
  52. public int getScreenHeight() {
  53. return screenHeight;
  54. }
  55. public float getScaleWidth() {
  56. return getScreenWidth() / DESIGN_WIDTH;
  57. }
  58. public float getScaleHeight() {
  59. return getScreenHeight() / DESIGN_HEIGHT;
  60. }
  61. }

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

  1. package nd.no.xww.screenadapter;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.view.View;
  5. import android.widget.RelativeLayout;
  6. /**
  7. * @author xww
  8. * @desciption : 通过缩放比设置控件的宽高、缩进等
  9. * @date 2020/1/18
  10. * @time 19:48
  11. */
  12. public class AdaptRelativeLayout extends RelativeLayout {
  13. private static final String TAG = "AdaptRelativeLayout";
  14. private boolean flag = true;
  15. public AdaptRelativeLayout(Context context) {
  16. super(context);
  17. }
  18. public AdaptRelativeLayout(Context context, AttributeSet attrs) {
  19. super(context, attrs);
  20. }
  21. public AdaptRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  22. super(context, attrs, defStyleAttr);
  23. }
  24. @Override
  25. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  26. if (flag) { // just measure once
  27. float scaleWidth = ScreenPixels.getInstance(getContext()).getScaleWidth();
  28. float scaleHeight = ScreenPixels.getInstance(getContext()).getScaleHeight();
  29. final int count = getChildCount();
  30. LayoutParams params;
  31. View childView;
  32. for (int i = 0; i < count; i++) {
  33. childView = getChildAt(i);
  34. params = (LayoutParams) childView.getLayoutParams();
  35. params.width = (int) (params.width * scaleWidth);
  36. params.height = (int) (params.height * scaleHeight);
  37. params.leftMargin = (int) (params.leftMargin * scaleWidth);
  38. params.rightMargin = (int) (params.rightMargin * scaleWidth);
  39. params.topMargin = (int) (params.topMargin * scaleHeight);
  40. params.bottomMargin = (int) (params.bottomMargin * scaleHeight);
  41. }
  42. flag = false;
  43. }
  44. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  45. }
  46. }

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

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <nd.no.xww.screenadapter.AdaptRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <TextView
  9. android:layout_width="540px"
  10. android:layout_height="360px"
  11. android:background="@color/colorAccent"
  12. android:gravity="center"
  13. android:layout_marginTop="10px"
  14. android:layout_marginLeft="10px"
  15. android:text="博主:威威喵"
  16. android:textColor="#ffffff" />
  17. <TextView
  18. android:layout_width="540px"
  19. android:layout_height="360px"
  20. android:layout_alignParentRight="true"
  21. android:layout_alignParentBottom="true"
  22. android:background="@color/colorAccent"
  23. android:gravity="center"
  24. android:layout_marginBottom="10px"
  25. android:layout_marginRight="10px"
  26. android:text="博客:https://blog.csdn.net/smile_Running"
  27. android:textColor="#ffffff" />
  28. </nd.no.xww.screenadapter.AdaptRelativeLayout>

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

 

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

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

  1. package nd.no.xww.screenadapter;
  2. import android.app.Activity;
  3. import android.app.Application;
  4. import android.content.ComponentCallbacks;
  5. import android.content.res.Configuration;
  6. import android.util.DisplayMetrics;
  7. /**
  8. * @author xww
  9. * @desciption : 根据 dp 值来适配
  10. * @date 2020/1/19
  11. * @time 17:54
  12. */
  13. public class ScreenDensity {
  14. // 设计稿的屏幕宽度 dp 值
  15. private static final float DESIGN_DENSITY = 360f;
  16. private static float appScaleDensity;
  17. public static void setDensity(Application application, Activity activity) {
  18. // 获取 application 的 DisplayMetrics
  19. DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
  20. float appDensity = appDisplayMetrics.density;
  21. appScaleDensity = appDisplayMetrics.scaledDensity;
  22. //监听字体大小变化,重新获取变化后的 appScaleDensity,适配到应用中
  23. application.registerComponentCallbacks(new ComponentCallbacks() {
  24. @Override
  25. public void onConfigurationChanged(Configuration newConfig) {
  26. if (newConfig != null && newConfig.fontScale > 0) {
  27. appScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
  28. }
  29. }
  30. @Override
  31. public void onLowMemory() {
  32. }
  33. });
  34. // 获取 activity 的 DisplayMetrics
  35. DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
  36. // 计算缩放比例(设备屏幕宽度 / 设计稿宽度)
  37. float targetDensity = appDisplayMetrics.widthPixels / DESIGN_DENSITY;
  38. float targetScaleDensity = targetDensity * (appScaleDensity / appDensity);
  39. int targetDensityApi = (int) (targetDensity * 160);
  40. activityDisplayMetrics.density = targetDensity;
  41. activityDisplayMetrics.scaledDensity = targetScaleDensity;
  42. activityDisplayMetrics.densityDpi = targetDensityApi;
  43. }
  44. }

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

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

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

  1. package nd.no.xww.screenadapter;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.os.Bundle;
  4. public class MainActivity extends AppCompatActivity {
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. ScreenDensity.setDensity(getApplication(), this);
  9. setContentView(R.layout.activity_main);
  10. }
  11. }

布局文件代码:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <TextView
  9. android:id="@+id/tv"
  10. android:layout_width="180dp"
  11. android:layout_height="180dp"
  12. android:background="@color/colorAccent"
  13. android:gravity="center"
  14. android:text="博主:威威喵"
  15. android:textColor="#ffffff" />
  16. <TextView
  17. android:layout_width="180dp"
  18. android:layout_height="180dp"
  19. android:layout_below="@+id/tv"
  20. android:layout_alignParentRight="true"
  21. android:background="@color/colorAccent"
  22. android:gravity="center"
  23. android:text="博客:https://blog.csdn.net/smile_Running"
  24. android:textColor="#ffffff" />
  25. </RelativeLayout>

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

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

  1. package nd.no.xww.screenadapter;
  2. import android.app.Activity;
  3. import android.app.Application;
  4. import android.os.Bundle;
  5. /**
  6. * @author xww
  7. * @desciption :
  8. * @date 2020/1/19
  9. * @time 19:44
  10. */
  11. public class App extends Application {
  12. @Override
  13. public void onCreate() {
  14. super.onCreate();
  15. registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
  16. @Override
  17. public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
  18. ScreenDensity.setDensity(App.this, activity);
  19. }
  20. @Override
  21. public void onActivityStarted(Activity activity) {
  22. }
  23. @Override
  24. public void onActivityResumed(Activity activity) {
  25. }
  26. @Override
  27. public void onActivityPaused(Activity activity) {
  28. }
  29. @Override
  30. public void onActivityStopped(Activity activity) {
  31. }
  32. @Override
  33. public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
  34. }
  35. @Override
  36. public void onActivityDestroyed(Activity activity) {
  37. }
  38. });
  39. }
  40. }

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/270902
推荐阅读
相关标签
  

闽ICP备14008679号