您的位置:首页 > 移动开发 > Android开发

说说Android桌面(Launcher应用)背后的故事(五)——桌面壁纸的添加

2017-04-18 10:42 495 查看
上一篇中,我们了解了Workspace是如何处理多个CellLayout之间的滑动的。这篇,将记录如何将壁纸添加到桌面,以及Workspace如何处理滑动的时候,壁纸的滑动。

壁纸的添加,也是调用系统自带的,用如下方式调用:

[java] view
plain copy

//调用系统自带壁纸选择功能,ACTION_SET_WALLPAPER为选择的时候使用的过滤条件

Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER);

//启动系统选择应用

Intent intent = new Intent(Intent.ACTION_CHOOSER);

intent.putExtra(Intent.EXTRA_INTENT, chooseIntent);

intent.putExtra(Intent.EXTRA_TITLE, "选择壁纸");

startActivity(intent);

这样就会列出所有Action含有Android.intent.action.SET_WALLPAPER的应用。当然,上面的代码也可以直接简写为:

[java] view
plain copy

Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER);

startActivity(Intent.createChooser(chooseIntent, "选择壁纸"));

但是,按照常见的,应该是startActivityForResult来启动,然后在onActivityResult中来获取返回后的壁纸。但是,选择壁纸后,并不是通过这种方式获取选择的壁纸的,那么我们如何获取选择的壁纸呢?

其实,当我们选择了一张壁纸后,系统会发出一个Broadcast,同时将选择的壁纸,缓存在整个应用程序的上下文中,这样,其实就是由于壁纸,不仅可以在桌面上更换,也可以在图片显示应用中更换,所以,其使用Broadcast机制来通知壁纸已经更换。我们需要实现一个BroadcastReciever来监听壁纸的选择:

[java] view
plain copy

/**

*

* 当别的应用程序改变了壁纸后,这里定义一个BroadcastReceiver来接受通知

*

*/

class WallpaperIntentReceiver extends BroadcastReceiver{

private Application application;

//WeakReference使得WallpaperIntentReceiver不会因为Launcher的引用而被推迟注销掉

private WeakReference<UorderLauncher> rLauncher;

public WallpaperIntentReceiver(Application application, UorderLauncher launcher) {

this.application = application;

this.rLauncher = new WeakReference<UorderLauncher>(launcher);

}

public void setLauncher(UorderLauncher l){

this.rLauncher = new WeakReference<UorderLauncher>(l);

}

@Override

public void onReceive(Context context, Intent intent) {

Log.v(TAG, "更换了壁纸");

/**

* 从ApplicationContext获取壁纸

*/

final Drawable lDrawable = application.getWallpaper();

if(lDrawable instanceof BitmapDrawable){

Log.v(TAG, "壁纸是BitmapDrawable类型的");

mWallpaper = ((BitmapDrawable)lDrawable).getBitmap();

}else{

throw new IllegalStateException("The wallpaper must be a BitmapDrawable object");

}

/**

* 如果此时Launcher是活动的,未被锁定,则加载新的Wallpaper

*/

if(rLauncher != null){

final UorderLauncher launcher = rLauncher.get();

if(launcher != null){

launcher.loadWallpaper();

}

}

}

}

在这个BroadcastReciever中,直接通过Application.getWallpaper();获取最新的壁纸,那么有了BroadcastReciever,我们怎么知道这个Reciever是接收壁纸更换的Broadcast的呢?所以,我们需要注册它:

[java] view
plain copy

private void registerIntentReceivers(){

if(mWallpaperReceiver == null){

mWallpaperReceiver = new WallpaperIntentReceiver(getApplication(), this);

/**

* 注册的时候,指定IntentFilter,这样改BroadcastReciver就是接收壁纸更换的Broadcast的了

*/

IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);

getApplication().registerReceiver(mWallpaperReceiver, filter);

}else{

mWallpaperReceiver.setLauncher(this);

}

}

现在,知道了壁纸的获取,那么,接下来我们自然而然想到的是:壁纸我已经获取到了,但是怎么样将它绘制在Workspace上面呢?注意,上面BroadcastReceiver获取到壁纸的时候调用了launcher.loadWallpaper()来完成壁纸加载的,在这个方法中,可以看到其调用了mWorkspace.loadWallpaper(mWallpaper);那么,就需要回到Workspace中了:

[java] view
plain copy

/**

* 这里加载壁纸,需要重新绘制界面

* @param wallpaper

*/

public void loadWallpaper(Bitmap wallpaper) {

wallpaperLoaded = true;

mWallpaper = wallpaper;

//要求重新绘制界面

requestLayout();

invalidate();

}

这个方法仅仅要求重新绘制布局,那么,我们就知道,在绘制的方法中,应该会对壁纸进行相关的绘制。在dispatchDraw中,有对壁纸的处理,

[java] view
plain copy

/**

* 壁纸本身可能并没有整个workspace那么宽

* 所以,屏幕滑动的时候,壁纸向右边滑动的距离需要根据mWallpaperOffset做相应的调整

*/

float x = getScrollX()*mWallpaperOffset;

//这里啥个意思,TODO:

Log.v(TAG, "getRight-getLeft="+getRight()+"-"+getLeft()+"="+(getRight()-getLeft()));

/**

* getRight()-getLeft()=手机屏幕的宽度

*/

if(x<getRight()-getLeft()-mWallpaperWidth){

//当壁纸宽度小于屏幕宽度的时候,才会出现小于的情况

//这种情况就固定

//但是在获得用户选择的壁纸的时候,我们对其作了大小调整,所以,这里基本不会出现这种情况

x = getRight()-getLeft()-mWallpaperWidth;

}

float y = (getBottom()-getTop()-mWallpaperHeight)/2;

Log.v(TAG, "开始画壁纸:x,y分别为:"+x+","+y);

//canvas.drawColor(Color.BLACK);

if(mWallpaper!=null){

Log.v(TAG, "开始画壁纸");

canvas.drawBitmap(mWallpaper, x, y, mPaint);

//invalidate();

}

在这里,我们看到了,其通过canvas.drawBitmap(mWallpaper, x, y, mPaint);绘制了壁纸,这里关键的是x,y的计算,桌面可以横向滑动的,那么每次滑动后重新绘制的时候,这个x的值是在变化的,通过代码我们可以发现,其中有一个变量mWallpaperOffset,查找这个变量,在onMeasure中,对该变量进行了赋值:

[java] view
plain copy

//加载壁纸

if(wallpaperLoaded){

wallpaperLoaded = false;

mWallpaper = BitmapUtils.centerToFit(mWallpaper, width, height, getContext());

mWallpaperWidth = mWallpaper.getWidth();

mWallpaperHeight = mWallpaper.getHeight();

Log.v(TAG, "测量壁纸大小:"+mWallpaperWidth+","+mWallpaperHeight);

}

final int wallpaperWidth = mWallpaperWidth;

//计算Wallpaper每次随着屏幕滑动移动的距离

if(wallpaperWidth > width){

/**

* 计算壁纸滑动的速率

* 壁纸可以滑动的距离是count*width-wallpaperWidth

* 屏幕可以滑动的距离是(count-1)*width

* 这样,一除就是壁纸相对于屏幕滑动的速率了

*/

//mWallpaperOffset = wallpaperWidth/(count*(float)width);

mWallpaperOffset = (count*width-wallpaperWidth)/((count-1)*(float)width);

}else {

mWallpaperOffset = 1.0f;

}

Log.v(TAG, "wallpaper的offset:"+mWallpaperOffset);

这样,当我们每次滑动的时候,Workspace在重绘的时候就会计算这个值,然后在dispatchDraw中首先绘制壁纸,然后绘制每个CellLayout。

好了,至此,我们应该清楚壁纸的添加机制了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐