从 SQLite 数据库中检索图像并显示在 ListView 中
Retrieve image from SQLite database and show in ListView
遇到问题。我是 android 开发的新手。我的问题是我有一个 SQLite 数据库,我在其中保存图像和一些数据,但是当我检索该数据时,图像没有显示在列表视图中。正在显示其他数据。
这是我的自定义列表布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/petImageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="5dp"
android:layout_marginTop="0dp"
android:src="@drawable/cat1"/>
<TextView
android:id="@+id/petNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="50dp"
android:layout_marginTop="22dp"
android:layout_marginRight="50dp"
android:layout_toEndOf="@id/petImageView"
android:layout_toRightOf="@id/petImageView"
android:background="@android:color/background_light"
android:textAlignment="center"
android:textSize="35sp"
android:textStyle="bold" />
</RelativeLayout>
这是我的自定义适配器
public class CustomAdapter extends BaseAdapter {
private int layout;
private ArrayList<DataList> recordList;
private Context context;
public CustomAdapter(Context context, int layout, ArrayList<DataList> recordList) {
this.context = context;
this.recordList = recordList;
this.layout=layout;
}
public int getCount() {
return recordList.size();
}
public Object getItem(int position) {
return recordList.get(position);
}
public long getItemId(int position) {
return position;
}
private class ViewHolder{
ImageView petImageView;
TextView petNameTextView;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder = new ViewHolder();
if (v == null) {
LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = layoutInflater.inflate(layout, null);
holder.petImageView = v.findViewById(R.id.petImageView);
holder.petNameTextView = v.findViewById(R.id.petNameTextView);
v.setTag(holder);
}else{
holder = (ViewHolder)v.getTag();
}
DataList datalist = recordList.get(position);
holder.petNameTextView.setText(datalist.getName());
byte[] recordImage = datalist.getImage();
Bitmap bitmap = BitmapFactory.decodeByteArray(recordImage, 0, recordImage.length);
holder.petImageView.setImageBitmap(bitmap);
return v;
}
}
这是 Activity
public class myPetsActivity extends AppCompatActivity {
ListView myPetList;
ArrayList<DataList> petList = new ArrayList<DataList>();
CustomAdapter customAdapter;
ImageView petImageView;
DatabaseHelper mDatabaseHelper;
String name;
byte[] image;
int id;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_pets);
myPetList = findViewById(R.id.petsListView);
petList = new ArrayList<>();
customAdapter = new CustomAdapter(this, R.layout.custom_list_layout, petList);
myPetList.setAdapter(customAdapter);
mDatabaseHelper = new DatabaseHelper(this);
Cursor data = mDatabaseHelper.getData("SELECT * FROM pet_table");
petList.clear();
while(data.moveToNext()) {
id = data.getInt(0);
name = data.getString(1);
image = data.getBlob(2);
petList.add(new DataList(id, name, image));
Log.i("image",String.valueOf(image));
}
customAdapter.notifyDataSetChanged();
}
}
这是数据列表
public class DataList {
private int id;
private byte[] image ;
private String name;
public DataList(int id, String name, byte[] image){
this.id=id;
this.name=name;
this.image=image;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
尝试使用 recyclerview 而不是已弃用的 listview:developer.android.com/guide/topics/ui/layout/recyclerview 如果显示的图像不正确,则可能在您的适配器代码中。
您也可以在 while 循环之后实例化适配器。
删除这行代码:
View v = convertView;
在java中,当您将对象分配给彼此时,并不意味着它们是同一事物,它们只是指向内存中的相同引用。 Java 对待原始对象和其他对象有点不同。
按照这个例子:
https://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
您是否考虑过将图像作为字符串保存到数据库(Base64)?从数据库读取后简单地将其转换回来。在这里你有这样做的方法:
从本质上讲,您的代码没有任何问题,如图所示。所以问题可能出在将图像存储到数据库中。
- 注意请参阅有关在数据库中存储图像的评论。
工作示例
以下工作示例,仅对您的代码进行了微小的更改,然后实际上仅提供建议(一个例外是 myPetsActivity 已命名为 MyPetsActivity 以遵循通用约定)。但是,DatabaseHelper.java 已完整写入,可能无法完全反映您的 DatabaseHelper.java
一个区别是图像本身已放入资产文件夹中,并从该文件夹中检索以存储到 pet_table table 参见addPetWithImageFromAssets 方法 DatabaseHelper.java class.
另一个区别是上面的方法已经被用来用一些测试数据填充数据库(参见MyPetsActivity.java[中的addSomeData方法).一排
对于名为 Mr. 的宠物Invisible Pet 被赋予了一个不存在的图像(这将导致 BLOB 使用图像的默认值 x'00',这很好,因为它不显示) .
custom_list_layout.xml 已经删除了不必要的 RelativeLayout(如果不删除它仍然有效)。为了适合我的测试(使用 Android 4.1.1 设备),它还添加了 2 个属性 marginLeft 和 alignParentLeft。
图像
我刚刚将一些可用的 JPG 小图片复制到资产文件夹中,如下所示:-
- 注意名称是相关的,实际图像不是 (正如我所说的只是一些我躺在那里)
代码
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mypets.db";
public static final int DBVERSION = 1;
public static final String TABLE_PET = "pet_table";
public static final String COLUMN_PET_ID = BaseColumns._ID;
public static final String COLUMN_PET_NAME = "name";
public static final String COLUMN_PET_IMAGE = "image";
public static final String COLUMN_PET_IMAGEPATH = "imagepath";
SQLiteDatabase mDB;
Context mContext;
public DatabaseHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mContext = context;
mDB = this.getWritableDatabase();
}
@Override
public void onCreate(SQLiteDatabase db) {
String crt_pet_table = "CREATE TABLE IF NOT EXISTS " + TABLE_PET + "(" +
COLUMN_PET_ID + " INTEGER PRIMARY KEY, " +
COLUMN_PET_NAME + " TEXT," +
COLUMN_PET_IMAGE + " BLOB DEFAULT x'00'," +
COLUMN_PET_IMAGEPATH + " TEXT DEFAULT ''" +
")";
db.execSQL(crt_pet_table);
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
/**
* NOT USED
* @param petname
* @return
*/
public long addPet(String petname) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_PET_NAME,petname);
return mDB.insert(TABLE_PET,null,cv);
}
public long addPetWithImageFromAssets(String petname, String petimagename) {
byte[] petimage = new byte[0];
int image_size = 0;
try {
InputStream is = mContext.getAssets().open(petimagename);
image_size = is.available();
petimage = new byte[image_size];
is.read(petimage);
is.close();
} catch (IOException e) {
}
ContentValues cv = new ContentValues();
cv.put(COLUMN_PET_NAME,petname);
if (image_size > 0) {
cv.put(COLUMN_PET_IMAGE,petimage);
}
return mDB.insert(TABLE_PET,null,cv);
}
public Cursor getData(String query) {
return mDB.rawQuery(query,null);
}
}
- 注意!!! 你必须调整以上内容以适应。
DataList.java
public class DataList {
/**
* NOTE changed id to use long rather than int
*/
private long id;
private byte[] image ;
private String name;
public DataList(int id, String name, byte[] image){
this.id=id;
this.name=name;
this.image=image;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 这只是更改为对 id 使用 long,因为如果有数百万行,int 可能太小。
MyPetsActivity.java
public class MyPetsActivity extends AppCompatActivity {
ListView myPetList;
ArrayList<DataList> petList = new ArrayList<DataList>();
CustomAdapter customAdapter;
ImageView petImageView;
DatabaseHelper mDatabaseHelper;
String name;
byte[] image;
int id;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_pets);
myPetList = findViewById(R.id.petsListView);
petList = new ArrayList<>();
customAdapter = new CustomAdapter(this, R.layout.custom_list_layout, petList);
myPetList.setAdapter(customAdapter);
mDatabaseHelper = new DatabaseHelper(this);
addSomeData(); //<<<<<<<<<< FOR DEMO
Cursor data = mDatabaseHelper.getData("SELECT * FROM pet_table");
petList.clear();
while(data.moveToNext()) {
id = data.getInt(0);
name = data.getString(1);
image = data.getBlob(2);
petList.add(new DataList(id, name, image));
Log.i("image",String.valueOf(image));
}
data.close(); //<<<<<<<<<< SHOULD ALWAYS CLOSE CURSOR WHEN DONE WITH IT
customAdapter.notifyDataSetChanged();
}
private void addSomeData() {
mDatabaseHelper.getWritableDatabase().delete(DatabaseHelper.TABLE_PET,null,null); //<<<<<<<<<< Delete all pets
mDatabaseHelper.addPetWithImageFromAssets("Fluffy","mypet001.JPG");
mDatabaseHelper.addPetWithImageFromAssets("Not Fluffy","mypet002.JPG");
mDatabaseHelper.addPetWithImageFromAssets("Petty","mypet003.JPG");
mDatabaseHelper.addPetWithImageFromAssets("Mr. Invisible Pet","noimageforthispet"); //<<<<<<< ooops!!!!!
}
}
- 注意 添加方法 addSomeData 以加载一些测试数据,包括图像(添加的最后一行除外,它命名为非-现有图像文件(资产))。
CustomAdapter.java
- 不变。
activity_my_pets.xml
- 非常基本但假设。
.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<ListView
android:id="@+id/petsListView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
custom_list_layout.xml
细微的变化
<TextView
android:id="@+id/petNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="50dp"
android:layout_marginTop="22dp"
android:layout_marginRight="50dp"
android:layout_toEndOf="@id/petImageView"
android:layout_toRightOf="@id/petImageView"
android:background="@android:color/background_light"
android:textAlignment="center"
android:textSize="35sp"
android:textStyle="bold" />
结果
遇到问题。我是 android 开发的新手。我的问题是我有一个 SQLite 数据库,我在其中保存图像和一些数据,但是当我检索该数据时,图像没有显示在列表视图中。正在显示其他数据。
这是我的自定义列表布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/petImageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="5dp"
android:layout_marginTop="0dp"
android:src="@drawable/cat1"/>
<TextView
android:id="@+id/petNameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="50dp"
android:layout_marginTop="22dp"
android:layout_marginRight="50dp"
android:layout_toEndOf="@id/petImageView"
android:layout_toRightOf="@id/petImageView"
android:background="@android:color/background_light"
android:textAlignment="center"
android:textSize="35sp"
android:textStyle="bold" />
</RelativeLayout>
这是我的自定义适配器
public class CustomAdapter extends BaseAdapter {
private int layout;
private ArrayList<DataList> recordList;
private Context context;
public CustomAdapter(Context context, int layout, ArrayList<DataList> recordList) {
this.context = context;
this.recordList = recordList;
this.layout=layout;
}
public int getCount() {
return recordList.size();
}
public Object getItem(int position) {
return recordList.get(position);
}
public long getItemId(int position) {
return position;
}
private class ViewHolder{
ImageView petImageView;
TextView petNameTextView;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder = new ViewHolder();
if (v == null) {
LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = layoutInflater.inflate(layout, null);
holder.petImageView = v.findViewById(R.id.petImageView);
holder.petNameTextView = v.findViewById(R.id.petNameTextView);
v.setTag(holder);
}else{
holder = (ViewHolder)v.getTag();
}
DataList datalist = recordList.get(position);
holder.petNameTextView.setText(datalist.getName());
byte[] recordImage = datalist.getImage();
Bitmap bitmap = BitmapFactory.decodeByteArray(recordImage, 0, recordImage.length);
holder.petImageView.setImageBitmap(bitmap);
return v;
}
}
这是 Activity
public class myPetsActivity extends AppCompatActivity {
ListView myPetList;
ArrayList<DataList> petList = new ArrayList<DataList>();
CustomAdapter customAdapter;
ImageView petImageView;
DatabaseHelper mDatabaseHelper;
String name;
byte[] image;
int id;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_pets);
myPetList = findViewById(R.id.petsListView);
petList = new ArrayList<>();
customAdapter = new CustomAdapter(this, R.layout.custom_list_layout, petList);
myPetList.setAdapter(customAdapter);
mDatabaseHelper = new DatabaseHelper(this);
Cursor data = mDatabaseHelper.getData("SELECT * FROM pet_table");
petList.clear();
while(data.moveToNext()) {
id = data.getInt(0);
name = data.getString(1);
image = data.getBlob(2);
petList.add(new DataList(id, name, image));
Log.i("image",String.valueOf(image));
}
customAdapter.notifyDataSetChanged();
}
}
这是数据列表
public class DataList {
private int id;
private byte[] image ;
private String name;
public DataList(int id, String name, byte[] image){
this.id=id;
this.name=name;
this.image=image;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
尝试使用 recyclerview 而不是已弃用的 listview:developer.android.com/guide/topics/ui/layout/recyclerview 如果显示的图像不正确,则可能在您的适配器代码中。
您也可以在 while 循环之后实例化适配器。
删除这行代码:
View v = convertView;
在java中,当您将对象分配给彼此时,并不意味着它们是同一事物,它们只是指向内存中的相同引用。 Java 对待原始对象和其他对象有点不同。
按照这个例子: https://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
您是否考虑过将图像作为字符串保存到数据库(Base64)?从数据库读取后简单地将其转换回来。在这里你有这样做的方法:
从本质上讲,您的代码没有任何问题,如图所示。所以问题可能出在将图像存储到数据库中。
- 注意请参阅有关在数据库中存储图像的评论。
工作示例
以下工作示例,仅对您的代码进行了微小的更改,然后实际上仅提供建议(一个例外是 myPetsActivity 已命名为 MyPetsActivity 以遵循通用约定)。但是,DatabaseHelper.java 已完整写入,可能无法完全反映您的 DatabaseHelper.java
一个区别是图像本身已放入资产文件夹中,并从该文件夹中检索以存储到 pet_table table 参见addPetWithImageFromAssets 方法 DatabaseHelper.java class.
另一个区别是上面的方法已经被用来用一些测试数据填充数据库(参见MyPetsActivity.java[中的addSomeData方法).一排 对于名为 Mr. 的宠物Invisible Pet 被赋予了一个不存在的图像(这将导致 BLOB 使用图像的默认值 x'00',这很好,因为它不显示) .
custom_list_layout.xml 已经删除了不必要的 RelativeLayout(如果不删除它仍然有效)。为了适合我的测试(使用 Android 4.1.1 设备),它还添加了 2 个属性 marginLeft 和 alignParentLeft。
图像
我刚刚将一些可用的 JPG 小图片复制到资产文件夹中,如下所示:-
- 注意名称是相关的,实际图像不是 (正如我所说的只是一些我躺在那里)
代码
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mypets.db";
public static final int DBVERSION = 1;
public static final String TABLE_PET = "pet_table";
public static final String COLUMN_PET_ID = BaseColumns._ID;
public static final String COLUMN_PET_NAME = "name";
public static final String COLUMN_PET_IMAGE = "image";
public static final String COLUMN_PET_IMAGEPATH = "imagepath";
SQLiteDatabase mDB;
Context mContext;
public DatabaseHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mContext = context;
mDB = this.getWritableDatabase();
}
@Override
public void onCreate(SQLiteDatabase db) {
String crt_pet_table = "CREATE TABLE IF NOT EXISTS " + TABLE_PET + "(" +
COLUMN_PET_ID + " INTEGER PRIMARY KEY, " +
COLUMN_PET_NAME + " TEXT," +
COLUMN_PET_IMAGE + " BLOB DEFAULT x'00'," +
COLUMN_PET_IMAGEPATH + " TEXT DEFAULT ''" +
")";
db.execSQL(crt_pet_table);
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
/**
* NOT USED
* @param petname
* @return
*/
public long addPet(String petname) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_PET_NAME,petname);
return mDB.insert(TABLE_PET,null,cv);
}
public long addPetWithImageFromAssets(String petname, String petimagename) {
byte[] petimage = new byte[0];
int image_size = 0;
try {
InputStream is = mContext.getAssets().open(petimagename);
image_size = is.available();
petimage = new byte[image_size];
is.read(petimage);
is.close();
} catch (IOException e) {
}
ContentValues cv = new ContentValues();
cv.put(COLUMN_PET_NAME,petname);
if (image_size > 0) {
cv.put(COLUMN_PET_IMAGE,petimage);
}
return mDB.insert(TABLE_PET,null,cv);
}
public Cursor getData(String query) {
return mDB.rawQuery(query,null);
}
}
- 注意!!! 你必须调整以上内容以适应。
DataList.java
public class DataList {
/**
* NOTE changed id to use long rather than int
*/
private long id;
private byte[] image ;
private String name;
public DataList(int id, String name, byte[] image){
this.id=id;
this.name=name;
this.image=image;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 这只是更改为对 id 使用 long,因为如果有数百万行,int 可能太小。
MyPetsActivity.java
public class MyPetsActivity extends AppCompatActivity {
ListView myPetList;
ArrayList<DataList> petList = new ArrayList<DataList>();
CustomAdapter customAdapter;
ImageView petImageView;
DatabaseHelper mDatabaseHelper;
String name;
byte[] image;
int id;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_pets);
myPetList = findViewById(R.id.petsListView);
petList = new ArrayList<>();
customAdapter = new CustomAdapter(this, R.layout.custom_list_layout, petList);
myPetList.setAdapter(customAdapter);
mDatabaseHelper = new DatabaseHelper(this);
addSomeData(); //<<<<<<<<<< FOR DEMO
Cursor data = mDatabaseHelper.getData("SELECT * FROM pet_table");
petList.clear();
while(data.moveToNext()) {
id = data.getInt(0);
name = data.getString(1);
image = data.getBlob(2);
petList.add(new DataList(id, name, image));
Log.i("image",String.valueOf(image));
}
data.close(); //<<<<<<<<<< SHOULD ALWAYS CLOSE CURSOR WHEN DONE WITH IT
customAdapter.notifyDataSetChanged();
}
private void addSomeData() {
mDatabaseHelper.getWritableDatabase().delete(DatabaseHelper.TABLE_PET,null,null); //<<<<<<<<<< Delete all pets
mDatabaseHelper.addPetWithImageFromAssets("Fluffy","mypet001.JPG");
mDatabaseHelper.addPetWithImageFromAssets("Not Fluffy","mypet002.JPG");
mDatabaseHelper.addPetWithImageFromAssets("Petty","mypet003.JPG");
mDatabaseHelper.addPetWithImageFromAssets("Mr. Invisible Pet","noimageforthispet"); //<<<<<<< ooops!!!!!
}
}
- 注意 添加方法 addSomeData 以加载一些测试数据,包括图像(添加的最后一行除外,它命名为非-现有图像文件(资产))。
CustomAdapter.java
- 不变。
activity_my_pets.xml
- 非常基本但假设。
.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<ListView
android:id="@+id/petsListView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
custom_list_layout.xml
细微的变化
<TextView android:id="@+id/petNameTextView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_marginLeft="50dp" android:layout_marginTop="22dp" android:layout_marginRight="50dp" android:layout_toEndOf="@id/petImageView" android:layout_toRightOf="@id/petImageView" android:background="@android:color/background_light" android:textAlignment="center" android:textSize="35sp" android:textStyle="bold" />