El fondo de pantalla de Android sigue siendo divertido en la estación B

La función de configurar fondos de pantalla del sistema en realidad no tiene muchas escenas para las aplicaciones de la capa de aplicación, pero en las actividades circundantes de algunas escenas, es de hecho una forma de mejorar la permanencia de la marca, al igual que los hermosos fondos de pantalla de los personajes creados en una determinada figura de actividad. estos pueden agregar una función de configuración de fondo de pantalla.

Desde el Android original, el sistema admite la configuración de fondos de pantalla de dos maneras, una es un fondo de pantalla estático y la otra es un fondo de pantalla dinámico.

papel pintado estático

No hay nada que decir sobre el fondo de pantalla estático, y se hace con una línea de código a través de la API proporcionada por el sistema.

El código más simple se muestra a continuación.

val wallpaperManager = WallpaperManager.getInstance(this)
try {
    val bitmap = ContextCompat.getDrawable(this, R.drawable.ic_launcher_background)?.toBitmap()
    wallpaperManager.setBitmap(bitmap)
} catch (e: Exception) {
    e.printStackTrace()
}
复制代码

Además de setBitmap, el sistema también proporciona setResource, setStream, un total de tres formas de configurar un fondo de pantalla estático.

Los tres métodos tienen el mismo objetivo, todos establecen un mapa de bits en la API del sistema.

fondo de pantalla dinámico

Los fondos de pantalla en vivo son un poco interesantes. Muchas ROM de teléfonos móviles también tienen fondos de pantalla en vivo incorporados. No creas que estas son características nuevas. Desde Android 1.5, este método ha sido compatible. Es solo que hay menos personas haciéndolo, principalmente porque no hay escenas particularmente adecuadas y los fondos de pantalla dinámicos consumen más energía que los fondos de pantalla estáticos, por lo que la mayoría de las veces no usamos este método.

Como un servicio del sistema, cuando el sistema se inicia, ya sea un fondo de pantalla dinámico o estático, se ejecutará en segundo plano como un servicio: WallpaperService, su tipo de ventana es TYPE_WALLPAPER y WallpaperService proporciona un SurfaceHolder para exponerlo al mundo exterior. Se renderiza la imagen, que es el principio básico para configurar el fondo de pantalla.

Para crear un fondo de pantalla dinámico, debe heredar el WallpaperService del sistema y proporcionar un WallpaperService.Engin para renderizar. El siguiente es un código de plantilla.

class MyWallpaperService : WallpaperService() {
    override fun onCreateEngine(): Engine = WallpaperEngine()

    inner class WallpaperEngine : WallpaperService.Engine() {
        lateinit var mediaPlayer: MediaPlayer

        override fun onSurfaceCreated(holder: SurfaceHolder?) {
            super.onSurfaceCreated(holder)
        }

        override fun onCommand(action: String?, x: Int, y: Int, z: Int, extras: Bundle?, resultRequested: Boolean): Bundle {
            try {
                Log.d("xys", "onCommand: $action----$x---$y---$z")
                if ("android.wallpaper.tap" == action) {
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return super.onCommand(action, x, y, z, extras, resultRequested)
        }

        override fun onVisibilityChanged(visible: Boolean) {
            if (visible) {
            } else {
            }
        }

        override fun onDestroy() {
            super.onDestroy()
        }
    }
}
复制代码

Luego registre el Servicio en el manifiesto.

<service
    android:name=".MyWallpaperService"
    android:exported="true"
    android:label="Wallpaper"
    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/my_wallpaper" />
</service>
复制代码

Además, debe solicitar los permisos correspondientes.

<uses-permission android:name="android.permission.SET_WALLPAPER" />
复制代码

Finalmente, agregue un archivo de descripción en la carpeta xml, correspondiente al archivo en la etiqueta de recurso anterior.

<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/app_name"
    android:thumbnail="@mipmap/ic_launcher" />
复制代码

Los fondos de pantalla en vivo solo se pueden configurar a través de la interfaz de vista previa del fondo de pantalla del sistema.

val localIntent = Intent()
localIntent.action = WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER
localIntent.putExtra(
    WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
    ComponentName(applicationContext.packageName, MyWallpaperService::class.java.name))
startActivity(localIntent)
复制代码

Entonces podemos establecer un fondo de pantalla en vivo.

jugar con flores

Dado que el SurfaceHolder proporcionado se usa para renderizar, todas nuestras escenas que pueden usar el SurfaceHolder se pueden usar para crear fondos de pantalla dinámicos.

一般来说,有三种比较常见的使用场景。

  • MediaPlayer
  • Camera
  • SurfaceView

这三种也是SurfaceHolder的常用使用场景。

首先来看下MediaPlayer,这是最简单的方式,可以设置一个视频,在桌面上循环播放。

inner class WallpaperEngine : WallpaperService.Engine() {
    lateinit var mediaPlayer: MediaPlayer

    override fun onSurfaceCreated(holder: SurfaceHolder?) {
        super.onSurfaceCreated(holder)
        mediaPlayer = MediaPlayer.create(applicationContext, R.raw.testwallpaper).also {
            it.setSurface(holder!!.surface)
            it.isLooping = true
        }
    }

    override fun onVisibilityChanged(visible: Boolean) {
        if (visible) {
            mediaPlayer.start()
        } else {
            mediaPlayer.pause()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mediaPlayer.isPlaying) {
            mediaPlayer.stop()
        }
        mediaPlayer.release()
    }
}
复制代码

接下来,再来看下使用Camera来刷新Surface的。

inner class WallpaperEngine : WallpaperService.Engine() {
    lateinit var camera: Camera

    override fun onVisibilityChanged(visible: Boolean) {
        if (visible) {
            startPreview()
        } else {
            stopPreview()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        stopPreview()
    }

    private fun startPreview() {
        camera = Camera.open()
        camera.setDisplayOrientation(90)
        try {
            camera.setPreviewDisplay(surfaceHolder)
            camera.startPreview()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    private fun stopPreview() {
        try {
            camera.stopPreview()
            camera.release()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}
复制代码

同时需要添加下Camera的权限。

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

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
复制代码

由于这里偷懒,没有使用最新的CameraAPI,也没有动态申请权限,所以你需要自己手动去授权。

最后一种,通过Surface来进行自绘渲染。

val holder = surfaceHolder
var canvas: Canvas? = null
try {
    canvas = holder.lockCanvas()
    if (canvas != null) {
    		canvas.save()
        // Draw Something
    }
} finally {
    if (canvas != null) holder.unlockCanvasAndPost(canvas)
}
复制代码

这里就可以完全使用Canvas的API来进行绘制了。

这里有一个比较复杂的绘制Demo,可以给大家参考。

www.developer.com/design/buil…

有意思的方法

虽然WallpaperService是一个系统服务,但它也提供了一些比较有用的回调函数来帮助我们做一些有意思的东西。

onOffsetsChanged

当用户在手机桌面滑动时,有的壁纸图片会跟着左右移动,这个功能就是通过这个回调来实现的,在手势滑动的每一帧都会回调这个方法。

xOffset:x轴滑动的百分比

yOffset:y轴滑动百分比

xOffsetStep:x轴桌面Page数进度

yOffsetStep:y轴桌面Page数进度

xPixelOffset:x轴像素偏移量

通过这个函数,就可以拿到手势的移动惯量,从而对图片做出一些修改。

onTouchEvent、onCommand

这两个方法,都可以获取用户的点击行为,通过判断点击类型,就可以针对用户的特殊点击行为来做一些逻辑处理,例如点击某些特定的地方时,唤起App,或者打开某个界面等等。

class MyWallpaperService : WallpaperService() {
  override fun onCreateEngine(): Engine = WallpaperEngine()

  private inner class WallpaperEngine : WallpaperService.Engine() {

    override fun onTouchEvent(event: MotionEvent?) {
      // on finder press events
      if (event?.action == MotionEvent.ACTION_DOWN) {
        // get the canvas from the Engine or leave
        val canvas = surfaceHolder?.lockCanvas() ?: return
        // TODO
        // update the surface
        surfaceHolder.unlockCanvasAndPost(canvas)
      }
    }
  }
}
复制代码

B站怎么玩的呢

不得不说,B站在这方面玩的是真的花,最近B站里面新加了一个异想少女系列,你可以设置一个动态壁纸,同时还带交互,有点意思。

其实类似这样的交互,基本上都是通过OpenGL或者是RenderScript来实现的,通过GLSurfaceView来进行渲染,从而实现了一些复杂的交互,下面这些例子,就是一些实践。

github.com/PavelDoGrea…

github.com/jinkg/live-…

www.cnblogs.com/YFEYI/categ…

code.tutsplus.com/tutorials/c…

但是B站的这个效果,显然比上面的方案更加成熟和完整,所以,通过调研可以发现,它们使用的是Live2D的方案。

www.live2d.com/

动态壁纸的Demo如下。

github.com/Live2D/Cubi…

这个东西是小日子的一个SDK,专业做2D可交互纸片人,这个东西已经出来很久了,前端之前用它来做网页的看板娘,现在客户端又拿来做动态壁纸,风水轮流换啊,想要使用的,可以参考它们官方的Demo。

但是官方的动态壁纸Demo在客户端是有Bug的,会存在各种闪的问题,由于我本身不懂OpenGL,所以也无法解决,通过回退Commit,发现可以直接使用这个CommitID : Merge pull request #2 from Live2D/create-new-function ,就没有闪的问题。

a9040ddbf99d9a130495e4a6190592068f2f7a77

好了,B站YYDS,但我觉得这东西的使用场景太有限了,而且特别卡,极端影响功耗,所以,要不要这么卷呢,你看着办吧。

Supongo que te gusta

Origin juejin.im/post/7135998448720936968
Recomendado
Clasificación