SimpleCursorAdapter 中的图像

Posted

技术标签:

【中文标题】SimpleCursorAdapter 中的图像【英文标题】:Images in SimpleCursorAdapter 【发布时间】:2011-10-06 08:04:49 【问题描述】:

我正在尝试使用SimpleCursorAdapterViewBinder 从数据库中获取图像并将其放入我的ListView 项目视图中。这是我的代码:

private void setUpViews() 
    mNewsView = (ListView) findViewById(R.id.news_list);

    Cursor cursor = getNews();
    SimpleCursorAdapter curAdapter = new SimpleCursorAdapter(
            getApplicationContext(), R.layout.cursor_item, cursor,
            new String[]  "title", "content", "image" ,
            new int[]  R.id.cursor_title, R.id.cursor_content,
                    R.id.news_image );
    
    ViewBinder viewBinder = new ViewBinder() 
        
        public boolean setViewValue(View view, Cursor cursor,
                int columnIndex) 
            ImageView image = (ImageView) view;
            byte[] byteArr = cursor.getBlob(columnIndex);
            image.setImageBitmap(BitmapFactory.decodeByteArray(byteArr, 0, byteArr.length));
            return true;
        
    ;
    ImageView image = (ImageView) findViewById(R.id.news_image);
    viewBinder.setViewValue(image, cursor, cursor.getColumnIndex("image"));
    curAdapter.setViewBinder(viewBinder);
    mNewsView.setAdapter(curAdapter);

我得到:

android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 60

在执行byte[] byteArr = cursor.getBlob(columnIndex); 时。有谁知道我做错了什么?

【问题讨论】:

【参考方案1】:

我扩展了 SimpleCursorAdapter,虽然我没有使用 ViewBinder,但这里是我的代码,用于在列表视图的 sqlite 数据库中使用存储为 blob 的图像。这是改编自我读过的一篇文章here。

我的一行布局文件是:

row_layout_two_line.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:background="@drawable/select_item">

<ImageView 
    android:id="@+id/pic"
    android:layout_
    android:layout_
    android:layout_marginLeft="10dp"
    android:contentDescription="@string/imagedesc"
    android:src="@drawable/icon"
    android:layout_gravity="center_vertical">
</ImageView>

<LinearLayout
    android:id="@+id/linearLayout0"
    android:layout_
    android:layout_
    android:layout_gravity="center_vertical" 
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_
        android:layout_ >

        <TextView
            android:id="@+id/label"
            android:layout_
            android:layout_
            android:layout_marginLeft="10dp"
            android:text="@+id/label"
            android:textStyle="bold"
            android:textColor="#000"
            android:textSize="20sp" >
        </TextView>

        <TextView
            android:id="@+id/label1"
            android:layout_
            android:layout_
            android:layout_marginLeft="10dp"
            android:text="@+id/label1"
            android:textStyle="bold"
            android:textColor="#000"
            android:textSize="20sp" >
        </TextView>

    </LinearLayout>

    <TextView
        android:id="@+id/label2"
        android:layout_
        android:layout_
        android:layout_marginLeft="35dp"
        android:text="@+id/label2"
        android:textColor="#000"
        android:textSize="15sp" >
    </TextView>
</LinearLayout>

调用代码

...
    adapter = null;
    mCursor = search();

    startManagingCursor(mCursor);
    // Now create a new list adapter bound to the cursor.
    BaseAdapter adapter = new ImageCursorAdapter(this, // Context.
            R.layout.row_layout_two_line, // Specify the row template
                                                    // to use (here, two
                                                    // columns bound to the
                                                    // two retrieved cursor
                                                    // rows).
            mCursor, // Pass in the cursor to bind to.
            // Array of cursor columns to bind to.
            new String [] "personImage", "firstName", "lastName", "title",
            // Parallel array of which template objects to bind to those
            // columns.
            new int[]  R.id.pic, R.id.label, R.id.label1, R.id.label2 );


    // Bind to our new adapter.
    setListAdapter(adapter);
...

ImageCursorAdapter.java

import android.content.Context;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

public class ImageCursorAdapter extends SimpleCursorAdapter 

    private Cursor c;
    private Context context;

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


public View getView(int pos, View inView, ViewGroup parent) 
       View v = inView;
       if (v == null) 
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = inflater.inflate(R.layout.row_layout_two_line, null);
       
       this.c.moveToPosition(pos);      
       String firstName = this.c.getString(this.c.getColumnIndex("firstName"));
       String lastName = this.c.getString(this.c.getColumnIndex("lastName"));
       String titleStr = this.c.getString(this.c.getColumnIndex("title"));
       byte[] image = this.c.getBlob(this.c.getColumnIndex("personImage"));
       ImageView iv = (ImageView) v.findViewById(R.id.pic);
       if (image != null) 
           // If there is no image in the database "NA" is stored instead of a blob 
           // test if there more than 3 chars "NA" + a terminating char if more than
           // there is an image otherwise load the default
           if(image.length > 3)
           
               iv.setImageBitmap(BitmapFactory.decodeByteArray(image, 0, image.length));
           
           else
           
               iv.setImageResource(R.drawable.icon);
           
       
           TextView fname = (TextView) v.findViewById(R.id.label);
           fname.setText(firstName);

           TextView lname = (TextView) v.findViewById(R.id.label1);
           lname.setText(lastName);

           TextView title = (TextView) v.findViewById(R.id.label2);
           title.setText(titleStr);
       return(v);

这是最后的样子

【讨论】:

如果我要在 getView 中手动构建视图,那么使用 SimpleCursorAdapter 并告诉它来自列的意义何在????【参考方案2】:

我认为 cursor.moveToFirst() 尚未被调用,因此光标正在抛出 android.database.CursorIndexOutOfBoundsException.

在使用cursor 之前,您应该始终通过调用cursor.moveToFirst() 检查光标是否为空。这也会将光标定位在第一个位置。

【讨论】:

【参考方案3】:

联系人列表使用 ListView 和 SimpleCusorAdapter 以及联系人照片和过滤器/搜索

我一直在寻找一个更简单的解决方案,我的最终解决方案与 Daniel 在这里提到的解决方案非常接近,所以我想我应该在这里分享我的解决方案。我正在使用 Fragment 将设备联系人显示为带有图片的名称列表。结果与 Daniel 的结果非常相似,但仅显示名称。了解代码后,可以很容易地显示更多信息。

在我的例子中,我使用 PHOTO_URI 从 ContactsContract 获取姓名和图片,因此我不必像 Daniel 那样扩展 SimpleCursorAdapter

我的示例还包括过滤联系人列表作为SearchView 中的用户类型以查找联系人

我有一个名为FragmentContacts 的片段和两个布局文件,第一个是主布局frag_contacts.xml,第二个是每个联系人行list_row_contact

frag_contacts.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:orientation="vertical">
    <FrameLayout
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:padding="8dip">

        <android.support.v7.widget.SearchView
            android:layout_
            android:layout_
            android:background="@android:color/white"
            android:id="@+id/searchView"/>
    </FrameLayout>
    <LinearLayout
        android:id="@+id/ll_contactList"
        android:layout_
        android:layout_
        android:layout_weight="9"
        android:orientation="vertical" >
        <ListView
            android:id="@+id/lv_ContactList"
            android:layout_
            android:layout_
            android:divider="#aaaaaa"
            android:dividerHeight="1dp" >
        </ListView>
    </LinearLayout>
</LinearLayout>

list_row_contact.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="horizontal"
    android:descendantFocusability="blocksDescendants">

    <FrameLayout
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:gravity="center">

        <ImageView
            android:id="@+id/imgContact"
            android:layout_
            android:layout_
            android:layout_gravity="center"
            android:layout_margin="5dip" />
    </FrameLayout>

    <TextView
        android:id="@+id/contact_name"
        android:layout_
        android:layout_
        android:layout_weight="6"
        android:gravity="center_vertical"
        android:textSize="18sp"
        android:paddingLeft="10dip">
    </TextView>
</LinearLayout>

FragmentContacts.java

public class FragmentContacts extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
    private ListView lv_ContactList;
    private SearchView searchView;
    private SimpleCursorAdapter mCursorAdapter;

    private static final String DISPLAY_NAME = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME;

    private static final String[] contactsColumns =     Contacts._ID, Contacts.LOOKUP_KEY, DISPLAY_NAME, Contacts.PHOTO_URI    ;
    private final String contactsFilter = "(" +Contacts.HAS_PHONE_NUMBER+ "='1') AND (" + Contacts.IN_VISIBLE_GROUP + "='1')";
    private final String contactsSortOrder = DISPLAY_NAME + " COLLATE LOCALIZED ASC";

    private final static String[] listDisplayColumns =  DISPLAY_NAME, Contacts.PHOTO_URI   ;
    private final static int[] listDataViewIDs =  R.id.contact_name, R.id.imgContact ;

    String[] mSelectionArgs;

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        View view = inflater.inflate(R.layout.frag_contacts, null);
        lv_ContactList = (ListView)view.findViewById(R.id.lv_ContactList);
        searchView = (SearchView)view.findViewById( R.id.searchView);
        return view;
    

    @Override
    public void onResume()
        super.onResume();

        mCursorAdapter= new SimpleCursorAdapter( getActivity(), R.layout.list_row_contact, null, listDisplayColumns, listDataViewIDs, 0);
        lv_ContactList.setAdapter(mCursorAdapter);
        getLoaderManager().initLoader(0, null, this);
        searchView.setOnQueryTextListener( new SearchView.OnQueryTextListener() 
            @Override
            public boolean onQueryTextSubmit( String query ) return false;
            @Override
            public boolean onQueryTextChange( String newText ) 
                if( newText.isEmpty() ) mSelectionArgs = null;
                else mSelectionArgs = new String[] "%"+newText.trim()+"%";
                getLoaderManager().restartLoader( 0, null, FragmentContacts.this );
                return false;
            
         );
    

    @Override
    public Loader<Cursor> onCreateLoader( int id, Bundle args ) 
        if(mSelectionArgs == null)
            return new CursorLoader( getActivity(), Contacts.CONTENT_URI, contactsColumns, contactsFilter, null, contactsSortOrder );
        else
            return new CursorLoader( getActivity(), Contacts.CONTENT_URI, contactsColumns, contactsFilter + " AND (" + DISPLAY_NAME+" LIKE ?)", mSelectionArgs, contactsSortOrder );
    
    @Override
    public void onLoadFinished( Loader<Cursor> loader, Cursor data ) 
        mCursorAdapter.swapCursor(data);
    
    @Override
    public void onLoaderReset( Loader<Cursor> loader ) 
        mCursorAdapter.swapCursor(null);
    

【讨论】:

以上是关于SimpleCursorAdapter 中的图像的主要内容,如果未能解决你的问题,请参考以下文章

SimpleCursorAdapter无法在MainActivity中使用

数据库更改时,Android SimpleCursorAdapter 不会更新

使用详解及源码解析Android中的AdapterBaseAdapterArrayAdapterSimpleAdapter和SimpleCursorAdapter

当 SimpleCursorAdapter 将值从表添加到 listView 时检查列值

使用 SimpleCursorAdapter 从 Cursor 更改值

SimpleCursorAdapter使用代码