我是android开发新手,请帮忙。我有一个 RecyclerView – 它有一个相对的项目列表,每个项目都有机会将其添加到收藏夹。
单击 ImageButton 时,会调用 updateAddToFavourite 方法,该方法应该更新数据库中记录上的数据(将列中的值从 0 更改为 1),但是当我启动应用程序并单击收藏夹按钮时,我收到错误:
致命异常:主要 进程:com.example.foodmap,PID:16170 kotlin.UninitializedPropertyAccessException:lateinit 属性数据库尚未初始化
我做错了什么?
MainActivity.kt
package com.example.foodmap
import android.content.Intent
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.RecyclerView
import com.example.foodmap.DatabaseHelper
import com.example.foodmap.DatabaseHelper.columnProducts_id
import com.example.foodmap.DatabaseHelper.columnSafetyIndicator
import com.example.foodmap.DatabaseHelper.tableProducts
import com.example.foodmap.databinding.ActivityMainBinding
import com.mikepenz.materialdrawer.AccountHeader
import com.mikepenz.materialdrawer.AccountHeaderBuilder
import com.mikepenz.materialdrawer.Drawer
import com.mikepenz.materialdrawer.DrawerBuilder
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
class MainActivity : AppCompatActivity(), OnItemClickListener {
private lateinit var binding: ActivityMainBinding
private lateinit var databaseHelper: DatabaseHelper
lateinit var db: SQLiteDatabase
private lateinit var productCursor: Cursor
private lateinit var fodmapCursor: Cursor
private lateinit var productAdapter: ProductAdapter
private lateinit var recyclerView: RecyclerView
private lateinit var mDrawer: Drawer
private lateinit var mHeader: AccountHeader
private lateinit var mToolbar: Toolbar
private val TAG = "MainActivity" // костыль чтобы вызвать метод из MainActivity
private fun initial() {
recyclerView = binding.rvProducts
productAdapter = ProductAdapter(myProducts(), this)
recyclerView.adapter = productAdapter
mToolbar = binding.mainToolbar
}
private fun initFunc() {
setSupportActionBar(mToolbar)
createHeader()
createDrawer()
}
private fun createDrawer() {
mDrawer =
DrawerBuilder()
.withActivity(this)
.withToolbar(mToolbar)
.withActionBarDrawerToggle(true)
.withSelectedItem(-1)
.withAccountHeader(mHeader)
.addDrawerItems(
PrimaryDrawerItem().withIdentifier(1)
.withIconTintingEnabled(true)
.withName("Профиль пользователя")
.withSelectable(false),
PrimaryDrawerItem().withIdentifier(2)
.withIconTintingEnabled(true)
.withName("Мой дневник")
.withSelectable(false),
PrimaryDrawerItem().withIdentifier(3)
.withIconTintingEnabled(true)
.withName("Мои продукты")
.withSelectable(false),
PrimaryDrawerItem().withIdentifier(4)
.withIconTintingEnabled(true)
.withName("Мои рецепты")
.withSelectable(false),
PrimaryDrawerItem().withIdentifier(5)
.withIconTintingEnabled(true)
.withName("Выход")
.withSelectable(false)
).withOnDrawerItemClickListener(object : Drawer.OnDrawerItemClickListener {
override fun onItemClick(
view: View?,
position: Int,
drawerItem: IDrawerItem<*>
): Boolean {
val newIntent = Intent(this@MainActivity, SelectedProducts::class.java)
when (position) {
3 -> startActivity(newIntent)
}
return false
}
}).build()
}
private fun createHeader() {
mHeader = AccountHeaderBuilder()
.withActivity(this)
.withHeaderBackground(R.drawable.header)
.addProfiles(
ProfileDrawerItem()
.withName("Денис Белозёров")
.withIcon(R.drawable._avatar180)
.withEmail("[email protected]")
)
.build()
}
private fun myProducts(): ArrayList<ProductModel> {
val productList = ArrayList<ProductModel>()
val count: Int = productCursor.count
var i = 0
while (i < count) {
var colorSafety = getColor(R.color.green) // храним цвет каждого продукта
productCursor.moveToPosition(i)
val nameProduct: String =
productCursor.getString(productCursor.getColumnIndexOrThrow(DatabaseHelper.columnProductsName))
val categoryProduct: String =
productCursor.getString(productCursor.getColumnIndexOrThrow(DatabaseHelper.columnCategoryName))
val safetyIndicator: String =
productCursor.getString(productCursor.getColumnIndexOrThrow(DatabaseHelper.columnSafetyIndicator))
when (safetyIndicator) {
"Безопасен" -> colorSafety = getColor(R.color.green)
"Небезопасен" -> colorSafety = getColor(R.color.red)
"Умеренно безопасен" -> colorSafety = getColor(R.color.yellow)
}
productList.add(ProductModel(nameProduct, categoryProduct, colorSafety))
i++
}
return productList
}
private fun createAlertDialog(
title: String,
olygos: String,
fructose: String,
polyols: String,
lactose: String,
weight: String
) {
val productDialogView =
LayoutInflater.from(this).inflate(R.layout.custom_alert_dialog_info_products_1, null)
val builder = AlertDialog.Builder(this).setView(productDialogView).setTitle(title)
val textViewWeight: TextView = productDialogView.findViewById(R.id.Product_weight_1_1_1)
val textViewOlygos: TextView = productDialogView.findViewById(R.id.Olygos_1_1_2)
val textViewFructose: TextView = productDialogView.findViewById(R.id.Fructose_1_1_3)
val textViewPolyols: TextView = productDialogView.findViewById(R.id.Polyols_1_1_4)
val textViewLactose: TextView = productDialogView.findViewById(R.id.Lactose_1_1_5)
textViewWeight.setText(weight + " гр")
textViewOlygos.setText(olygos)
textViewFructose.setText(fructose)
textViewPolyols.setText(polyols)
textViewLactose.setText(lactose)
builder.setPositiveButton("OK") { dialog, which ->
}
builder.show()
}
override fun onItemClicked(product: ProductModel) {
val queryFodmap =
"SELECT Продукты._id, Продукты.[Название продукта], Продукты.[Наличие глютена], FODMAP.[Единицы измерения гр], FODMAP.[Наличие Olygos (олигосахариды)], FODMAP.[Наличие Fructose (фруктоза)], FODMAP.[Наличие Polyols (полиолы)], FODMAP.[Наличие Lactose (лактоза)] FROM Продукты INNER JOIN FODMAP ON Продукты.[Название продукта] = FODMAP.[Название продукта] WHERE Продукты.[Название продукта] = \"${product.product}\""
fodmapCursor = db.rawQuery(queryFodmap, null)
fodmapCursor.moveToFirst()
val itemOlygos =
fodmapCursor.getString(fodmapCursor.getColumnIndexOrThrow(DatabaseHelper.columnFodmapOlygos)) // извлекаем характеристики продукта и передаем в AlertDialog
val itemFructose =
fodmapCursor.getString(fodmapCursor.getColumnIndexOrThrow(DatabaseHelper.columnFodmapFructose))
val itemPolyols =
fodmapCursor.getString(fodmapCursor.getColumnIndexOrThrow(DatabaseHelper.columnFodmapPolyols))
val itemLactose =
fodmapCursor.getString(fodmapCursor.getColumnIndexOrThrow(DatabaseHelper.columnFodmapLactose))
val itemWeight =
fodmapCursor.getString(fodmapCursor.getColumnIndexOrThrow(DatabaseHelper.columnFodmapUnit))
createAlertDialog(
"${product.product}",
itemOlygos,
itemFructose,
itemPolyols,
itemLactose,
itemWeight
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
databaseHelper = DatabaseHelper(applicationContext)
databaseHelper.create_db()
}
override fun onResume() {
super.onResume()
db = databaseHelper.open()
val queryProducts =
"SELECT * FROM " + DatabaseHelper.tableProducts + " INNER JOIN " + DatabaseHelper.tableCategory + " ON " + "Продукты.[Категория продукта] = Категория.[Код категории]"
val queryFodmap =
"SELECT Продукты._id, Продукты.[Название продукта], Продукты.[Наличие глютена], FODMAP.[Единицы измерения гр], FODMAP.[Наличие Olygos (олигосахариды)], FODMAP.[Наличие Fructose (фруктоза)], FODMAP.[Наличие Polyols (полиолы)], FODMAP.[Наличие Lactose (лактоза)]\n" +
"FROM Продукты INNER JOIN FODMAP ON Продукты.[Название продукта] = FODMAP.[Название продукта]"
productCursor = db.rawQuery(queryProducts, null)
fodmapCursor = db.rawQuery(queryFodmap, null)
initial()
initFunc()
}
override fun onDestroy() {
super.onDestroy()
db.close()
productCursor.close()
}
companion object {
val instance = MainActivity()
}
fun updateAddToFavourite(position: Int) {
db.execSQL("UPDATE ${DatabaseHelper.tableProducts} SET ${DatabaseHelper.columnSafetyIndicator} = 1 WHERE ${DatabaseHelper.columnProducts_id} == $position")
}
}
DatabaseHelper.java
package com.example.foodmap;
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class DatabaseHelper extends SQLiteOpenHelper {
private static String DB_PATH;
private static String DB_NAME = "fodmap.db";
private static final int SCHEMA = 1;
static final String tableProducts = "Продукты";
static final String columnProducts_id = "_id";
static final String columnProductsName = "Название продукта";
static final String columnProductsCode = "Код продукта";
static final String columnProductsCategory = "Категория продукта";
static final String columnProductsGluten = "Наличие глютена";
static final String columnProductsProtein = "Содержание белков (на 100 гр)";
static final String columnProductsFats = "Содержание жиров (на 100 гр)";
static final String columnProductsCarb = "Содержание углеводов (на 100 гр)";
static final String columnSafetyIndicator = "Индикатор безопасности";
static final String tableCategory = "Категория";
static final String columnCategoryCode = "Код категории";
static final String columnCategoryName = "Название категории";
static final String tableFODMAP = "FODMAP";
static final String columnFodmapProducts = "Название продукта";
static final String columnFodmapUnit = "Единицы измерения гр";
static final String columnFodmapOlygos = "Наличие Olygos (олигосахариды)";
static final String columnFodmapFructose = "Наличие Fructose (фруктоза)";
static final String columnFodmapPolyols = "Наличие Polyols (полиолы)";
static final String columnFodmapLactose = "Наличие Lactose (лактоза)";
private Context myContext;
DatabaseHelper(Context context) {
super(context, DB_NAME, null, SCHEMA);
this.myContext = context;
DB_PATH = context.getFilesDir().getPath() + DB_NAME;
}
@Override
public void onCreate(SQLiteDatabase db) { }
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
void create_db(){
File file = new File(DB_PATH);
if (!file.exists()) {
//получаем локальную бд как поток
try(InputStream myInput = myContext.getAssets().open(DB_NAME);
// Открываем пустую бд
OutputStream myOutput = new FileOutputStream(DB_PATH)) {
// побайтово копируем данные
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.flush();
}
catch(IOException ex){
Log.d("DatabaseHelper", ex.getMessage());
}
}
}
public SQLiteDatabase open()throws SQLException {
return SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);
}
}
产品适配器.kt
package com.example.foodmap
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.example.foodmap.databinding.ItemProductsLayoutBinding
class MyHolder(val binding: ItemProductsLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
val name_product = binding.nameProduct
val name_category = binding.categoryProduct
val safety_indicator: ImageView = binding.safetyIndicator
val add_to_favorite: ImageButton = binding.addToFavorite
var buttonOn: Boolean = false
fun bind(product: ProductModel, clickListener: OnItemClickListener) {
name_product.text = product.product
name_category.text = product.category
safety_indicator.setColorFilter(product.safetyIndicator)
itemView.setOnClickListener {
clickListener.onItemClicked(product)
}
}
}
class ProductAdapter(
private var products: ArrayList<ProductModel>,
val itemClickListener: OnItemClickListener
) : RecyclerView.Adapter<MyHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_products_layout, parent, false)
return MyHolder(ItemProductsLayoutBinding.inflate(LayoutInflater.from(parent.context), parent,false))
}
override fun onBindViewHolder(myHolder: MyHolder, position: Int) {
val currentPosition = products.get(position)
myHolder.bind(currentPosition, itemClickListener)
myHolder.binding.addToFavorite.setOnClickListener {
if (!myHolder.buttonOn) {
myHolder.buttonOn = true
myHolder.add_to_favorite.setImageResource(R.drawable.baseline_favorite_24)
val getMainActivity = MainActivity.instance
getMainActivity.updateAddToFavourite(position)
} else {
myHolder.buttonOn = false
myHolder.add_to_favorite.setImageResource(R.drawable.baseline_favorite_border_24)
}
}
}
override fun getItemCount(): Int {
return products.size
}
@SuppressLint("NotifyDataSetChanged")
fun setList(list: ArrayList<ProductModel>) {
products = list
notifyDataSetChanged()
}
}
interface OnItemClickListener {
fun onItemClicked(product: ProductModel)
}
item_products_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/icon_product"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/no_image"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="2dp" >
<TextView
android:id="@+id/name_product"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_marginLeft="55dp"
android:layout_marginRight="105dp"
android:layout_toRightOf="@id/icon_product"
android:gravity="center_vertical"
android:textColor="@color/black"
android:textSize="16sp"
android:text="Product"
android:textStyle="bold" />
<TextView
android:id="@+id/category_product"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_marginLeft="55dp"
android:layout_marginRight="105dp"
android:layout_toRightOf="@id/icon_product"
android:gravity="center_vertical"
android:textColor="@color/black"
android:textSize="14sp"
android:text="Category"
android:textStyle="italic" />
</LinearLayout>
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/safety_indicator"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_toLeftOf="@+id/add_to_favorite"
android:layout_centerVertical="true"
android:src="@color/red"
app:civ_border_width="1dp"
app:civ_border_color="#FF000000"/>
<ImageButton
android:id="@+id/add_to_favorite"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:src="@drawable/baseline_favorite_border_24"
android:background="@null"
android:layout_alignParentRight="true" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
我不明白为什么初始化数据库会出错,所以我尝试在OnStart方法中添加初始化,但这并没有导致变化。
错误消息“kotlin.UninitializedPropertyAccessException:lateinit property db尚未初始化”表示您在初始化之前尝试使用
db
属性。要解决此问题,您需要确保 db
属性在使用之前已正确初始化。
在您的
MainActivity
中,您应该通过在 db
或 databaseHelper.open()
方法中调用 onCreate
方法来初始化 onResume
属性。这可确保在执行任何数据库操作之前数据库已打开并可供使用。
此外,请确保调整代码中可能依赖于初始化数据库才能正常工作的其他部分。
有关 Android 开发和使用数据库的进一步指导,您可以参考有关 SQLite 数据库的 Android 官方文档: Android SQLite 数据库文档