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

本文详细介绍了如何通过系统自带功能选择壁纸,并通过BroadcastReceiver监听壁纸更换。同时深入探讨了壁纸加载及绘制过程,尤其是在Workspace中壁纸随屏幕滑动的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

博客搬家啦——为了更好地经营博客,本人已经将博客迁移至www.ijavaboy.com。这里已经不再更新,给您带来的不便,深感抱歉!这篇文章的新地址:点击我微笑 


上一篇中,我们了解了Workspace是如何处理多个CellLayout之间的滑动的。这篇,将记录如何将壁纸添加到桌面,以及Workspace如何处理滑动的时候,壁纸的滑动。

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

  1. //调用系统自带壁纸选择功能,ACTION_SET_WALLPAPER为选择的时候使用的过滤条件   
  2. Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER);  
  3.         //启动系统选择应用   
  4. Intent intent = new Intent(Intent.ACTION_CHOOSER);  
  5.         intent.putExtra(Intent.EXTRA_INTENT, chooseIntent);  
  6.        intent.putExtra(Intent.EXTRA_TITLE, "选择壁纸");  
  7.     startActivity(intent);  
	//调用系统自带壁纸选择功能,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的应用。当然,上面的代码也可以直接简写为:

  1. Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER);  
  2. startActivity(Intent.createChooser(chooseIntent, "选择壁纸"));  
  Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
  startActivity(Intent.createChooser(chooseIntent, "选择壁纸"));

 

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

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

  1.   /** 
  2.    *  
  3.    * 当别的应用程序改变了壁纸后,这里定义一个BroadcastReceiver来接受通知 
  4.    * 
  5.    */  
  6.   class WallpaperIntentReceiver extends BroadcastReceiver{  
  7.       
  8.     private Application application;  
  9.     //WeakReference使得WallpaperIntentReceiver不会因为Launcher的引用而被推迟注销掉   
  10.     private WeakReference<UorderLauncher> rLauncher;  
  11.   
  12.     public WallpaperIntentReceiver(Application application, UorderLauncher launcher) {  
  13.         this.application = application;  
  14.         this.rLauncher = new WeakReference<UorderLauncher>(launcher);  
  15. }  
  16.       
  17.     public void setLauncher(UorderLauncher l){  
  18.         this.rLauncher = new WeakReference<UorderLauncher>(l);  
  19.     }  
  20.       
  21. @Override  
  22. public void onReceive(Context context, Intent intent) {  
  23.     Log.v(TAG, "更换了壁纸");  
  24.     /** 
  25.      * 从ApplicationContext获取壁纸 
  26.      */  
  27.     final Drawable lDrawable = application.getWallpaper();  
  28.     if(lDrawable instanceof BitmapDrawable){  
  29.         Log.v(TAG, "壁纸是BitmapDrawable类型的");  
  30.         mWallpaper = ((BitmapDrawable)lDrawable).getBitmap();  
  31.           
  32.     }else{  
  33.         throw new IllegalStateException("The wallpaper must be a BitmapDrawable object");  
  34.     }  
  35.       
  36.     /** 
  37.      * 如果此时Launcher是活动的,未被锁定,则加载新的Wallpaper 
  38.      */  
  39.     if(rLauncher != null){  
  40.         final UorderLauncher launcher = rLauncher.get();  
  41.         if(launcher != null){  
  42.             launcher.loadWallpaper();  
  43.         }  
  44.     }  
  45. }  
  46.       
  47.   }  
    /**
     * 
     * 当别的应用程序改变了壁纸后,这里定义一个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的呢?所以,我们需要注册它:

  1. private void registerIntentReceivers(){  
  2.     if(mWallpaperReceiver == null){  
  3.         mWallpaperReceiver = new WallpaperIntentReceiver(getApplication(), this);  
  4.         /** 
  5.          * 注册的时候,指定IntentFilter,这样改BroadcastReciver就是接收壁纸更换的Broadcast的了 
  6.          */  
  7.         IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);  
  8.         getApplication().registerReceiver(mWallpaperReceiver, filter);  
  9.     }else{  
  10.         mWallpaperReceiver.setLauncher(this);  
  11.     }  
  12. }  
	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中了:

  1. /** 
  2.  * 这里加载壁纸,需要重新绘制界面 
  3.  * @param wallpaper 
  4.  */  
  5. public void loadWallpaper(Bitmap wallpaper) {  
  6.     wallpaperLoaded = true;  
  7.     mWallpaper = wallpaper;  
  8.     //要求重新绘制界面   
  9.     requestLayout();  
  10.     invalidate();  
  11. }  
	/**
	 * 这里加载壁纸,需要重新绘制界面
	 * @param wallpaper
	 */
	public void loadWallpaper(Bitmap wallpaper) {
		wallpaperLoaded = true;
		mWallpaper = wallpaper;
		//要求重新绘制界面
		requestLayout();
		invalidate();
	}


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

  1. /** 
  2.  * 壁纸本身可能并没有整个workspace那么宽 
  3.  * 所以,屏幕滑动的时候,壁纸向右边滑动的距离需要根据mWallpaperOffset做相应的调整 
  4.  */  
  5. float x = getScrollX()*mWallpaperOffset;  
  6. //这里啥个意思,TODO:   
  7. Log.v(TAG, "getRight-getLeft="+getRight()+"-"+getLeft()+"="+(getRight()-getLeft()));  
  8.   
  9. /** 
  10.  * getRight()-getLeft()=手机屏幕的宽度 
  11.  */  
  12. if(x<getRight()-getLeft()-mWallpaperWidth){  
  13.     //当壁纸宽度小于屏幕宽度的时候,才会出现小于的情况   
  14.     //这种情况就固定   
  15.     //但是在获得用户选择的壁纸的时候,我们对其作了大小调整,所以,这里基本不会出现这种情况   
  16.     x = getRight()-getLeft()-mWallpaperWidth;  
  17. }  
  18.   
  19. float y = (getBottom()-getTop()-mWallpaperHeight)/2;  
  20.   
  21. Log.v(TAG, "开始画壁纸:x,y分别为:"+x+","+y);  
  22. //canvas.drawColor(Color.BLACK);   
  23. if(mWallpaper!=null){  
  24.     Log.v(TAG, "开始画壁纸");  
  25.     canvas.drawBitmap(mWallpaper, x, y, mPaint);  
  26.     //invalidate();   
  27. }   
    	/**
    	 * 壁纸本身可能并没有整个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中,对该变量进行了赋值:

  1. //加载壁纸   
  2. if(wallpaperLoaded){  
  3.     wallpaperLoaded = false;  
  4.     mWallpaper = BitmapUtils.centerToFit(mWallpaper, width, height, getContext());  
  5.     mWallpaperWidth = mWallpaper.getWidth();  
  6.     mWallpaperHeight = mWallpaper.getHeight();  
  7.     Log.v(TAG, "测量壁纸大小:"+mWallpaperWidth+","+mWallpaperHeight);  
  8. }  
  9.   
  10. final int wallpaperWidth = mWallpaperWidth;  
  11. //计算Wallpaper每次随着屏幕滑动移动的距离   
  12. if(wallpaperWidth > width){  
  13.     /** 
  14.      * 计算壁纸滑动的速率 
  15.      * 壁纸可以滑动的距离是count*width-wallpaperWidth 
  16.      * 屏幕可以滑动的距离是(count-1)*width 
  17.      * 这样,一除就是壁纸相对于屏幕滑动的速率了 
  18.      */  
  19.     //mWallpaperOffset = wallpaperWidth/(count*(float)width);   
  20.     mWallpaperOffset = (count*width-wallpaperWidth)/((count-1)*(float)width);  
  21. }else {  
  22.     mWallpaperOffset = 1.0f;  
  23. }  
  24.   
  25. Log.v(TAG, "wallpaper的offset:"+mWallpaperOffset);  
		//加载壁纸
		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。

好了,至此,我们应该清楚壁纸的添加机制了。

下一篇将揭晓item的拖拽之谜...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值