欢迎使用CSDN-markdown编辑器

来源:互联网 发布:配眼镜的数据 编辑:程序博客网 时间:2024/05/17 05:59

Android相册实现

主要原理:

  • LruCache缓存技术
  • ImageLoad异步加载技术
  • ContentProvider获取手机所有图片
  • GridView显示图片
  • CommonAdapter实现Adapter的通用适配

ContentProvider获取手机所有图片

  • 使用ContentProvider获取所有图片
  • MediaStore.Images.Media.MIME_TYPE为Image格式的MIME类型

    private void getImages() {    imgs = new ArrayList<String>();    Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;        String key_MIME_TYPE = MediaStore.Images.Media.MIME_TYPE;        String key_DATA = MediaStore.Images.Media.DATA;        ContentResolver mContentResolver = getContentResolver();        Cursor cursor = mContentResolver.query(mImageUri, new String[] { key_DATA },                key_MIME_TYPE + "=? or " + key_MIME_TYPE + "=? or " + key_MIME_TYPE + "=?",                new String[] { "image/jpg", "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED);        if (cursor != null) {            if (cursor.moveToLast()) {                imgs = new ArrayList<String>();                while (true) {                    String path = cursor.getString(0);                    imgs.add(path);                    if (imgs.size() >= 10000 || !cursor.moveToPrevious()) {                        break;                    }                }            }            cursor.close();        }}

使用LruCache缓存技术

  • LruCache类是该技术的核心,他的创建方法如下:

    int maxMemory = (int) Runtime.getRuntime().maxMemory();int cacheSize = maxMemory / 8;LruCache lc = new LruCache<String, Bitmap>(cacheSize) {        @Override        protected int sizeOf(String key, Bitmap value) {            return value.getRowBytes() * value.getHeight();        }    };
  • cacheSize为使用的最大内存,一般使用应用程序所允许的最大内存的1/8

  • 当内存满了的时候他能自动地删除最近不常使用的资源
  • 他的主要方法和Map类似为 put()和get(),保存图片资源时可以使用图片的路径作为key,Bitmap作为value。当想要使用某一张图片时,先通过路径索引lc,若没有获得则根据路径创建bitmap然后再把bitmap存入lc中

Bitmap的压缩技术

  • 用一个小分辨率的ImageView来显示大分辨率的Bitmap不会有什么改变反而浪费了宝贵的内存,所以可以先把Bitmap压缩一定大小就可以节省这一部分内存
  • 实现Bitmap压缩的API是BitmapFactory.Options,它可以在Bitmap加载的时候传进去,当我们把option.inJustDecodeBounds = true时,BitmapFactory加载Bitmap时不会生成Bitmap对象,但是可以获得Bitmap的宽高,我们就是根据这个宽高来进行压缩。option还有一个参数inSampleSize,当它是1时Bitmap大小不会改变,它是2时,Bitmap大小减少一倍,以此类推。这样,我们就可以得到压缩Bitmap的方法,下面是实现任意比例压缩的方法

    public static int calculateInSampleSize(BitmapFactory.Options option, int reqWidth, int reqHeight) {    int inSampleSize = 1;    int width = option.outWidth;    int height = option.outHeight;    if (width > reqWidth || height > reqHeight) {        int halfWidth = width / 2;        int halfHeight = height / 2;        while ((halfWidth / inSampleSize) > reqWidth && (halfHeight / inSampleSize) > reqHeight) {            inSampleSize *= 2;        }    }    return inSampleSize;}public static Bitmap decodeSampleBitmapFromPath(String path, int reqWidth, int reqHeight) {    Bitmap bitmap = null;    BitmapFactory.Options option = new Options();    //获取Bitmap的宽高    option.inJustDecodeBounds = true;    BitmapFactory.decodeFile(path, option);    int inSampleSize = calculateInSampleSize(option, reqWidth, reqHeight);    option.inJustDecodeBounds = false;    option.inSampleSize = inSampleSize;    bitmap = BitmapFactory.decodeFile(path, option);    return bitmap;}

结合Lrucache实现异步缓存加载Bitmap的ImageLoad

  • 异步加载就是开启一个线程去加载bitmap,实现一个回调接口在加载完成时调用便达到异步加载
  • 关键的API为 Handler的异步回调机制
  • 我们先来定义一个接口用来回调时调用

    public interface ImageCallback {    public void imageLoaded(Bitmap imageDrawable);}
  • 当需要加载Bitmap时,我们先在lc中找

    if (lc.get(item) != null) {    return lc.get(item);}
  • 如果没有找到就开启一个线程在通过路径加载

    new Thread() {    public void run() {        try {            final Bitmap bmp = BitmapUtils.decodeSampleBitmapFromPath(item, 100, 100);            lc.put(item, bmp);            mHander.post(new Runnable() {//这个运行在主线程                @Override                public void run() {                    callback.imageLoaded(bmp);//加载完成后回调                }            });        } catch (Exception e) {            e.printStackTrace();        }    }}.start();
  • 当然要先实现回调接口

    new ImageCallback() {    @Override    public void imageLoaded(Bitmap bmp) {        if (imageView.getTag().equals(filePath)) {            if (bmp != null) {                imageView.setImageBitmap(bmp);            } else {                Log.d("asd", "imageLoaded");                imageView.setImageResource(R.drawable.empty_photo);            }        }    }}
  • 下面是所有的代码

    public class ImageLoader {    private LruCache<String, Bitmap> lc;    private int screenW, screenH;    private Handler mHander = new Handler();    public ImageLoader() {          int maxMemory = (int) Runtime.getRuntime().maxMemory();        int cacheSize = maxMemory / 8;        lc = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap value) {                return value.getRowBytes() * value.getHeight();            }        };    }    private Bitmap loadDrawable(final String item, final ImageCallback callback) {        if (lc.get(item) != null) {            return lc.get(item);        }        new Thread() {            public void run() {                try {                    final Bitmap bmp = BitmapUtils.decodeSampleBitmapFromPath(item, 100, 100);                    lc.put(item, bmp);                    mHander.post(new Runnable() {                        @Override                        public void run() {                            callback.imageLoaded(bmp);                        }                    });                } catch (Exception e) {                    e.printStackTrace();                }            }        }.start();        return null;    }    public void loadImage(final String filePath, final ImageView imageView) {        Bitmap bmp = loadDrawable(filePath, new ImageCallback() {            @Override            public void imageLoaded(Bitmap bmp) {                if (imageView.getTag().equals(filePath)) {                    if (bmp != null) {                        imageView.setImageBitmap(bmp);                    } else {                        Log.d("asd", "imageLoaded");                        imageView.setImageResource(R.drawable.empty_photo);                    }                }            }        });        if (bmp != null) {            if (imageView.getTag().equals(filePath)) {                imageView.setImageBitmap(bmp);            }        } else {            imageView.setImageResource(R.drawable.empty_photo);        }    }    public interface ImageCallback {        public void imageLoaded(Bitmap imageDrawable);    }}
  • 到处,异步加载图片的方法就介绍完成了,用的时候只要创建ImageLoad对象,然后调用它的
    public void loadImage(final String filePath, final ImageView imageView)方法就可以了

实现一个通用的ViewHolder

  • 在编写Adapter的时候,一般都会用到ViewHolder的方法,所以,实现一个通用的ViewHolder可以减少开发时间
  • 因为每个ViewHolder的控件不同,所以我们需要一个集合在保存这些控件,又为了能获得响应的控件,我们有需要可以根据标记提取控件,所以使用map的机制能很好的实现这一点。在key的选择上,我们可以选择控件的id,这样可以做到key的唯一,也能根据id来获取控件。此外,ViewHolder应该和view相联系,所以需要使用view.setTag()方法保存ViewHolder,以下是全部代码

    public class ViewHolder {    private View mConverView;    private SparseArray<View> mView;//使用整形数据来作为key    private ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {        mView = new SparseArray<View>();        mConverView = LayoutInflater.from(context).inflate(layoutId, parent, false);        mConverView.setTag(this);    }    public static ViewHolder getViewHolder(Context context, ViewGroup parent,            View convertView, int layoutId, int position) {        if(convertView == null){            return new ViewHolder(context, parent, layoutId, position);        }else{            return (ViewHolder) convertView.getTag();        }    }    public <T extends View> T getView(int viewId) {        View view = mView.get(viewId);        if(view == null){            view = mConverView.findViewById(viewId);            mView.put(viewId, view);        }        return (T) view;    }    public View getConvertView() {        return mConverView;    }}
  • 使用通用的ViewHolder的方法就是在Adapter的getview中使用getViewHolder方法获得viewholder,在通过getview获得要控件,然后就可以执行了

实现一个通用的Adapter

  • 在构造方法中传入context、布局id和list,因为要适配所有的bean,所以要使用泛型。这样就可以得到一个初步的通用Adapter

    public abstract class CommontAdapter<T> extends BaseAdapter {    private Context mContext;    private List<T> mList;    private int id;    private LayoutInflater inflater;    public CommontAdapter(Context context, List<T> list, int layouId) {        mContext = context;        mList = list;        id = layouId;        inflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return mList.size();    }    @Override    public T getItem(int position) {        return mList.get(position);    }    @Override    public long getItemId(int position) {        return position;    }}
  • 可以看到我们的通用Adapter是个抽象类,我们并没有实现getView()方法,因为我们在使用Adapter的时候他的其他三个方法通常都是固定的,只有getview方法需要实现多一点的逻辑,所以我们把他留给创建对象的时候使用,这样我们就可以直接创建一个内部类实现getview方法就可以轻松地实现一个Adapter。

  • 接下来我们再看看getview还是有可以改进的部分,一般的getview基本上都是这样:

    public View getView(int position, View convertView, ViewGroup parent) {    Fruit fruit = getItem(position);// 获得Fruit对象    View view;// 加载布局    ViewHolder viewHolder = new ViewHolder();    if(convertView == null){        view = LayoutInflater.from(getContext()).inflate(resourceId, null);        viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_iv);        viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_tv);        view.setTag(viewHolder);    }else{        view=convertView;        viewHolder=(ViewHolder) view.getTag();    }    viewHolder.fruitImage.setImageResource(fruit.getImageId());    viewHolder.fruitName.setText(fruit.getName());    return view;}
  • 我们可以看到获得bean对象和viewholder和return都是固定不需要改变的,所以我们可以写死这一点,我们再创建一个convert的抽象方法,然后在getview中调用就可以减少很多代码

    public View getView(int position, View convertView, ViewGroup parent) {    final ViewHolder holder = ViewHolder.getViewHolder(mContext, parent, convertView, id, position);    convert(holder, getItem(position), position);    return holder.getConvertView();}
  • 这样我们可以直接在使用通用Adapter的时候直接内部类实现convert方法就可以了

     mListView.setAdapter(mAdapter = new CommonAdapter<String>(          getApplicationContext(), mDatas, R.layout.item_single_str) {              @Override              public void convert(ViewHolder c, String item)              {                  TextView view = viewHolder.getView(R.id.id_tv_title);                  view.setText(item);              }           });     

实现相片选择器

  • 在经过一系列的准备后,我们现在已经可以去实现一个相片选择器了
  • 我们先获得手机的所有相片和做一些初始化,如ImageLoad

    getImages();imageLoader = new ImageLoader();
  • 然后我们获得GridView并设置适配器,我们可以选择通用的适配器

    gv_photo.setAdapter(new CommontAdapter<String>(getApplicationContext(), imgs, R.layout.item_photo){    @Override    public void convert(ViewHolder holder, String item, int position) {        if (position == 0) {            ImageView iv = holder.getView(R.id.iv_photo);            CheckBox cb = holder.getView(R.id.cb_photo);            ImageView iv_cramer = holder.getView(R.id.iv_cramer_photo);            iv_cramer.setVisibility(View.VISIBLE);            cb.setVisibility(View.INVISIBLE);            iv.setVisibility(View.INVISIBLE);            iv.setOnClickListener(null);            iv_cramer.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);                    startActivityForResult(intent, 200);                }            });            return;        }        ImageView iv = holder.getView(R.id.iv_photo);        CheckBox cb = holder.getView(R.id.cb_photo);        ImageView iv_cramer = holder.getView(R.id.iv_cramer_photo);        iv_cramer.setVisibility(View.INVISIBLE);        cb.setVisibility(View.VISIBLE);        iv.setVisibility(View.VISIBLE);        cb.setTag(R.id.cb_photo, position);        cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                sparesMap.put((Integer) buttonView.getTag(R.id.cb_photo), isChecked);            }        });        cb.setChecked(sparesMap.get(position));        iv.setTag(item);        imageLoader.loadImage(item, iv);    }});
  • 这样我们就可以实现一个相片选择器了

0 0