返回

从WindowManager的手铐中释放应用程序

Android

Android多屏开发的挑战及解决之道

随着Android设备的不断涌现,从智能手机到平板电脑,再到可折叠设备,应用程序开发面临着日益严峻的挑战:如何适配不同的屏幕尺寸和状态。

传统上,应用程序依赖WindowManager来管理窗口,但它在多屏环境下存在局限性,无法跟踪窗口状态,难以响应屏幕变化。

应对措施:跨设备优化策略

Android推出了多项技术来解决多屏环境的挑战:

1. 分屏模式(Split-Screen Mode): 允许同时运行两个应用程序。
2. 自由窗口模式(Freeform Window Mode): 允许用户自由移动和调整窗口大小。
3. 显示方向变更(Display Orientation Changes): 允许改变设备的显示方向。

WindowInset 与布局变化监听

为了提高应用程序对不同屏幕尺寸和状态的适应性,可利用WindowInset和布局变化监听器:

WindowInset: 提供窗口周围区域(如状态栏、导航栏)的信息,帮助应用程序调整布局以避免遮挡。

布局变化监听器: 通知应用程序布局变化(如设备旋转或键盘弹出),以便及时调整布局或更新UI。

实践:利用WindowManager的全新API跨设备优化

屏幕变化监听: 监听屏幕旋转、键盘弹出等变化,重新调整布局或更新UI。

分屏模式支持: 使用WindowManager的API支持分屏模式,同时运行多个窗口。

自由窗口模式支持: 支持自由窗口模式,允许用户自由移动和调整窗口大小。

结语

合理利用WindowManager的API,可以提升应用程序在不同屏幕尺寸和状态下的适应性,为用户带来更好的体验。

常见问题解答

1. 如何在应用程序中实现分屏模式?

package com.example.multiscreenapp

import android.app.Activity
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            window.setActivitySplitScreen(Activity.SPLIT_SCREEN_PREVENTED)
        }
    }
}

2. 如何监听屏幕变化?

package com.example.multiscreenapp

import android.app.Activity
import android.content.res.Configuration
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        addConfigurationChangeListener(this)
    }

    private fun addConfigurationChangeListener(activity: Activity) {
        activity.addOnConfigurationChangedListener(object : Configuration.OnConfigurationChangedListener {
            override fun onConfigurationChanged(newConfig: Configuration) {
                // 处理屏幕变化,例如:
                if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    // 屏幕为横向
                } else {
                    // 屏幕为纵向
                }
            }
        })
    }
}

3. 如何在自由窗口模式下支持应用程序?

package com.example.multiscreenapp

import android.app.Activity
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.WindowManager

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
        }
    }
}

4. 如何利用WindowInset调整布局?

package com.example.multiscreenapp

import android.graphics.Insets
import android.os.Build
import android.view.WindowInsets
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val windowInsetsController = window.insetsController
            if (windowInsetsController != null) {
                // 隐藏系统UI,包括状态栏和导航栏
                windowInsetsController.systemBarsBehavior = WindowInsets.Behavior.SHOW_TRANSIENT_BARS_BY_SWIPE
            }
        }
    }

    fun adjustLayout(view: View, windowInsets: WindowInsets) {
        // 获取窗口内边距,即状态栏和导航栏的高度
        val insets: Insets = windowInsets.systemWindowInsets
        // 调整布局,避免被内边距遮挡
        view.setPadding(0, insets.top, 0, insets.bottom)
    }
}

5. 如何使用布局变化监听器?

package com.example.multiscreenapp

import android.graphics.Insets
import android.os.Build
import android.view.WindowInsets
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            WindowCompat.setDecorFitsSystemWindows(window, false)
        }

        val contentView = findViewById<View>(android.R.id.content)
        ViewCompat.setOnApplyWindowInsetsListener(contentView) { view, windowInsets ->
            adjustLayout(view, windowInsets)
            WindowInsets.CONSUMED
        }
    }

    fun adjustLayout(view: View, windowInsets: WindowInsets) {
        // 获取窗口内边距,即状态栏和导航栏的高度
        val insets: Insets = windowInsets.systemWindowInsets
        // 调整布局,避免被内边距遮挡
        view.setPadding(0, insets.top, 0, insets.bottom)
    }
}