【问题标题】:Android custom SimpleCursorAdapter with image from file with path in databaseAndroid 自定义 SimpleCursorAdapter 带有来自文件中的图像和数据库中的路径
【发布时间】:2013-08-30 17:50:41
【问题描述】:

要点:自定义适配器通过数据库中的文件路径间接获取文件资源。效率低下/内存问题。征求您的意见。

所有搜索的引用、相关链接和有用的话题都在帖子底部。

以下代码有效,但有几个因素值得关注。需要一些更有经验的眼睛来建议改进或避免潜在的错误。应用程序不需要是内容提供者(仅来自应用程序的本地数据)。有问题的 ListView 重量很轻,只有大约 5 到最多 10 个条目。 (我省略了数据库的东西,因为它可以工作。)

概述:

  • DataBase 包含一些文本和一个图像文件路径。 - 好的
  • 图像文件存储在设备上(SD 卡/外部存储,无论何时)。 - 好的

文件不在数据库中,这与普通的 SimpleCursorAdapter 不同 - 必须提取图像文件。在填充列表视图之前添加了将其制作为缩略图的开销。

如上所述,它很轻,但是,即使只有一两个条目,VM 也会打嗝。我怀疑这都是与位图相关的内存抖动:

08-27 19:53:14.273: I/dalvikvm-heap(11900): Grow heap (frag case) to 4.075MB for 1228816-byte allocation
08-27 19:53:14.393: D/dalvikvm(11900): GC_CONCURRENT freed <1K, 5% free 4032K/4244K, paused 13ms+3ms, total 116ms

/* myTextAndImageCursorAdapter.java */

import android.widget.SimpleCursorAdapter;

//import android.support.v4.widget.SimpleCursorAdapter;
import android.content.Context;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import android.database.Cursor;
import java.io.File;

import android.widget.ImageView;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import static android.media.ThumbnailUtils.extractThumbnail;


public class TextAndImageCursorAdapter extends SimpleCursorAdapter { 

    private Context context;
    private int layout;

    public TextAndImageCursorAdapter (Context ctx, int layout, Cursor c, String[] from, int[] to) {
        super(context, layout, c, from, to);
        this.context = ctx;
        this.layout = layout;
    }

    @Override
    public View newView(Context ctx, Cursor cursor, ViewGroup parent) {

        Cursor c = getCursor();

        final LayoutInflater inflater = LayoutInflater.from(ctx);
        View vView = inflater.inflate(layout, parent, false);

        int iCol_Text = c.getColumnIndex(DBCOL_TEXT);
        int iCol_Image = c.getColumnIndex(DBCOL_IMAGE);

        String sText = c.getString(iCol_Text);
        String sFileAndPath_Image = c.getString (iCol_Image);  //// sImage path & file

        TextView tvText = (TextView) v.findViewById(R.id.gui_text);
        if (tvText != null) {
            tvText.setText(sSomeText);
        }

        ImageView ivImage (ImageView) v.findViewById(R.id.gui_image);
        if (ivImage != null) {
            ivImage.setImage (mySetImage (sFileAndPath_Image) );
        }

        return vView;
    }

    @Override
    public void bindView(View v, Context ctx, Cursor c) {
        //// ( like newView(), without an inflater, view, or return ) 
        int iCol_Text = c.getColumnIndex(DBCOL_TEXT);
        int iCol_Image = c.getColumnIndex(DBCOL_IMAGE);

        String sText = c.getString(iCol_Text);
        String sFileAndPath_Image = c.getString (iCol_Image);  //// path & file

        TextView tvText = (TextView) v.findViewById(R.id.gui_text);
        if (tvText != null) {
            tvText.setText(sSomeText);
        }

        ImageView ivImage (ImageView) v.findViewById(R.id.gui_image);
        if (ivImage != null) {
            ivImage.setImageBitmap ( mySetImage ( sFileAndPath_Image ) ) ;
        }
    }
    /////
    /////
    protected Bitmap mySetImage ( String path ) {
        int width = 60; int height = 40 ;

        File imgFile = new File ( path );  //// usually like: \sdcard0\wherever\filename1234.bmp
        Bitmap myBitmap = null;

        if( imgFile.exists() )
        {
                myBitmap = BitmapFactory.decodeFile ( imgFile.getAbsolutePath () );                  
        }
            else                    
                Log.d ("oops", "no image file ... using default.");
                myBitmap = getTheDefaultImage ();  //// not shown - this is arbitrary
        }

        imgFile.close();
        return ( extractThumbnail ( myBitmap, width, height ) ) ;
    }      
}

[编辑 - 添加链接] 搜索条件: " Android 自定义 simplecursoradapter,带有来自数据库中路径的文件中的图像 "

最近的命中,但尝试从 res 中提取图像,而不是从外部/sd 存储(未回答): SimpleCursorAdapter how to show an image?

也是近乎命中/未命中 - 类似算法(未回答)参考 4): Customizing list shown from SimpleCursorAdapter using ViewBinder

几乎,但 OP 的代码不起作用,(没有有效的答案): Load Image in a custom list by SimpleCursorAdapter

为 OP 工作,但使用 JSON 进行远程检索,而不是本地检索(也许这可以调整,但我不清楚如何)。 How to show images in imageview in simple adapter?

不完全是,但再次关闭: ListView scroll slow while loading image from Internal Storage

Image Loader 问题(参考文献 2): Imageloader not loading image on real device

相关链接:

Android Custom Cursor Adapter

Android: Issue with newView and bindView in custom SimpleCursorAdapter

同名点击,但与我的具体问题无关 - 这些通常是指应用内资源: show image from database where you saved the path of image

Custom SimpleCursorAdapter error

Custom SimpleCursorAdapter, database query and NullPointerException

nullPointerException with extended SimpleCursorAdapter

Android SimpleCursorAdapter - Adding conditional images

外部参考:

0) 自定义光标适配器的简单介绍 http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/

1) Romain Guy - 基本布局... 2 txts, 1 image http://www.curious-creature.org/2009/02/22/android-layout-tricks-1/

2) AQuery(Android 查询) http://code.google.com/p/android-query/wiki/ImageLoading

3) Android 缩略图 http://developer.android.com/reference/android/media/ThumbnailUtils.html

4) 定制。带有“开/关”星形图像的列表视图: http://enjoyandroid.wordpress.com/2012/03/12/customizing-simple-cursor-adapter/

【问题讨论】:

    标签: android image data-binding android-cursoradapter


    【解决方案1】:

    你可以做两件事:

    1) 使用 ViewHolder 模式,缓存 LayoutInfalter 最重要的是:不要绑定数据两次:

    /* ... imports */
    
    import static android.media.ThumbnailUtils.extractThumbnail;
    
    public class TextAndImageCursorAdapter extends SimpleCursorAdapter { 
    
        private LayoutInflater mLayoutInflater;
        private Context context;
        private int layout;
    
    
        private class ViewHolder {
            TextView textView;
            ImageView imageView;
    
            ViewHolder(View v) {
                textView = (TextView) v.findViewById(R.id.gui_text);
                imageView = (ImageView) v.findViewById(R.id.gui_image);
            }
        }
    
        public TextAndImageCursorAdapter (Context ctx, int layout, Cursor c, String[] from, int[] to) {
            super(ctx, layout, c, from, to);
            this.context = ctx;
            this.layout = layout;
            mLayoutInflater = LayoutInflater.from(ctx);
        }
    
    
        @Override
        public View newView(Context ctx, Cursor cursor, ViewGroup parent) {
            View vView = mLayoutInflater.inflate(layout, parent, false);
            vView.setTag( new ViewHolder(vView) );
            // no need to bind data here. you do in later
            return vView;// **EDITED:**need to return the view
        }
    
        @Override
        public void bindView(View v, Context ctx, Cursor c) {
            // you might want to cache these too
            int iCol_Text = c.getColumnIndex(DBCOL_TEXT);
            int iCol_Image = c.getColumnIndex(DBCOL_IMAGE);
    
            String sText = c.getString(iCol_Text);
            String sFileAndPath_Image = c.getString (iCol_Image);  //// path & file
    
            ViewHolder vh = (ViewHolder) v.getTag();
    
            vh.textView.setText(sSomeText);
            vh.imageView.setImageBitmap ( mySetImage ( sFileAndPath_Image ) );
        }
    }
    

    2) 这非常重要:不要在每次绑定时都创建缩略图。你需要缓存结果:

    private void setThumbnail(String path, Bitmap b) {
        // save thumbnail to some kind of cache
        // see comment below
    }
    
    private Bitmap getThumbnail(String path) {
        Bitmap thumbnail = null;
        // try to fetch the thumbnail from some kind of cache
        // see comment below
        return thumbnail;
    }
    
    protected Bitmap mySetImage ( String path ) {
        int width = 60; int height = 40 ;
    
        Bitmap thumbnail = getThumbnail(path); // try to fetch thumbnail
        if (thumbnail != null) return thumbnail;
    
        File imgFile = new File ( path );  //// usually like: /sdcard/wherever/filename1234.bmp
        Bitmap myBitmap = null;
    
        if( imgFile.exists() ) {
                myBitmap = BitmapFactory.decodeFile ( imgFile.getAbsolutePath () );                  
        } else {
                Log.d ("oops", "no image file ... using default.");
                myBitmap = getTheDefaultImage ();  //// not shown - this is arbitrary
        }
    
        imgFile.close();
        thumbnail = extractThumbnail ( myBitmap, width, height );
        myBitmap.recycle();
        setThumbnail(path, thumbnail); // save thumbnail for later reuse
        return thumbnail;
    }     
    

    根据您的用例,您想用某种 LruCache 填充 getThumbnail()setThumbnail()

    编辑:

      @Override
    public View newView(Context ctx, Cursor cursor, ViewGroup parent) {
        View vView = mLayoutInflater.inflate(layout, parent, false);
        vView.setTag( new ViewHolder(vView) );
        // no need to bind data here. you do in later
        return vView;// **EDITED:**need to return the view
    }
    

    【讨论】:

    • 谢谢! - 正在审查...这里是有关 ViewHolder 模式的相关链接:stackoverflow.com/questions/12223293/…(来自佛罗里达州的汉堡 :))
    • 澄清newView()和bindView()的区别:stackoverflow.com/questions/12672749/…(答案有很好的解释)
    • 其实我现在住在斯里兰卡 ;)
    • @DiogoBento - 是的,谢谢!当使用 ListView(或一般的视图)时,我不清楚应该在何时何地调用 Bitmap.recycle()。视图只是指向位图,还是位图实际上已复制到视图中? (后者似乎很浪费。)@ flx - 斯里兰卡?!塞尔舍恩!
    猜你喜欢
    • 2013-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    相关资源
    最近更新 更多