【问题标题】:Getter for defaultDisplay: Display!' is deprecated. Deprecated in Java默认显示的吸气剂:显示!已弃用。在 Java 中已弃用
【发布时间】:2020-08-06 02:50:27
【问题描述】:

我需要屏幕的宽度。但最近发现 Android defaultDisplay deprecated with message:

getter for defaultDisplay: Display!'已弃用。在 Java 中已弃用

代码:

val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
return displayMetrics.heightPixels

请提出替代方案。

【问题讨论】:

    标签: android android-layout kotlin


    【解决方案1】:

    defaultDisplay 在 API 级别 30 (Android R) 及更高版本中被标记为已弃用。 这意味着,如果您的 SDK 最低配置低于 API 级别 30,您应该同时使用旧的弃用代码和新的推荐代码实现。

    正确解决问题后,您可以使用@Suppress("DEPRECATION") 来抑制警告

    示例:Kotlin 解决方案

        val outMetrics = DisplayMetrics()
    
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
            val display = activity.display
            display?.getRealMetrics(outMetrics)
        } else {
            @Suppress("DEPRECATION")
            val display = activity.windowManager.defaultDisplay
            @Suppress("DEPRECATION")
            display.getMetrics(outMetrics)
        }
    

    【讨论】:

    • 请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助、质量更好,并且更有可能吸引投票
    • 有谁知道 JetPack 中是否有这方面的东西?或者是否有任何方法可以在不检查 SDK/API 版本的情况下执行此操作?
    • 这两个给我的结果不同。我得到 display.heightPixels 并且在不推荐使用的方法的情况下得到 2028 但在新方法的情况下得到 2160
    • 为了便于阅读,最好在方法名称的顶部写上@Suppress("DEPRECATION")
    • getRealMetrics() 在 API 级别 31 上已弃用。
    【解决方案2】:

    WindowManager.getDefaultDisplay() 在 API 级别 30 中已被弃用,取而代之的是需要最低 API 级别 30 的 Context.getDisplay() 方法。

    目前,androidx.core.content.ContextCompat 似乎没有提供任何向后兼容的getDisplay() 方法。

    如果您只需要检索默认显示,而不是像其他答案建议的那样对不同的 API 级别使用不同的方法,您可以使用 DisplayManager.getDisplay(Display.DEFAULT_DISPLAY) 方法(自 API 17 起支持)来实现相同的结果。

    不推荐使用的代码:

    val defaultDisplay = getSystemService<WindowManager>()?.defaultDisplay
    

    新代码:

    val defaultDisplay = getSystemService<DisplayManager>()?.getDisplay(Display.DEFAULT_DISPLAY)
    

    参考:androidx.core source code


    如果您需要获取窗口的大小,新的Jetpack WindowManager library 为新旧平台版本中的新窗口管理器功能(例如可折叠设备和 Chrome 操作系统)提供了一个通用 API 界面。

    dependencies {
        implementation "androidx.window:window:1.0.0-beta02"
    }
    

    Jetpack WindowManager 提供两种方法来检索WindowMetrics 信息,作为异步流或同步。

    异步 WindowMetrics 流:

    当窗口大小发生变化时,使用WindowInfoRepository#currentWindowMetrics 获得库的通知,与此更改是否会触发配置更改无关。

    import androidx.window.layout.WindowInfoRepository
    import androidx.window.layout.WindowInfoRepository.Companion.windowInfoRepository
    import androidx.window.layout.WindowMetrics
    import androidx.lifecycle.lifecycleScope
    import androidx.lifecycle.flowWithLifecycle
    
    lifecycleScope.launch(Dispatchers.Main) {
        windowInfoRepository().currentWindowMetrics.flowWithLifecycle(lifecycle)
            .collect { windowMetrics: WindowMetrics ->
               val currentBounds = windowMetrics.bounds // E.g. [0 0 1350 1800]
               val width = currentBounds.width()
               val height = currentBounds.height()
            }
    }
    

    同步 WindowMetrics:

    在异步 API 难以处理的视图中编写代码时使用WindowMetricsCalculator(例如onMeasure 或在测试期间)。

    import androidx.window.layout.WindowMetricsCalculator
    import androidx.window.layout.WindowMetrics
    
    val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
    val currentBounds = windowMetrics.bounds // E.g. [0 0 1350 1800]
    val width = currentBounds.width()
    val height = currentBounds.height()
    

    参考:Unbundling the WindowManager | Android Developers Medium

    【讨论】:

    • 我必须添加androidx.lifecycle:lifecycle-runtime-ktx 依赖才能使用flowWithLifecycle。可能值得一提
    • 谢谢伊沃。我已经更新了答案。
    【解决方案3】:

    此方法在 API 级别 30 中已弃用。

    请改用Context.getDisplay()

    不推荐使用的方法:getDefaultDisplay

    新方法:getDisplay

    【讨论】:

    • 但这说明:调用需要 API 级别 R
    • 是的,因为 getDefaultDisplay 方法在 api 级别 30 中已弃用
    • 所以我们需要同时检查 API 30 以上和以下?
    • 我猜/希望他们很快会将它添加到 androidx.core 库中。我刚刚检查了最新的源代码但我没有找到任何东西(他们实际上是suppressed the deprecation warning in the classes that use it
    【解决方案4】:

    试试这样的:

    
        private Display getDisplay(@NonNull WindowManager windowManager) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                // This one (context) may or may not have a display associated with it, due to it being
                // an application context
                return getDisplayPostR();
            } else {
                return getDisplayPreR(windowManager);
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.R)
        private Display getDisplayPostR() {
            // We can't get the WindowManager by using the context we have, because that context is a
            // application context, which isn't associated with any display. Instead, grab the default
            // display, and create a DisplayContext, from which we can use the WindowManager or
            // just get that Display from there.
            //
            // Note: the default display doesn't have to be the one where the app is on, however the
            // getDisplays which returns a Display[] has a length of 1 on Pixel 3.
            //
            // This gets rid of the exception interrupting the onUserLogin() method
            Display defaultDisplay = DisplayManagerCompat.getInstance(context).getDisplay(Display.DEFAULT_DISPLAY);
            Context displayContext = context.createDisplayContext(defaultDisplay);
            return displayContext.getDisplay();
        }
    
        @SuppressWarnings("deprecation")
        private Display getDisplayPreR(@NonNull WindowManager windowManager) {
            return windowManager.getDefaultDisplay();
        }
    

    或获取实际尺寸:

        private Point getScreenResolution() {
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            if (wm == null) {
                return null;
            }
            Display display = getDisplay(wm);
    
            return getSize(display, wm);
        }
    
        private Point getSize(Display forWhichDisplay, WindowManager windowManager) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                return getSizePostR(windowManager);
            } else {
                return getSizePreR(forWhichDisplay);
            }
        }
    
        @RequiresApi(api = Build.VERSION_CODES.R)
        private Point getSizePostR(@NonNull WindowManager windowManager) {
            WindowMetrics currentWindowMetrics = windowManager.getCurrentWindowMetrics();
            Rect bounds = currentWindowMetrics.getBounds();
    
            // Get the insets, such as titlebar and other decor views
            WindowInsets windowInsets = currentWindowMetrics.getWindowInsets();
            Insets insets = windowInsets.getInsets(WindowInsets.Type.navigationBars());
            // If cutouts exist, get the max of what we already calculated and the system's safe insets
            if (windowInsets.getDisplayCutout() != null) {
                insets = Insets.max(
                        insets,
                        Insets.of(
                                windowInsets.getDisplayCutout().getSafeInsetLeft(),
                                windowInsets.getDisplayCutout().getSafeInsetTop(),
                                windowInsets.getDisplayCutout().getSafeInsetRight(),
                                windowInsets.getDisplayCutout().getSafeInsetBottom()
                        )
                );
            }
            // Calculate the inset widths/heights
            int insetsWidth = insets.right + insets.left;
            int insetsHeight = insets.top + insets.bottom;
    
            // Get the display width
            int displayWidth = bounds.width() - insetsWidth;
            int displayHeight = bounds.height() - insetsHeight;
    
            return new Point(displayWidth, displayHeight);
        }
    
        // This was deprecated in API 30
        @SuppressWarnings("deprecation")
        private Point getSizePreR(Display display) {
            Point size = new Point();
            if (isRealDisplaySizeAvailable()) {
                display.getRealSize(size);
            } else {
                display.getSize(size);
            }
    
            return size;
        }
    
        private static boolean isRealDisplaySizeAvailable() {
            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
        }
    

    【讨论】:

      【解决方案5】:

      任何想用 java 做的人都在这里:

      if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
      
              Display display = this.getDisplay();
              DisplayMetrics displayMetrics = new DisplayMetrics();
              display.getRealMetrics(displayMetrics);
      
              float density  = getResources().getDisplayMetrics().density;
              float dpHeight = displayMetrics.heightPixels / density;
              float dpWidth  = displayMetrics.widthPixels / density;
      
              Log.d(TAG, "OmesChecka R: "+"second width:"+dpWidth+"second h:"+dpHeight);
      
          }else {
      
              Display display = getWindowManager().getDefaultDisplay();
              DisplayMetrics outMetrics = new DisplayMetrics ();
              display.getMetrics(outMetrics);
      
              float density  = getResources().getDisplayMetrics().density;
              float dpHeight = outMetrics.heightPixels / density;
              float dpWidth  = outMetrics.widthPixels / density;
      
              Log.d(TAG, "OmesChecka: "+"second width:"+dpWidth+"second h:"+dpHeight);
          }
      

      【讨论】:

        【解决方案6】:

        由于我必须去几个地方才能得到完整的答案,所以在一篇文章中: getDefaultDisplay 方法自 Android API 30 (Android 11) 起已弃用,但仍然有效。

        即便如此,由于它已被贬值,您应该使用适用于 Android 11 的新方法,并仍然实施适用于 Android 10 或更早版本的旧方法。

        这是带有 cmets 的代码:(要运行它,您需要在 build.gradle 中启用 viewBinding(因为我没有使用 findViewByID),一个带有名为“main_activity”的顶级布局的 activity_main.xml,一个按钮视图命名为“calculate_button”,三个文本视图命名为“android_version”、“screen_height”和“screen_width”。)

        package com.example.test
        
        import androidx.appcompat.app.AppCompatActivity
        import android.os.Bundle
        import android.util.DisplayMetrics
        import android.view.WindowManager
        import com.example.test.databinding.ActivityMainBinding
        
        class MainActivity : AppCompatActivity() {
            private lateinit var binding: ActivityMainBinding
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                binding = ActivityMainBinding.inflate(layoutInflater)
                setContentView(binding.root)
                binding.calculateButton.setOnClickListener { calcScreenSize() }
            }
        
            private fun calcScreenSize() {   //All in one function to calculate and display screen size in pixels 
                val metrics = DisplayMetrics()
                var width: Int = 0
                var height: Int = 0
                val version = android.os.Build.VERSION.SDK_INT   //Check android version. Returns API # ie 29 (Android 10), 30 (Android 11)
                if (version >= android.os.Build.VERSION_CODES.R) {    //If API is version 30 (Android 11) or greater, use the new method for getting width/height
                    binding.androidVersion.setText("Android API Version: $version")
                    binding.mainActivity.display?.getRealMetrics(metrics) //New method
                    width = metrics.widthPixels
                    height = metrics.heightPixels
                    binding.screenHeight.setText("Height: $height")  //Display Width and Height in Text Views
                    binding.screenWidth.setText("Width: $width")
        
                } else {
                    binding.androidVersion.setText("Android API Version: $version")  //If API is less than version 30 (ie 29 (Android 10)), use the old method for getting width/height
                    @Suppress("DEPRECATION")    //Suppress the "Deprecation" warning
                    windowManager.defaultDisplay.getMetrics(metrics) //Old method
                    width = metrics.widthPixels
                    height = metrics.heightPixels
                    binding.screenHeight.setText("Height: $height") //Display Width and Height in Text Views
                    binding.screenWidth.setText("Width: $width")
        
                }
        
            }
        }
        

        【讨论】:

          【解决方案7】:

          在 Api Level 31 中,Display.getRealMetrics() 方法也被弃用了。推荐的方式是使用WindowManager#getCurrentWindowMetrics()。我更喜欢以下方法来获取屏幕尺寸:

          object ScreenSizeCompat {
              private val api: Api =
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ApiLevel30()
                  else Api()
          
              /**
               * Returns screen size in pixels.
               */
              fun getScreenSize(context: Context): Size = api.getScreenSize(context)
          
              @Suppress("DEPRECATION")
              private open class Api {
                  open fun getScreenSize(context: Context): Size {
                      val display = context.getSystemService(WindowManager::class.java).defaultDisplay
                      val metrics = if (display != null) {
                          DisplayMetrics().also { display.getRealMetrics(it) }
                      } else {
                          Resources.getSystem().displayMetrics
                      }
                      return Size(metrics.widthPixels, metrics.heightPixels)
                  }
              }
          
              @RequiresApi(Build.VERSION_CODES.R)
              private class ApiLevel30 : Api() {
                  override fun getScreenSize(context: Context): Size {
                      val metrics: WindowMetrics = context.getSystemService(WindowManager::class.java).currentWindowMetrics
                      return Size(metrics.bounds.width(), metrics.bounds.height())
                  }
              }
          }
          

          例如,要获取屏幕高度,请在Activity 中使用它:

          ScreenSizeCompat.getScreenSize(this).height
          

          【讨论】:

            【解决方案8】:

            我使用DisplayCompatManager 在android R-Above 上获取宽度和高度,并使用DisplayMatrics 在其他android 版本上获取它。

            所以,这是我的代码(+ @Suppress("DEPRECATION"))

            private fun screenValue() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            
                    val defaultDisplay =
                        DisplayManagerCompat.getInstance(this).getDisplay(Display.DEFAULT_DISPLAY)
                    val displayContext = createDisplayContext(defaultDisplay!!)
            
                    width = displayContext.resources.displayMetrics.widthPixels
                    height = displayContext.resources.displayMetrics.heightPixels
            
                    Log.e(tag, "width (ANDOIRD R/ABOVE): $width")
                    Log.e(tag, "height (ANDOIRD R/ABOVE) : $height")
            
                } else {
            
                    val displayMetrics = DisplayMetrics()
                    @Suppress("DEPRECATION")
                    windowManager.defaultDisplay.getMetrics(displayMetrics)
            
                    height = displayMetrics.heightPixels
                    width = displayMetrics.widthPixels
            
                    Log.e(tag, "width (BOTTOM ANDROID R): $width")
                    Log.e(tag, "height (BOTTOM ANDROID R) : $height")
            
                }
            }
            

            If you want see my gist in github

            【讨论】:

              【解决方案9】:

              与其那样做,不如这样做,省去编写更多代码的麻烦。

              val dialog = super.onCreateDialog(savedInstanceState)
              dialog.setOnShowListener {
                  val bottomSheetDialog = it as BottomSheetDialog
                  val parentLayout =
                      bottomSheetDialog.findViewById<View>(R.id.design_bottom_sheet)
                  parentLayout?.let { bottomSheet ->
                      val behaviour = BottomSheetBehavior.from(bottomSheet)
                      val layoutParams = bottomSheet.layoutParams
                      layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
                      bottomSheet.layoutParams = layoutParams
                      behaviour.state = BottomSheetBehavior.STATE_EXPANDED
                  }
              }
              return dialog
              

              【讨论】:

              • 约翰,我认为你的答案属于另一个问题。这个问题是关于在 getDefaultDisplay 已被弃用的情况下该怎么做。您的答案似乎与对话框有关。
              • @acarlstein 我刚刚展示了最好的方法。我没有使用defaultDisplay,而是向他展示了一种更好的方法。他正在使用defaultDisplay 来获取屏幕的尺寸。在我的示例中,我使用父布局来使布局的高度与父布局匹配。
              • 问题与对话无关。
              • @Shark 你在说什么。你看到我对@acarlstein 的回答了吗?
              • 是的,它不提供替代已弃用的getDefaultDisplay()。实际上,让我发布一个答案。
              猜你喜欢
              • 2021-10-01
              • 2020-04-02
              • 1970-01-01
              • 2022-07-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-02-21
              • 1970-01-01
              相关资源
              最近更新 更多