Android静态,动态壁纸,主题,锁屏简单实现

751 阅读6分钟

4f811a1c-0d03-4df9-8a91-f358682b4760.jpg

给设备开发一套精美的壁纸也是一件不容易的事情

一、前言

在Android设备中壁纸的存在有一定的意义:主要体现在以下几个方面:

  1. 增强用户归属感和个性化表达‌:个性化壁纸能够帮助用户在众多设备中找到自己的位置,尤其是在公共场合或多人聚会时,独特的壁纸背景成为了个人身份的象征,增强了用户的归属感‌。此外,通过选择不同的壁纸背景,用户可以展示自己的审美品味和兴趣爱好,从而在社交场合中留下深刻印象‌。
  2. 提升使用体验和心情愉悦‌:一个美观、和谐的壁纸能够显著提升用户的整体使用体验。每当用户解锁手机时,看到心仪的壁纸,心情都会随之愉悦‌1。此外,治愈系的壁纸能够有效舒缓心中的焦虑和压力,看到喜欢的图案时,心情仿佛得到了一次温暖的拥抱,感受到生活的美好‌。
  3. 记录生活点滴和情感寄托‌:手机壁纸不仅是视觉上的装饰,更是情感和记忆的载体。用户可以选择自己或亲朋好友的照片作为壁纸,展现个人生活,增加手机与用户之间的情感联系‌13。许多用户还会选择一些激励人心的名言或励志句子作为屏保,时刻提醒自己追求目标,保持积极向上的态度‌。
  4. 文化与节日的象征‌:在不同节日或特殊时刻,更换手机壁纸也成为一种庆祝方式。例如,新年时更换喜庆的烟花、生肖图案等壁纸,既是对过去一年的告别,也是对未来的美好期许,体现了人们对时间流转的敏感与尊重‌4。
  5. 技术进步与用户体验的融合‌:随着技术的发展,Android系统提供了丰富的壁纸资源,包括静态和动态壁纸、布景主题等,用户可以根据自己的喜好选择合适的壁纸,使手机整体风格更加协调‌15。此外,息屏常显(AOD)壁纸功能也让常亮屏幕更个性化,避免了单调的黑色背景‌。

今天主要分享一下:
壁纸,主题,锁屏是如何开发的?
主要包含:

  1. 静态壁纸的开发
  2. 动态壁纸的开发
  3. 主题包开发
  4. 锁屏开发

二、 静态壁纸

静态壁纸开发其实很简单:

  1. 需要注册壁纸设置权限:
<uses-permission android:name="android.permission.SET_WALLPAPER" />

2. 通过图片bitmap,或者resource直接设置:

val resources = context.resources
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.ava2)
WallpaperManager.getInstance(context).setBitmap(bitmap)

三、动态壁纸

动态壁纸的开发相对静态壁纸开发稍微麻烦了一点点
1. 注册权限相关配置

  <uses-permission android:name="android.permission.SET_WALLPAPER" />
  <queries>
        <intent>
            <action android:name="android.service.wallpaper.WallpaperService" />
        </intent>
  </queries>

2. 注册动态壁纸服务类

<service
    android:name=".wallpager.WXWallpaperService"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.BIND_WALLPAPER">
    <intent-filter>
        <action android:name="android.service.wallpaper.WallpaperService" />
    </intent-filter>

    <meta-data
        android:name="android.service.wallpaper"
        android:resource="@xml/wx_wallpaper" />
</service>
  • 配置壁纸resource资源
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/app_name"
    android:thumbnail="@drawable/ava3" />

3. 编写壁纸服务类代码:继承 WallpaperService 类创建动态壁纸服务,通过 Engine 实现动态渲染逻辑,如下:


class WXWallpaperService : WallpaperService() {

    override fun onCreateEngine() = MyWallpaperEngine()

    inner class MyWallpaperEngine : Engine() {
        private val handler = Handler(Looper.getMainLooper())
        private val drawRunner = Runnable { draw() }
        private var visible = false

        override fun onVisibilityChanged(visible: Boolean) {
            this.visible = visible
            if (visible) handler.post(drawRunner)
            else handler.removeCallbacks(drawRunner)
        }

        private fun draw() {
            var canvas: Canvas? = null
            try {
                canvas = surfaceHolder.lockCanvas()
                canvas.apply {
                    drawColor(Color.rgb((0..255).random(), (0..255).random(), (0..255).random()))
                }
            } finally {
                canvas?.let { surfaceHolder.unlockCanvasAndPost(it) }  // 必须解锁
            }
            handler.removeCallbacks(drawRunner)
            if (visible) handler.postDelayed(drawRunner, 1000)
        }
    }
}

4. 若需支持视频转动态壁纸功能,可集成 MediaPlayer 实现解码,需处理视频分辨率适配(建议限制为 4K 以下)。

surfaceHolder.addCallback(object : SurfaceHolder.Callback {
   override fun surfaceCreated(holder: SurfaceHolder) {
       mediaPlayer.setSurface(holder.surface)
       mediaPlayer.prepareAsync()
   }
})

5. 设置壁纸生效:
1)自 Android 12 起,Google 为增强安全性,禁止应用直接通过代码设置动态壁纸组件,必须通过系统壁纸选择器交互完成,只能使用 ACTION_CHANGE_LIVE_WALLPAPER Intent 跳转系统界面,由用户手动确认设置

val intent = Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER).putExtra(
    WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, ComponentName(context, WXWallpaperService::class.java)
)
context.startActivity(intent)

2)Android 12 以下,可通过setWallpaperComponent设置替换系统壁纸,需要添加权限:

 <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />

然后设置生效:

WallpaperManager.getInstance(context).setWallpaperComponent(ComponentName(context, WXWallpaperService::class.java))

四、主题开发

主题开发里面包含了:

  1. 壁纸开发
  2. 还包含主题包开发
  3. 锁屏样式定制
  • 在 res/values/styles.xml 中定义 Theme.LockScreen 主题属性,也可以定义其他SystemUI相关主题熟悉
<style name="Theme.LockScreen" parent="Theme.DeviceDefault"> 
   <item name="android:windowBackground">@drawable/lock_bg</item> 
   <item name="android:keyguardPreviewLayout">@layout/custom_lock</item> 
</style>
  • 通过共享存储实现主题切换:
object ThemeManager {
    private const val KEY_BG = "lock_bg"
    private const val KEY_STYLE = "lock_style"

    fun applyTheme(context: Context, bgResId: Int) {
        context.getSharedPreferences("theme_prefs", MODE_PRIVATE).edit {
            putInt(KEY_BG, bgResId)
            apply()
        }
        context.sendBroadcast(Intent("LOCK_THEME_CHANGED"))
    }

    fun getCurrentBg(context: Context): Int {
        return context.getSharedPreferences("theme_prefs", MODE_PRIVATE)
            .getInt(KEY_BG, R.drawable.default_bg)
    }
}

五、锁屏样式开发

系统锁屏开发涉及到KeyguardService这个类,KeyguardService是Android系统中管理锁屏界面的核心服务类。
它是作为SystemUI组件的一部分运行在system_server进程,
通过KeyguardServiceDelegate进行服务绑定和生命周期管理,
使用bindServiceAsUser方式避免多用户场景下的重复启动问题。
系统源码SystemUI里面有相关锁屏服务代码

class MiKeyguardService : KeyguardService() {
    private lateinit var windowManager: WindowManager
    private lateinit var keyguardView: View

    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        setupKeyguardView()
    }

    private fun setupKeyguardView() {
        val params = WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
            WindowManager.LayoutParams.FLAG_FULLSCREEN or
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
            PixelFormat.TRANSLUCENT
        )
        
        keyguardView = LayoutInflater.from(this)
            .inflate(R.layout.mi_lock, null).apply {
                findViewById<ImageButton>(R.id.mi_unlock).setOnClickListener {
                    disableKeyguard()
                }
            }
        
        windowManager.addView(keyguardView, params)
    }

    override fun onDestroy() {
        windowManager.removeView(keyguardView)
        super.onDestroy()
    }

相关锁屏服务注册配置信息

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.milockscreen">
    
    <uses-permission android:name="android.permission.BIND_KEYGUARD" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
    
    <application>
        <service
            android:name=".MiKeyguardService"
            android:label="MIUI Custom Lock"
            android:permission="android.permission.BIND_KEYGUARD">
            <intent-filter>
                <action android:name="android.service.keyguard.KeyguardService" />
            </intent-filter>
        </service>
    </application>
</manifest>
  • 三方锁屏开发步骤:
    1. 新建普通service服务,下面用compose方式实现:

class WXKeyguardService : LifecycleService(), SavedStateRegistryOwner, ViewModelStoreOwner {

    /**
     * Build our saved state registry controller
     */
    private val savedStateRegistryController: SavedStateRegistryController by lazy(LazyThreadSafetyMode.NONE) {
        SavedStateRegistryController.create(this)
    }

    /**
     * Build our view model store
     */
    private val internalViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

    /**
     * Context dedicated to the view
     */
    private val overlayContext: Context by lazy {
        val defaultDisplay: Display = getSystemService(DisplayManager::class.java).getDisplay(Display.DEFAULT_DISPLAY)
        createDisplayContext(defaultDisplay).createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, null)
    }


    override val savedStateRegistry: SavedStateRegistry
        get() = savedStateRegistryController.savedStateRegistry

    override val viewModelStore: ViewModelStore
        get() = internalViewModelStore

    // Access our window manager
    private val windowManager by lazy {
        overlayContext.getSystemService(WindowManager::class.java)
    }

    private val composeView by lazy {
        ComposeView(overlayContext)
    }
    private val screenOffReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            when (intent.action) {
                Intent.ACTION_SCREEN_OFF -> showLockScreen()
                Intent.ACTION_USER_PRESENT -> removeLockScreen()
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        savedStateRegistryController.performRestore(null)
        composeView.setViewTreeLifecycleOwner(this)
        composeView.setViewTreeViewModelStoreOwner(this)
        composeView.setViewTreeSavedStateRegistryOwner(this)
        composeView.setContent { Content() }
        registerReceiver(screenOffReceiver, IntentFilter(Intent.ACTION_SCREEN_OFF))
    }

    // 在Service或Activity中执行
    private fun disableSystemKeyguard() {
        val keyguardManager = getSystemService(KEYGUARD_SERVICE) as KeyguardManager
        val lock = keyguardManager.newKeyguardLock("CustomLock")
        lock.disableKeyguard()  // 需SYSTEM_ALERT_WINDOW和DISABLE_KEYGUARD权限 :ml-citation{ref="3" data="citationList"}
    }

    private fun showLockScreen() {
        disableSystemKeyguard()
        val flag: Int = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
                WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
        val params = WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
            PixelFormat.TRANSLUCENT
        )
        windowManager.addView(composeView, params)
    }

    private fun removeLockScreen() {
        windowManager.removeView(composeView)
    }

    @Composable
    private fun Content() {//示例作品就写了一张图片
        Box(
            modifier = Modifier
                .fillMaxSize()
                .clickable {
                    removeLockScreen()
                }
                .background(Color.Red)
        ) {
            Image(
                modifier = Modifier
                    .wrapContentHeight()
                    .wrapContentWidth(), painter = painterResource(id = R.drawable.ava2), contentDescription = ""
            )
        }
    }

    override fun onDestroy() {
        unregisterReceiver(screenOffReceiver)
        windowManager.removeView(composeView)
        super.onDestroy()
    }
}

2. 配置服务注册和相关权限

    <service
        android:name=".lockscreen.WXKeyguardService"
        android:enabled="true"
        android:exported="true">
    </service>

注册悬浮窗权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

申请悬浮窗权限

if (!Settings.canDrawOverlays(context)) {
    (context as Activity).startActivityForResult(
        Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
            data = "package:${context.packageName}".toUri()
        }, 3000
    )
} else {
    startService(Intent(context, WXKeyguardService::class.java))
}

六、总结

本文主要分享了Android中 静态壁纸的开发, 动态壁纸的开发。 主题包开发, 锁屏开发等

感谢阅读:

欢迎用你发财的小手 关注,点赞、收藏

这里你会学到不一样的东西