android学习日记:4.0源生背景修改

经过整整一周的学习,总算实现了android中源生背景的修改。效果如下:

android学习日记:4.0源生背景修改_第1张图片 android学习日记:4.0源生背景修改_第2张图片

分别是修改前的原始界面、选择背景设置界面以及最后的效果图。

之前一直想用ScrollView来实现这个效果,后来发现用Gallery会简单很多。虽然Gallery被谷歌抛弃了,不过在4.0源码中还是有着它的大量身影。

设计思路很简单:布局->事件->处理->保存状态。布局文件参考的源码Launcher中的wallpaperchooer。事件处理则是使用StartActivityForResult来获得图片的ResID。处理是简单的获得背景layout后用获得的ResID来进行填充。保存状态这一步暂时还没有来的及做,所以,一旦手机重启,背景界面就会重置。。。
布局文件如下(我弄懂的地方已注释):

public class BackgroundChooserFragment extends Fragment implements
AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener{
	private ArrayList<Integer> mThumbs; //下方的小图
	private ArrayList<Integer> mImages; //用做背景的大图
	private Bitmap mBitmap = null;      //绘图时会用到bitmap
	private WallpaperLoader mLoader;    //加载图片
	private WallpaperDrawable mWallpaperDrawable = new WallpaperDrawable();  //图片的绘制
	private Intent intent;		    //用来向Launcher回传ResID
	@Override
	public void onCreate(Bundle savedInstanceState) { 	//系统创建Fragments 时调用,
								//可做执行初始化工作或者当程
								//序被暂停或停止时用来恢复状态,
								//跟Activity 中的onCreate相当。
	super.onCreate(savedInstanceState);
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {	// 用于首次绘制用户界面的回调方法,必须返回要创
								//建的Fragments 视图UI。假如你不希望提供
								//Fragments 用户界面则可以返回NULL。 
		// TODO Auto-generated method stub
		 findWallpapers();
		 View view = inflater.inflate(R.layout.background_dialogchooser, container, false);
		 //在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是
		 //LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是找xml
		 //布局文件下的具体widget控件(如Button、TextView等)。
         view.setBackgroundDrawable(mWallpaperDrawable);	//铺设背景图片

         final Gallery gallery = (Gallery) view.findViewById(R.id.background_gallery); //获得Gallery
         gallery.setCallbackDuringFling(false);
         gallery.setOnItemSelectedListener(this);
         gallery.setAdapter(new ImageAdapter(getActivity()));   //为Gallery添加适配器,左右的拖动就是在这里实现的
         
         View setButton = view.findViewById(R.id.background_set);
         setButton.setOnClickListener(new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
                 selectWallpaper(gallery.getSelectedItemPosition());
             }
         });
         return view;
	}
	private void selectWallpaper(int position) {
		
		Activity activity = getActivity();  //这是个fragment,所以必需要先获得父Activity才能使用Intent
		intent = new Intent(activity, Launcher.class);
		
		intent.putExtra("background", mImages.get(position));
		
	    	activity.setResult(Activity.RESULT_OK,intent);  //数据的回传在这里
	    	activity.finish();    //调用finish时会马上执行intent
        
    }
	private void findWallpapers() {  //本方法用来加载图片资源
        mThumbs = new ArrayList<Integer>(24);  //最多24张图片
        mImages = new ArrayList<Integer>(24);

        final Resources resources = getResources();
        final String packageName = resources.getResourcePackageName(R.array.wallpapers);
        addWallpapers(resources, packageName, R.array.wallpapers);
        addWallpapers(resources, packageName, R.array.extra_wallpapers);
        //同一个包下的两个数组文件
    }
	
	private void addWallpapers(Resources resources, String packageName, int list) {
        final String[] extras = resources.getStringArray(list);
        for (String extra : extras) {
        	//使用getIdentifier()获取资源Id,参数(ID名,文件夹名,包名)
            int res = resources.getIdentifier(extra, "drawable", packageName);
            if (res != 0) {
                final int thumbRes = resources.getIdentifier(extra + "_small",
                        "drawable", packageName);

                if (thumbRes != 0) {
                    mThumbs.add(thumbRes);
                    mImages.add(res);

                }
            }
        }
    }
	private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
	//适配器,烦恼了我很久的东西
        private LayoutInflater mLayoutInflater;

        ImageAdapter(Activity activity) {
            mLayoutInflater = activity.getLayoutInflater();
        }

        public int getCount() {
            return mThumbs.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            View view;
	    //先获得小图的布局文件,然后用一个image来填充图。最后返回该布局文件View
            if (convertView == null) {
                view = mLayoutInflater.inflate(R.layout.wallpaper_item, parent, false);
            } else {
                view = convertView;
            }

            ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
            int thumbRes = mThumbs.get(position);
            image.setImageResource(thumbRes);
            Drawable thumbDrawable = image.getDrawable();
            
                thumbDrawable.setDither(true); //大幅减少图片的失真
          

            return view;
        }
    }
	
	class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
		//AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。
		//Params 启动任务执行的输入参数,比如HTTP请求的URL。这里是int
		//Progress 后台任务执行的百分比。
		//Result 后台执行任务最终返回的结果,比如String。这里是Bitmap
		//相当于一个小的线程,在背后偷偷运行一点不复杂的东西
        BitmapFactory.Options mOptions;

        WallpaperLoader() {	
            mOptions = new BitmapFactory.Options();
            mOptions.inDither = false;
            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
        }

        @Override
        protected Bitmap doInBackground(Integer... params) {
            if (isCancelled() || !isAdded()) {
                return null;
            }
            try {
                return BitmapFactory.decodeResource(getResources(),
                        mImages.get(params[0]), mOptions);
            } catch (OutOfMemoryError e) {
                return null;
            }
        }
	//为后面的Excute定义操作
        @Override
        protected void onPostExecute(Bitmap b) {
            if (b == null) return;

            if (!isCancelled() && !mOptions.mCancel) {
                // Help the GC
                if (mBitmap != null) {
                    mBitmap.recycle();
                }

                View v = getView();
                if (v != null) {
                    mBitmap = b;
                    mWallpaperDrawable.setBitmap(b);
                    v.postInvalidate();
                } else {
                    mBitmap = null;
                    mWallpaperDrawable.setBitmap(null);
                }
                mLoader = null;
            } else {
               b.recycle();
            }
        }

        void cancel() {
            mOptions.requestCancelDecode();
            super.cancel(true);
        }
    }
	//该方法实现了在背景区域上的图像绘制
	static class WallpaperDrawable extends Drawable {

        Bitmap mBitmap;
        int mIntrinsicWidth;
        int mIntrinsicHeight;

        /* package */void setBitmap(Bitmap bitmap) {
            mBitmap = bitmap;
            if (mBitmap == null)
                return;
            mIntrinsicWidth = mBitmap.getWidth();
            mIntrinsicHeight = mBitmap.getHeight();
        }

        @Override
        public void draw(Canvas canvas) {
            if (mBitmap == null) return;
            int width = canvas.getWidth();
            int height = canvas.getHeight();
            int x = (width - mIntrinsicWidth) / 2;
            Log.i("bruce","width " + width);
            Log.i("bruce","mIntrinsicWidth "+mIntrinsicWidth);
            int y = (height - mIntrinsicHeight) / 2;
            Log.i("bruce","height "+height);
            Log.i("bruce","mIntrinsicHeight "+mIntrinsicHeight);
            canvas.drawBitmap(mBitmap, x, y, null);
        }

        @Override
        public int getOpacity() {
            return android.graphics.PixelFormat.OPAQUE;
        }

        @Override
        public void setAlpha(int alpha) {
            // Ignore
        }

        @Override
        public void setColorFilter(ColorFilter cf) {
            // Ignore
        }
    }
	@Override
    public void onNothingSelected(AdapterView<?> parent) {
    }
	@Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectWallpaper(position);
    }

    // Selection handler for the embedded Gallery view
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (mLoader != null ) {
            mLoader.cancel();
        }
        mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
    }
	@Override
    public void onDestroy() {
    	Log.i("bruce","onDestroy");
        super.onDestroy();
        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
            mLoader.cancel(true);
            mLoader = null;
        }
    }

}
布局文件中的WallpaperLoader和WallpaperDrawable方法现在暂时还没有细看,等有时间了会继续研究下。

至于事件和处理,他们相比于布局而言简单很多。事件就是一个button的单击。处理需要在Launcher中进行,是简单的布局文件背景设置。这里我就不列源码赘述了。

下一步的工作就是学习savedInstanceState了,将选择的结果存储到savedInstanceState中,然后这个小小的研究就可以宣告一段落了。




你可能感兴趣的:(android学习日记:4.0源生背景修改)