Android 静态壁纸设置实现方案

news2025/3/28 5:56:58

提示:Android 平台,静态壁纸实现方案

文章目录

  • 需求:Android 实现壁纸 设置
    • 场景
  • 参考资料
  • 实现方案
    • 直接调用系统 API,WallpaperManager 来实现 wallpaperManager.setResource
    • 系统源码分析
      • 系统app WallpaperPicker
      • WallpaperPickerActivity ->WallpaperCropActivity
        • onClick 底部壁纸图片点击事件
      • AlphaDisableableButton
        • actionbar_set_wallpaper.xml
      • DialogUtils
        • DialogUtils ->executeCropTaskAfterPrompt
      • WallpaperManagerCompat
      • WallpaperPicker 源码小结
    • WallpaperManager
  • 拓展
    • 放置默认壁纸 供选择
    • 配置壁纸资源包
    • 加载壁纸配置
  • 总结


需求:Android 实现壁纸 设置

最近看到一个友商实现了一个功能,壁纸作为单独apk 拎出来作为一个apk 单独出现形式,比较有意义。 自己也实现一个,实现需求同时,分析源码实现方案 和 流程。

#需求
将壁纸设置作为一个apk形式,实现壁纸设置功能

场景

很多平板方案,将这个功能单独实现,作为一个app,方便客户使用。
这里只是用一个静态壁纸设置实现的方式,来初步了解Android壁纸相关内容。

参考资料

android WallpaperPicker7.0源码分析
Android 切换壁纸代码流程追踪:
Android 12新特性之获取壁纸主色调并设置系统主题色

实现方案

直接调用系统 API,WallpaperManager 来实现 wallpaperManager.setResource

WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
try {
	wallpaperManager.setResource(R.drawable.picture);
} catch (IOException e) {
	e.printStackTrace();
}

设置静态壁纸有很多途径,但归根结底都是一下三种方法:

  • 使用WallpaperManager的setResource(int ResourceID)方法
  • 使用WallpaperManager的setBitmap(Bitmap bitmap)方法
  • 使用WallpaperManager的setStream(InputStream data)方法

可以参考WallpaperManager 源码分析,源码代码量还好,不多。

\frameworks\base\core\java\android\app\WallpaperManager.java 

在这里插入图片描述

系统源码分析

系统app WallpaperPicker

我们先看一下如何进入到WallpaperPicker 的, 实际上 我们设置壁纸两个入口:

  • 首页 长按,设置壁纸
  • 进入设置目录,进入壁纸设置【接下来从设置入口进行分析】
    在这里插入图片描述

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/a1222db5ee604392b1faf8bdb67ace90.png

在这里插入图片描述

在这里插入图片描述

一路跟踪界面,我们发现了以下几个类,可以看出最终是从设置跳转到了一个wallpaperpicker 的包中。

com.android.settings/.Settings$WallpaperSettingsActivity 
com.android.wallpaperpicker/.WallpaperPickerActivity  

系统里面搜索结果如下:
在这里插入图片描述
可以肯定的是 wallpaperpicker 就是一个单独apk, 将这个apk 源码进行分析下。

WallpaperPickerActivity ->WallpaperCropActivity

上面已经分析了,系统对应的壁纸选择跳转到一个apk 进行选择,对应的Activity 为 WallpaperPickerActivity

public class WallpaperPickerActivity extends WallpaperCropActivity
        implements OnClickListener, OnLongClickListener, ActionMode.Callback {
    static final String TAG = "WallpaperPickerActivity";
onClick 底部壁纸图片点击事件

这里突破点是左上角设置壁纸图标,当无壁纸选中 默认情况是不可点击的,下面壁纸选项点击选中时候左上角 设置壁纸按钮可点击了。
看该方法的注释:

Called when a wallpaper tile is clicked 

也就是点击后底部弹框的操作 
 /**
     * Called when a wallpaper tile is clicked
     */
    @Override
    public void onClick(View v) {
        if (mActionMode != null) {
            // When CAB is up, clicking toggles the item instead
            if (v.isLongClickable()) {
                onLongClick(v);
            }
            return;
        }
        WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
        if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
            selectTile(v);
            setWallpaperButtonEnabled(true);
        }
        info.onClick(this);
    }


    public void setWallpaperButtonEnabled(boolean enabled) {
        mSetWallpaperButton.setEnabled(enabled);
    }


 mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
 

在 WallpaperPickerActivity 类中并没有找到 mSetWallpaperButton 定义地方,只是找到加载地方 findViewById,那么去父类 WallpaperCropActivity.java 看看


protected View mSetWallpaperButton;


// WallpaperCropActivity.java  类中,也是通过findViewById 来加载,并声明它是一个View 
mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
 
 那这个set_wallpaper_button 对应的到底是什么,继续找
 

在这里插入图片描述

这里看到了 在一个布局文件和上面所讲WallpaperPickerActivity 、WallpaperCropActivity 两个类加载了。
看看布局文件如下

<com.android.wallpaperpicker.AlphaDisableableButton
    xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/ActionBarSetWallpaperStyle"
    android:id="@+id/set_wallpaper_button"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingRight="20dp"
    android:drawableLeft="@drawable/ic_actionbar_accept"
    android:drawablePadding="8dp"
    android:gravity="start|center_vertical"
    android:text="@string/wallpaper_instructions"
    android:enabled="false" />

具体分析 如下 分析,AlphaDisableableButton 相关内容

AlphaDisableableButton

上面找到了AlphaDisableableButton , 其实就是一个自定义的View,看代码。


/**
 * A Button which becomes translucent when it is disabled
 */
public class AlphaDisableableButton extends Button {
    public static float DISABLED_ALPHA_VALUE = 0.4f;
    public AlphaDisableableButton(Context context) {
        this(context, null);
    }

    public AlphaDisableableButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AlphaDisableableButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setLayerType(LAYER_TYPE_HARDWARE, null);
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        if(enabled) {
            setAlpha(1.0f);
        } else {
            setAlpha(DISABLED_ALPHA_VALUE);
        }
    }
}

备注:AlphaDisableableButton 类源码如上,就是一个自定义View,如上类说明:不可见的时候透明状态。

A Button which becomes translucent when it is disabled 
actionbar_set_wallpaper.xml

上面已经分析到了AlphaDisableableButton 源码和布局文件,那这个xml 又是哪里加载的。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

这里说明WallpaperPickerActivity 、 WallpaperCropActivity 加载这个布局文件,不就是显示ActionBar 嘛,如 加载说明:

Show the custom action bar view

DialogUtils

承接上述分析 ,actionbar 点击后,也就是 设置壁纸按钮 点击,弹出选择框逻辑


  actionBar.getCustomView().setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        actionBar.hide();
                        // Never fade on finish because we return to the app that started us (e.g.
                        // Photos), not the home screen.
                        cropImageAndSetWallpaper(imageUri, null, false /* shouldFadeOutOnFinish */);
                    }
                });




cropImageAndSetWallpaper(imageUri, null, false /* shouldFadeOutOnFinish */);






  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public void cropImageAndSetWallpaper(Uri uri,
            CropAndSetWallpaperTask.OnBitmapCroppedHandler onBitmapCroppedHandler,
            boolean shouldFadeOutOnFinish) {
      。。。。。。。。。。。。。。
        DialogUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
    }

DialogUtils ->executeCropTaskAfterPrompt

接下来就是dialog 具体源码如下:


/**
 * Utility class used to show dialogs for things like picking which wallpaper to set.
 */
public class DialogUtils {
    /**
     * Calls cropTask.execute(), once the user has selected which wallpaper to set. On pre-N
     * devices, the prompt is not displayed since there is no API to set the lockscreen wallpaper.
     *
     * TODO: Don't use CropAndSetWallpaperTask on N+, because the new API will handle cropping instead.
     */
    public static void executeCropTaskAfterPrompt(
            Context context, final AsyncTask<Integer, ?, ?> cropTask,
            DialogInterface.OnCancelListener onCancelListener) {
        if (Utilities.isAtLeastN()) {
            new AlertDialog.Builder(context)
                    .setTitle(R.string.wallpaper_instructions)
                    .setItems(R.array.which_wallpaper_options, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int selectedItemIndex) {
                        int whichWallpaper;
                        if (selectedItemIndex == 0) {
                            whichWallpaper = WallpaperManagerCompat.FLAG_SET_SYSTEM;
                        } else if (selectedItemIndex == 1) {
                            whichWallpaper = WallpaperManagerCompat.FLAG_SET_LOCK;
                        } else {
                            whichWallpaper = WallpaperManagerCompat.FLAG_SET_SYSTEM
                                    | WallpaperManagerCompat.FLAG_SET_LOCK;
                        }
                        cropTask.execute(whichWallpaper);
                    }
                })
                .setOnCancelListener(onCancelListener)
                .show();
        } else {
            cropTask.execute(WallpaperManagerCompat.FLAG_SET_SYSTEM);
        }
    }
}

其实这个类说得很明白了,就是设置壁纸用的。
这里 我们记住 我们选择的是第三个选项,也就是 桌面壁纸和锁屏壁纸,参数flag 不一样而已

   whichWallpaper = WallpaperManagerCompat.FLAG_SET_SYSTEM
                                    | WallpaperManagerCompat.FLAG_SET_LOCK;

WallpaperManagerCompat

DialogUtils 源码分析到了 源码 ,那就继续追踪到 WallpaperManagerCompat

 whichWallpaper = WallpaperManagerCompat.FLAG_SET_SYSTEM
                                    | WallpaperManagerCompat.FLAG_SET_LOCK;
 cropTask.execute(whichWallpaper);



public abstract class WallpaperManagerCompat {
    public static final int FLAG_SET_SYSTEM = 1 << 0; // TODO: use WallpaperManager.FLAG_SET_SYSTEM
    public static final int FLAG_SET_LOCK = 1 << 1; // TODO: use WallpaperManager.FLAG_SET_LOCK

    private static WallpaperManagerCompat sInstance;
    private static final Object sInstanceLock = new Object();

    public static WallpaperManagerCompat getInstance(Context context) {
        synchronized (sInstanceLock) {
            if (sInstance == null) {
                if (Utilities.isAtLeastN()) {
                    sInstance = new WallpaperManagerCompatVN(context.getApplicationContext());
                } else {
                    sInstance = new WallpaperManagerCompatV16(context.getApplicationContext());
                }
            }
            return sInstance;
        }
    }

    public abstract void setStream(InputStream stream, Rect visibleCropHint, boolean allowBackup,
            int whichWallpaper) throws IOException;

    public abstract void clear(int whichWallpaper) throws IOException;
}

发现这里 WallpaperManagerCompat 类还是一个抽象类,那么实际的控制其实是 WallpaperManagerCompatVN 、WallpaperManagerCompatV16, 继续看其中代码如下


public class WallpaperManagerCompatV16 extends WallpaperManagerCompat {
    protected WallpaperManager mWallpaperManager;

    public WallpaperManagerCompatV16(Context context) {
        mWallpaperManager = WallpaperManager.getInstance(context.getApplicationContext());
    }

    @Override
    public void setStream(InputStream data, Rect visibleCropHint, boolean allowBackup,
            int whichWallpaper) throws IOException {
        mWallpaperManager.setStream(data);
    }

    @Override
    public void clear(int whichWallpaper) throws IOException {
        mWallpaperManager.clear();
    }
}




public class WallpaperManagerCompatVN extends WallpaperManagerCompatV16 {
    public WallpaperManagerCompatVN(Context context) {
        super(context);
    }

    @Override
    public void setStream(final InputStream data, Rect visibleCropHint, boolean allowBackup,
            int whichWallpaper) throws IOException {
        try {
            // TODO: use mWallpaperManager.setStream(data, visibleCropHint, allowBackup, which)
            // without needing reflection.
            Method setStream = WallpaperManager.class.getMethod("setStream", InputStream.class,
                    Rect.class, boolean.class, int.class);
            setStream.invoke(mWallpaperManager, data, visibleCropHint, allowBackup, whichWallpaper);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            // Fall back to previous implementation (set both)
            super.setStream(data, visibleCropHint, allowBackup, whichWallpaper);
        }
    }

    @Override
    public void clear(int whichWallpaper) throws IOException {
        try {
            // TODO: use mWallpaperManager.clear(whichWallpaper) without needing reflection.
            Method clear = WallpaperManager.class.getMethod("clear", int.class);
            clear.invoke(mWallpaperManager, whichWallpaper);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            // Fall back to previous implementation (set both)
            super.clear(whichWallpaper);
        }
    }
}


哈哈,这里最终调用的其实是 WallpaperManagerCompatVNWallpaperManagerCompatV16  类的 setStream 方法。 再最终调用的就是 WallpaperManager 类的 setStream 方法。 




WallpaperPicker 源码小结

上面的代码流程分析,最终共调用到了Framework层的WallpaperManager类的 setStream 方法。

WallpaperManager

上面已经分析到了 最终调用到 WallpaperManager 服务,通过反射调用
如下 类说明

/**
 * Provides access to the system wallpaper. With WallpaperManager, you can
 * get the current wallpaper, get the desired dimensions for the wallpaper, set
 * the wallpaper, and more.
 *
 * <p> An app can check whether wallpapers are supported for the current user, by calling
 * {@link #isWallpaperSupported()}, and whether setting of wallpapers is allowed, by calling
 * {@link #isSetWallpaperAllowed()}.
 */
@SystemService(Context.WALLPAPER_SERVICE)
public class WallpaperManager {

.....
}

提供了系统壁纸入口,可以获取当前壁纸、设置壁纸…, app 可以检查当前用户是否允许设置壁纸,是否有权限等。继续分析源码 ,查看部分方法如下
在这里插入图片描述

这里看到设置壁纸的三种类型方法,如上文开始的分析,

归根结底都是一下三种方法:

  • 使用WallpaperManager的setResource(int ResourceID)方法
  • 使用WallpaperManager的setBitmap(Bitmap bitmap)方法
  • 使用WallpaperManager的setStream(InputStream data)方法

尝试三种方案,设置静态壁纸就是很简单的逻辑了,直接调用api ,反射或者拿到framework.jar,调用API 直接调用的事情了。 如下,某个友商做出来的效果,实际效果大家可以自己随便做了。

在这里插入图片描述

拓展

如上通过WallerpaperPicker 包分析了壁纸设置的整体流程,如果只是需要定制壁纸选择需求可以显示图片并调用api 反射或者 api 调用实现需求;
如果需要添加壁纸选择怎么办呢? 那只需要在 app 里面配置资源包即可。
思路如下:

放置默认壁纸 供选择

在 资源包中添加壁纸图片
在这里插入图片描述

配置壁纸资源包

在这里插入图片描述

加载壁纸配置

在这里插入图片描述
在这里插入图片描述

总结

  • 这里实现了静态壁纸设置的方法,就是一个反射或者api 调用。 实际 逻辑比较简单的
  • 这里只是从系统app WallpaperPicker,通过界面,反推实现设置静态壁纸的逻辑和分析代码层面的流程和业务。 实际上 WallpaperPicker App有很多设计思想和架构设计,这里暂不深究。
  • 如果客户定制,需要动态壁纸功能。这里暂不分析,后续有机会实现下,暂不提供解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2321350.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在计算进程D状态持续时间及等IO的时间遇到的一处问题

一、背景 之前的博客 线程每次iodelay监控及D状态开始和结束监控并做堆栈记录-CSDN博客 里&#xff0c;我们讲到了通过内核模块抓取D状态的进程和等IO事件的方法&#xff0c;里面也用到了通过获取rq的symbol&#xff0c;再去获取rq里的rq_clock_task时间的方法&#xff08;内核…

Android11-12-13 替换系统默认壁纸

替换默认壁纸&#xff0c;是客需中再普通不过的需求&#xff0c;这里整理作为笔记记录 文章目录 需求场景 关联资料需求实现拓展总结 需求 客制化客户壁纸需求&#xff0c;替换客户定制的壁纸。 场景 手机-平板相关产品&#xff0c;各种广告机、工控、消费级产品&#xff0c…

Buffer overFolw---Kryo序列化出现缓冲区溢出的问题解决

问题&#xff1a; 由于我的数据量太大&#xff0c;我设置批次为10000万&#xff0c;50w数据大概有400M左右&#xff0c;然后进行spark数据处理时候报错为org.apache.spark.SparkException:Kryo serialization failed:Buffer overFolw.Available:0,rquired 58900977,To …

【Linux】线程基础

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;linux笔记仓 目录 01.背景知识02.线程概念简单使用线程线程调度成本更低 01.背景知识 OS进行内存管理&#xff0c;不是以字节为单位的&#xff0c;而是以内存块为单位的&#xff0c;默认大小为4kb&…

「宇树科技」13家核心零部件供应商梳理!

2025年2月6日&#xff0c;摩根士丹利&#xff08;Morgan Stanley&#xff09;发布最新人形机器人研报&#xff1a;Humanoid 100: Mapping the Humanoid Robot Value Chain&#xff08;人形机器人100&#xff1a;全球人形机器人产业链梳理&#xff09;。 2025年2月20日&#xf…

Spring Boot 项目打包运行

打包成jar包&#xff0c;执行 java -jar 包名&#xff1b; 保证打出的jar包是独立可运行的包&#xff0c;需要xml中添加插件配置 <!-- SpringBoot应用打包插件--> <build><plugins><plugin><groupId>org.springframework.boot</groupId&…

数据结构八股

线性数据结构 数组:数组的内存空间是连续的&#xff0c;随机访问的时间复杂度是01&#xff0c;适用于需要按索引访问元素的场景&#xff0c;但是插入和删除元素较慢&#xff0c;时间复杂度是On链表:链表是由节点组成&#xff0c;节点之间是分散存储的&#xff0c;内存不连续&a…

7.2 控件和组件

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 C#工具箱位于编辑窗口的左侧&#xff0c;它默认内置了大量的控件和组件。控件一般派生于System.Windows.Forms.Control类&#xff0c;显…

transform

http://zhihu.com/question/445556653/answer/3254012065 西科技的文章 视频讲解 小白也能听懂的 transformer模型原理详解 self- attention 多头注意力机制 encoder decoder 机器翻译_哔哩哔哩_bilibili

思库拉水厂开业庆典千人大会回顾

近日,思库拉离子水厂在广州隆重举办了开业盛典,现场汇聚了逾千名嘉宾。此次盛会不仅是对思库拉离子水厂正式投产的庆祝,更是对思库拉品牌未来蓝图的一次展示。 现场氛围热烈,洋溢着浓厚的喜庆气息。参与者来自五湖四海,既有思库拉的忠实拥趸,也有对思库拉产品充满兴趣的潜在消费…

JAVA学习*Object类

Object类 Object类是所有类的父类 类中有一些方法&#xff08;都需要掌握&#xff09; toString()方法 在学习类的对象的时候有介绍过了&#xff0c;当我们重新给此方法就会打印类与对象的信息 equals()方法 在Java中的比较&#xff0c; 如果左右两侧是基本类型变量&#…

基于python脚本实现的打砖块小游戏

目录 1. 打砖块游戏 2. 初始化 Pygame 和设置屏幕 3. 定义游戏对象 3.1 定义玩家操作的paddle 3.2 定义球&#xff08;Ball&#xff09; 3.3 砖块&#xff08;Bricks&#xff09; 4. 游戏主循环 4.1 事件处理 4.2 板子移动 4.3 球移动和碰撞检测 4.4 绘制游戏对象 …

地理信息系统(GIS)在智慧城市中的40个应用场景案例

在智慧城市发展进程中&#xff0c;地理信息系统&#xff08;GIS&#xff09;作为关键技术之一&#xff0c;正扮演着不可或缺的角色&#xff0c;堪称智慧城市的神经中枢。通过空间数据分析优化城市管理&#xff0c;GIS技术为智慧城市的构建提供了强大的支持。 本文分享了GIS在智…

XSS Game(DOM型) 靶场 通关

目录 靶场网址 Ma Spaghet! 分析 解题 Jefff 分析 解题 方法一 方法二 Ugandan Knuckles 分析 解题 Ricardo Milos 分析 解题 Ah Thats Hawt 分析 解题 方法一 方法二 Ligma 分析 解题 ​ Mafia 分析 解题 方法一&#xff1a;构造函数 方法二&#xf…

【大模型基础_毛玉仁】3.5 Prompt相关应用

目录 3.5 相关应用3.5.1 基于大语言模型的Agent3.5.2 数据合成3.5.3 Text-to-SQL3.5.4 GPTs 3.5 相关应用 Prompt工程应用广泛&#xff0c;能提升大语言模型处理基础及复杂任务的能力&#xff0c;在构建Agent、数据合成、Text-to-SQL转换和设计个性化GPTs等方面不可或缺。 . …

deepseek搭建本地私有知识库dify安装介绍docker compose图文教程

按照官方介绍&#xff0c;打开教程安装。下载源码&#xff0c; # 假设当前最新版本为 0.15.3 git clone https://github.com/langgenius/dify.git --branch 0.15.3 进入docker目录&#xff0c; cd dify/docker 网络科学的直接执行命令就可以了。 docker compose up -d 镜…

右击没有Word、PPT、Excel功能

右击没有Word、PPT、Excel功能 导航 文章目录 右击没有Word、PPT、Excel功能导航一、问题描述二、事情经过三、解决方案其他思路分享 一、问题描述 ​ 在安装并激活了office之后&#xff0c;业务反馈右击没有出现新建Word功能&#xff0c;仅有Word文档 二、事情经过 ​ 按道…

DeepSeek R1 本地部署指南 (6) - Windows 本地部署使用 GPU 运行

DeepSeek R1 本地部署指南 (1) - Windows 本地部署 上一篇&#xff0c;安装好 Windows 本地步骤后&#xff0c;如果发现在任务管理器中 GPU 显示 0%。 1.在命令行中输入&#xff1a; ollama ps 显示&#xff1a; PROCESSOR CPU 2.安装 CUDA Toolkit CUDA Toolkit Downloads htt…

婚姻的解构与重构 | 一场关于选择与责任的探索

注&#xff1a;本文为 “婚姻的解构与重构” 相关文章合辑。 未整理。 明明渴望爱情 为何反感催婚&#xff1f; 原创 常 晋 人民日报评论 2024 年 04 月 22 日 12:29 北京 没有催促指责&#xff0c;也毫无批评之意。面对单身、失业的 30 岁女儿&#xff0c;只是鼓励孩子&…

jangow靶机攻略

配置网卡 VMware需要配置&#xff0c;不配置扫不到ip,VirtualBox正常打开ip会直接显示出来 网卡配置都改成NAT 打开虚拟机&#xff0c;第一个框选第二行&#xff0c;回车 选第二个&#xff0c;按e键 进入下一个框后&#xff0c;将ro 后面的修改为 rw signin init/bin/bash 按…