分享

实例讲解Android中如何实现图片的异步加载功能

 CevenCheng 2011-10-18

原文地址::http://dev.10086.cn/blog/?uid-3072-action-viewspace-itemid-2582

android遍历sd卡中的所有文件::http://zhchzh1000./blog/763406

Android开发当中,经常会碰到图片的异步加载问题(也叫延时加载,英文叫 Lazyload)。图片的读取工作是个比较耗时的工作,如果还是从互联网读取图片资源就更加耗时。如果在主线程里处理的时间过长,就会引发著名的应用程序无响应的系统提示(ANR:Application Not Responding)。

本文通过一个名为Demo4FileManager的项目实例来讲解如何实现图片的异步加载功能。该应用的主要功能是列出SD卡下的所有目录和图片文件,用户也可以在此之上修改为读取网络图片的功能。异步加载的实现是通过AsyncTask类来实现的,首先通过一个叫FileLoadTask的类来加载当前目录下的目录或图片文件,在加载完目录或文件数据后,再去执行一个名为ImageLoadTask的类把当前目录下的图片资源解码并通知UI更新这些图片。下面我们一步一步来看看是怎么实现的。

1. 新建一个名为 FileItem.java 的类,封装了基本的目录、文件信息,并且实现Comparable接口,用于目录文件的排序。

public class FileItem implements Comparable<FileItem>{ 
    private String name;
    private String path;
    private int fileType;  //  0:文件,1:目录,2:上级目录
    private Bitmap image;
 
     // 下面是get和set方法
    .......
    
    // 和其他文件比较
    public int compareTo(FileItem another) {
        return this.name.compareTo(another.getName()); 
    }
}



2. 新建一个名为 FileListActivity.java 的类。在该类里实现一个名为listFile的方法,功能就是列出指定目录下的文件。我们可以看到在检查SD卡的状态为插入后,就启动一个文件加载任务。
    private void listFile(String path){
        Log.i(TAG, "listFile()");
        Log.d(TAG, "path:" + path);
        
        //  SD卡状态
        String status = Environment.getExternalStorageState();
        Log.d(TAG, "status of sdcard:" + status);
        if (status.equals(Environment.MEDIA_MOUNTED)) {//mounted
            task = new FileLoadTask(this, adapter);
            task.execute(path);
        }
    }

在onCreate方法里,加入listFile那个方法,缺省路径为 /sdcard。FileListAdapter为adapter类,这里就不详细介绍了,在附件里有。
    private FileLoadTask task;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.file_list);

        adapter = new FileListAdapter(this);
        fileList = (ListView) findViewById(R.id.fileList);
        fileList.setOnItemClickListener(this);  //  点击item事件
        fileList.setAdapter(adapter);
        
        listFile(FileItem.ROOT_PATH);
    }

3. 新建一个名为 FileLoadTask.java 的类,用来异步加载指定目录下的文件或图片。
public class FileLoadTask extends AsyncTask<String, FileItem, Void>{
    private static final String TAG = FileLoadTask.class.getSimpleName();
    private final static FolderFilter folderFilter = new FolderFilter();
    private final static ImageFilter imageFilter = new ImageFilter();
    
    private Context context;
    private FileListAdapter adapter;
    private Bitmap folderIcon; 
    private Bitmap fileIcon;
    private ImageLoadTask task;
    
    // 初始化
    public FileLoadTask(Context context, FileListAdapter adapter){
        Log.i(TAG, "FileLoadTask()");
        
        this.context = context;
        this.adapter = adapter;
        folderIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.folder);
        fileIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.file);
    }
    
    @Override
    protected void onPreExecute() {
        Log.i(TAG, "onPreExecute()");

        // 在执行任务前,先把adapter里的数据清空
        adapter.clear();
    }
    
    @Override
    protected Void doInBackground(String... path) {
        Log.i(TAG, "doInBackground()");
        
        File parent = new File(path[0]);
        if (parent.isDirectory()) {
            //设置返回按钮
            if(!path[0].equals(FileItem.ROOT_PATH)){
                FileItem root = new FileItem();
                root.setName(FileItem.ROOT_NAME);
                root.setFileType(FileItem.FILE_ROOT);
                publishProgress(root);
            }
            
            // 获取当前目录下的子目录
            File[] files = parent.listFiles(folderFilter);
            Arrays.sort(files);
            for (int i = 0; i < files.length; i++) {
                if(isCancelled()) return null;
                
                File file = files[i];                    
                FileItem bean = new FileItem();
                bean.setName(file.getName());
                bean.setPath(file.getAbsolutePath());
                bean.setFileType(FileItem.FILE_DIR);
                publishProgress(bean);  //处理好一个目录,通知任务去更新UI
            }
            
            // 获取当前目录下的图片文件
            files = parent.listFiles(imageFilter);
            Arrays.sort(files);
            for (int i = 0; i < files.length; i++) {
                if(isCancelled()) return null;
                
                File file = files[i];                    
                FileItem bean = new FileItem();
                bean.setName(file.getName());
                bean.setPath(file.getAbsolutePath());
                bean.setFileType(FileItem.FILE_IMAGE);
                publishProgress(bean);  //处理好一个文件,通知任务去更新UI
            }
        }
        return null;
    }
    
    @Override
    public void onProgressUpdate(FileItem... files) {
        Log.i(TAG, "onProgressUpdate()");
        if(isCancelled()) return;

        //  收到要处理的类,根据目录或文件先设置缺省的图片
        FileItem bean = files[0];
        if(bean.getFileType() == FileItem.FILE_IMAGE){
            bean.setImage(fileIcon);
        }else{
            bean.setImage(folderIcon);
        }
        //  更新数据,列表会显示对应数据
        adapter.add(bean);
        adapter.notifyDataSetChanged();
    }
    
    @Override
    protected void onPostExecute(Void result) {
        Log.i(TAG, "onPostExecute()");
        
        //启动图片加载任务
        if (task != null && task.getStatus() == AsyncTask.Status.RUNNING) {
            task.cancel(true);
        }
        task = new ImageLoadTask(context, adapter);
        task.execute();
    }
}

4. 新建一个名为 ImageLoadTask.java 的类,用来加载图片资源。
public class ImageLoadTask extends AsyncTask<Void, Void, Void>{
    private static final String TAG = ImageLoadTask.class.getSimpleName();
    private static int thumbnailWidth = 70;
    private static int thumbnailHeight = 100;
    
    private FileListAdapter adapter;

    // 初始化
    public ImageLoadTask(Context context, FileListAdapter adapter){
        Log.i(TAG, "ImageLoadTask()");
        
        this.adapter = adapter;
    }
        
    @Override
    protected Void doInBackground(Void... voids) {
        Log.i(TAG, "doInBackground()");
        
        BitmapFactory.Options ptions = new BitmapFactory.Options();
        options.inSampleSize = 16;

        // 要处理的文件
        for(int i=0; i<adapter.getCount(); i++){
            FileItem bean = adapter.getItem(i);
            if(bean.getFileType() == FileItem.FILE_DIR){
                continue;  //  非图片文件
            }
            
            try {
                //  生成缩略图
                options.inJustDecodeBounds = true;
                options.outWidth = 0;
                options.outHeight = 0;
                options.inSampleSize = 1;
                
                BitmapFactory.decodeFile(bean.getPath(), options);            
                if (options.outWidth > 0 && options.outHeight > 0) {
                    // Now see how much we need to scale it down.
                    int widthFactor = (options.outWidth + thumbnailWidth - 1) / thumbnailWidth;
                    int heightFactor = (options.outHeight + thumbnailHeight - 1) / thumbnailHeight;
                    widthFactor = Math.max(widthFactor, heightFactor);
                    widthFactor = Math.max(widthFactor, 1);
                    
                    // Now turn it into a power of two.
                    if (widthFactor > 1) {
                        if ((widthFactor & (widthFactor-1)) != 0) {
                            while ((widthFactor & (widthFactor-1)) != 0) {
                                widthFactor &= widthFactor-1;
                            }
                            widthFactor <<= 1;
                        }
                    }
                    options.inSampleSize = widthFactor;
                    options.inJustDecodeBounds = false;
                    Bitmap bitmap = BitmapFactory.decodeFile(bean.getPath(), options);            
                    if (bitmap != null) {
                        bean.setImage(bitmap);  //  读取到图片资源,加入到FileItem对象中
                        publishProgress();  // 通知去更新UI
                    }
                }
            } catch (Exception e) {
                // That's okay, guess it just wasn't a bitmap.
            }
        }
        return null;
    }
    
    @Override
    public void onProgressUpdate(Void... voids) {
        Log.i(TAG, "onProgressUpdate()");
        if(isCancelled()) return;
        //更新UI
        adapter.notifyDataSetChanged();
    }
}


关于本文作者
万云,从事互联网行业10年多。08年底开始基于Android平台的开发研究工作。大家可以通过 tony@toeach.net 这个邮件来和我交流技术方面的问题。


附件为本文用到的项目源码,需要的朋友请下载,并可随意的修改用于其他用途。
 Demo4FileManager.rar(64.3 KB)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多