上一篇中,我们了解了Workspace是如何处理多个CellLayout之间的滑动的。这篇,将记录如何将壁纸添加到桌面,以及Workspace如何处理滑动的时候,壁纸的滑动。
壁纸的添加,也是调用系统自带的,用如下方式调用:
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的应用。当然,上面的代码也可以直接简写为:
Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER); startActivity(Intent.createChooser(chooseIntent, "选择壁纸" ));
但是,按照常见的,应该是startActivityForResult来启动,然后在onActivityResult中来获取返回后的壁纸。但是,选择壁纸后,并不是通过这种方式获取选择的壁纸的,那么我们如何获取选择的壁纸呢?
其实,当我们选择了一张壁纸后,系统会发出一个Broadcast,同时将选择的壁纸,缓存在整个应用程序的上下文中,这样,其实就是由于壁纸,不仅可以在桌面上更换,也可以在图片显示应用中更换,所以,其使用Broadcast机制来通知壁纸已经更换。我们需要实现一个BroadcastReciever来监听壁纸的选择:
class WallpaperIntentReceiver extends BroadcastReceiver{ private Application application; 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, "更换了壁纸" ); 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" ); } if (rLauncher != null ){ final UorderLauncher launcher = rLauncher.get(); if (launcher != null ){ launcher.loadWallpaper(); } } } }
在这个BroadcastReciever中,直接通过Application.getWallpaper();获取最新的壁纸,那么有了BroadcastReciever,我们怎么知道这个Reciever是接收壁纸更换的Broadcast的呢?所以,我们需要注册它:
private void registerIntentReceivers(){ if (mWallpaperReceiver == null ){ mWallpaperReceiver = new WallpaperIntentReceiver(getApplication(), this ); IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); getApplication().registerReceiver(mWallpaperReceiver, filter); }else { mWallpaperReceiver.setLauncher(this ); } }
现在,知道了壁纸的获取,那么,接下来我们自然而然想到的是:壁纸我已经获取到了,但是怎么样将它绘制在Workspace上面呢?注意,上面BroadcastReceiver获取到壁纸的时候调用了launcher.loadWallpaper()来完成壁纸加载的,在这个方法中,可以看到其调用了mWorkspace.loadWallpaper(mWallpaper);那么,就需要回到Workspace中了:
public void loadWallpaper(Bitmap wallpaper) { wallpaperLoaded = true ; mWallpaper = wallpaper; requestLayout(); invalidate(); }
这个方法仅仅要求重新绘制布局,那么,我们就知道,在绘制的方法中,应该会对壁纸进行相关的绘制。在dispatchDraw中,有对壁纸的处理,
float x = getScrollX()*mWallpaperOffset; Log.v(TAG, "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); if (mWallpaper!= null ){ Log.v(TAG, "开始画壁纸" ); canvas.drawBitmap(mWallpaper, x, y, mPaint); }
在这里,我们看到了,其通过canvas.drawBitmap(mWallpaper, x, y, mPaint);绘制了壁纸,这里关键的是x,y的计算,桌面可以横向滑动的,那么每次滑动后重新绘制的时候,这个x的值是在变化的,通过代码我们可以发现,其中有一个变量mWallpaperOffset,查找这个变量,在onMeasure中,对该变量进行了赋值:
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; if (wallpaperWidth > width){ mWallpaperOffset = (count*width-wallpaperWidth)/((count-1 )*( float )width); }else { mWallpaperOffset = 1 .0f; } Log.v(TAG, "wallpaper的offset:" +mWallpaperOffset);
这样,当我们每次滑动的时候,Workspace在重绘的时候就会计算这个值,然后在dispatchDraw中首先绘制壁纸,然后绘制每个CellLayout。
好了,至此,我们应该清楚壁纸的添加机制了。