参考:WallpaperManager zooms image even though the dimension match the screen
在设置Android单屏壁纸时,有些机型会遇到壁纸没有显示在屏幕正中间的问题。上述参考中给出的解决办法能解决一部分问题,但仍然有些机型不能被适配。
下面整理一下我解决这个问题的经历:
1.设置单屏壁纸的代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75/**
* 设置单屏壁纸
* @param wallpaperBitmap 原始图片
* */
public void setWallpaper(final Bitmap wallpaperBitmap) {
//获取屏幕的宽和高,使用DisplayMetrics.widthPixels等属性
final int ww = Utils.getScreenWidth(this);
final int wh = Utils.getScreenHeight(this);
//显示等待框
showProgress();
//开启线程对壁纸做一些裁剪处理
new Thread(new Runnable() {
@Override
public void run() {
if (wallpaperBitmap != null && wallpaperBitmap.getByteCount() > 0) {
//参数符合要求
//根据屏幕的宽和高裁剪壁纸
Bitmap scaledBitmap = scaleBitmap(wallpaperBitmap, ww, wh);
//裁剪完成,使用Handler在主线程设置壁纸
Message msg = mHandler.obtainMessage();
msg.what = SCALE_IMG_FINISH;
//把裁剪之后的bitmap作为参数传递到主线程中
msg.obj = scaledBitmap;
mHandler.sendMessage(msg);
}
}
}).start();
}
/**
* 裁剪bitmap
* @param b 原始图片
* @param w 目标宽
* @param h 目标高
* @return 裁剪后的bitmap
* */
private Bitmap scaleBitmap(Bitmap b, int w, int h) {
int width = b.getWidth();
int height = b.getHeight();
//获取缩减比例
float scaleWidth = ((float) w) / width;
float scaleHeight = ((float) h) / height;
//使用矩阵变化
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);//缩放
//返回缩放之后的bitmap
return Bitmap.createBitmap(b, 0, 0, width, height, matrix, true);
}
//Handler省略了,Handler在主线程中运行。
//在Handler的SCALE_IMG_FINISH处理中调用了下面的setToScreen方法设置壁纸
......
/**
* 设置壁纸
* @param bitmap 壁纸
* */
private void setToScreen(Bitmap bitmap) {
try {
WallpaperManager manager = WallpaperManager.getInstance(this);
//这个方法是通知屏幕当前壁纸的尺寸是屏幕的尺寸,防止滚动壁纸
//在官方文档中,此方法是不建议在Launcher以外的App中使用的。
manager.suggestDesiredDimensions(Utils.getScreenWidth(this), Utils.getScreenHeight(this));
manager.setBitmap(bitmap);
//提示设置壁纸成功
Toast.makeText(this, R.string.big_image_set_wallpaper_done, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
//提示设置壁纸失败
Toast.makeText(this, R.string.big_image_set_wallpaper_failed, Toast.LENGTH_SHORT).show();
} finally {
//最后隐藏等待框
hideProgress();
}
}
2.在上面的代码段中描述的是设置单屏壁纸的最简单的版本。这时会在某些机型上碰到壁纸无法居中显示的问题,因此根据参考中的回答,做了如下修改:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61//对上面的设置壁纸的方法做了一些修改
/**
* 设置单屏壁纸
* @param wallpaperBitmap 原始图片
* */
public void setWallpaper(final Bitmap wallpaperBitmap) {
//获取屏幕的宽和高,使用DisplayMetrics.widthPixels等属性
final int ww = Utils.getScreenWidth(this);
final int wh = Utils.getScreenHeight(this);
//获取桌面建议的壁纸的宽和高
final int desiredWidth = WallpaperManager.getInstance(this).getDesiredMinimumWidth();
final int desiredHeight = WallpaperManager.getInstance(this).getDesiredMinimumHeight();
//显示等待框
showProgress();
//开启线程对壁纸做一些裁剪处理
new Thread(new Runnable() {
@Override
public void run() {
if (wallpaperBitmap != null && wallpaperBitmap.getByteCount() > 0) {
//参数符合要求
//根据屏幕的宽和高裁剪壁纸
Bitmap scaledBitmap = scaleBitmap(wallpaperBitmap, ww, wh);
//把壁纸调整到屏幕的正中间
Bitmap overLay = BitmapHelper.overlayIntoCentre(desiredWidth, desiredHeight, sb);
//处理完成,使用Handler在主线程设置壁纸
Message msg = mHandler.obtainMessage();
msg.what = SCALE_IMG_FINISH;
//把处理之后的bitmap作为参数传递到主线程中
msg.obj = overLay;
mHandler.sendMessage(msg);
}
}
}).start();
}
//这是BitmapHelper类中的一个静态方法,BitmapHelper类不贴出来了
/**
* 处理壁纸覆盖的问题
* 这个方法的原理是根据指定的高和宽创建一个底层的Bitmap,一般指定的宽和高是桌面建议的壁纸的宽和高。
* 因此底层Bitmap会铺满桌面建议的宽和高的区域,然后把要设置为壁纸的Bitmap对象覆盖
* 在这个底层Bitmap的正中间。从而让单屏壁纸正好显示在屏幕的中间。
* 这个方法是参考回答中的。
* @param width 底层Bitmap的像素宽,一般为桌面建议的壁纸宽度
* @param height 底层Bitmap的像素高,一般为桌面建议的壁纸高度
* @param bmp2 要设置为壁纸的对象
* */
public static Bitmap overlayIntoCentre(int width, int height, Bitmap bmp2) {
//创建Bitmap对象,让其充满整个壁纸的区域
Bitmap bmOverlay = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmOverlay);
//绘制底层的bitmap
canvas.drawBitmap(bmOverlay, new Matrix(), null);//draw background bitmap
//overlay the second in the centre of the first
//(may experience issues if the first bitmap is smaller than the second, but then why would you want to overlay a bigger one over a smaller one?!)
//EDIT: added Y offest fix - thanks @Jason Goff!
//在底层对象的基础上把壁纸的bitmap对象覆盖在中间位置
canvas.drawBitmap(bmp2, (bmOverlay.getWidth()/2)-(bmp2.getWidth()/2), (bmOverlay.getHeight()/2)-(bmp2.getHeight()/2), null);
return bmOverlay;
}
3.上面的代码能解决大部分机型单屏壁纸没有在屏幕中间的问题,但是仍然有一些机型(如华为的部分机型)还有问题,壁纸会向右偏移,导致屏幕的左边一半是纯黑色背景(纯黑色是底层Bitmap),右半部分是壁纸的左半部分。出现这个状况,是因为该机型在设置壁纸时,不会从中间显示壁纸,而是从最左边(即从底层Bitmap的最左侧),又因为真正的壁纸是在底层Bitmap的中间,导致屏幕上显示了一半底层Bitmap,和一半壁纸。我的解决方案是对这些机型单独做适配,在这些机型上,不再把壁纸覆盖在底层Bitmap的中间位置,而是根据机型的特点,覆盖在不同的地方。比如华为的某款机型,就覆盖在底层Bitmap的最左侧即可。因此,仿照上面代码段中的overlayIntoCentre
方法,我们编写如下的新方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63//我把下面的方法也放在了BitmapHelper类中
/**
* 处理壁纸覆盖的问题
* 这个方法的原理是根据指定的高和宽创建一个底层的Bitmap,一般指定的宽和高是桌面建议的壁纸的宽和高。
* 因此底层Bitmap会铺满桌面建议的宽和高的区域,然后根据指定的left和top参数把要设置为壁纸的Bitmap对象覆盖
* 在这个底层Bitmap的左上角的位置。
* 这个方法是参考回答中的。
* @param width 底层Bitmap的像素宽,一般为桌面建议的壁纸宽度
* @param height 底层Bitmap的像素高,一般为桌面建议的壁纸高度
* @param left 壁纸距离底层Bitmap左上角的x轴距离
* @param top 壁纸距离底层Bitmap左上角的y轴距离
* @param bmp2 要设置为壁纸的对象
* */
public static Bitmap overlay(int width, int height, int left, int top, Bitmap bitmap2) {
Bitmap bmOverlay = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmOverlay, new Matrix(), null);//draw background bitmap
//根据left和top覆盖壁纸
canvas.drawBitmap(bitmap2, left, top, null);
return bmOverlay;
}
//使用方法如下
/**
* 设置单屏壁纸
* @param wallpaperBitmap 原始图片
* */
public void setWallpaper(final Bitmap wallpaperBitmap) {
//获取屏幕的宽和高,使用DisplayMetrics.widthPixels等属性
final int ww = Utils.getScreenWidth(this);
final int wh = Utils.getScreenHeight(this);
//获取桌面建议的壁纸的宽和高
final int desiredWidth = WallpaperManager.getInstance(this).getDesiredMinimumWidth();
final int desiredHeight = WallpaperManager.getInstance(this).getDesiredMinimumHeight();
//显示等待框
showProgress();
//开启线程对壁纸做一些裁剪处理
new Thread(new Runnable() {
@Override
public void run() {
if (wallpaperBitmap != null && wallpaperBitmap.getByteCount() > 0) {
//参数符合要求
//根据屏幕的宽和高裁剪壁纸
Bitmap scaledBitmap = scaleBitmap(wallpaperBitmap, ww, wh);
//根据机型适配
Bitmap overLay = null;
if (isHuaweiXXXX()) {
//是华为的某款机型,把壁纸放在底层Bitmap的最左侧
overLay = BitmapHelper.overlay(desiredWidth, desiredHeight, 0, 0, sb);
} else {
//其他机型,把壁纸覆盖在底层Bitmap的中间
overLay = BitmapHelper.overlayIntoCentre(desiredWidth, desiredHeight, sb);
}
//处理完成,使用Handler在主线程设置壁纸
Message msg = mHandler.obtainMessage();
msg.what = SCALE_IMG_FINISH;
//把处理之后的bitmap作为参数传递到主线程中
msg.obj = overLay;
mHandler.sendMessage(msg);
}
}
}).start();
}
到此,基本上大部分机型的单屏壁纸都可以解决了,还可以根据需要做单独对机型做适配。