java - Sqlite+Recyclerview ,应用已停止
问题描述
我想创建一个可以从数据库中检索数据的recyclerview,并一步一步地跟随这个视频。当我尝试运行它时,应用程序“已停止”。我不知道我在哪里弄错了。请帮忙。
activity_main.xml
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:padding="16dp">
<EditText
android:id="@+id/edittext_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textviewamount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/edittext_name"
android:layout_centerHorizontal="true"
android:text="0"
android:textSize="50sp"
android:layout_marginTop="8dp"
/>
<Button
android:id="@+id/button_decrease"
android:layout_width="50dp"
android:layout_height="72dp"
android:layout_alignTop="@+id/textviewamount"
android:layout_toStartOf="@+id/textviewamount"
android:layout_toLeftOf="@+id/textviewamount"
android:text="-" />
<Button
android:id="@+id/button_increase"
android:layout_width="50dp"
android:layout_height="72dp"
android:layout_alignTop="@+id/textviewamount"
android:layout_toEndOf="@+id/textviewamount"
android:layout_toRightOf="@+id/textviewamount"
android:text="+" />
<Button
android:id="@+id/button_add"
android:layout_width="wrap_content"
android:layout_height="72dp"
android:layout_alignEnd="@+id/edittext_name"
android:layout_alignRight="@+id/edittext_name"
android:layout_alignTop="@+id/textviewamount" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/button_decrease"
android:layout_centerHorizontal="true">
</android.support.v7.widget.RecyclerView>
杂货项目.xml
android:layout_margin="8dp"
>
<TextView
android:id="@+id/textview_amount_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="30sp"
/>
<TextView
android:id="@+id/textview_name_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Empty Item"
android:textSize="30sp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
/>
MainActivity.java
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private SQLiteDatabase mDatabase;
private GroceryAdapter mAdapter;
private EditText mEditTextName;
private TextView mTextViewAmount;
private int mAmount = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GroceryDBHelper dbHelper = new GroceryDBHelper(this);
mDatabase = dbHelper.getWritableDatabase();
//能往database里写数据的必须语句
RecyclerView recyclerView = findViewById(R.id.recyclerview);
//MainActivit.xml里的recyclerView
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new GroceryAdapter(this,getAllItems());
//cursor的位置放getAllItems方法来传指针
recyclerView.setAdapter(mAdapter);
mEditTextName = findViewById(R.id.edittext_name);
mTextViewAmount = findViewById(R.id.textview_amount_item);
Button buttonIncrease = findViewById(R.id.button_increase);
Button buttonDecrease = findViewById(R.id.button_decrease);
Button buttonAdd = findViewById(R.id.button_add);
buttonIncrease.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
increase();
}
});
buttonDecrease.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
decrease();
}
});
buttonAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addItem();
}
});
}
private void increase()
{
mAmount++;
mTextViewAmount.setText(String.valueOf(mAmount));
}
private void decrease() {
if (mAmount > 0) {
mAmount--;
mTextViewAmount.setText(String.valueOf(mAmount));
}
}
private void addItem()
//往database里加item的方法
{
if (mEditTextName.getText().toString().trim().length() == 0 || mAmount == 0)
//trim:删除句首和句尾的空格
{
return;
//如果每输入或输入数量为0,就不添加item,直接返回additem方法
}
String name = mEditTextName.getText().toString();
ContentValues cv = new ContentValues();
cv.put(GroceryContract.GroceryEntry.COLUMN_NAME, name);
//将name放入database的name column里
cv.put(GroceryContract.GroceryEntry.COLUMN_AMOUNT, mAmount);
//将数量放入database的amount column里
//无需手动添加id和stamp,会自动添加
mDatabase.insert(GroceryContract.GroceryEntry.TABLE_NAME,null, cv);
//将cv放入database的table里
mAdapter.swapCursor(getAllItems());
//添加新item后转换指针
mEditTextName.getText().clear();
//清空输入栏
}
private Cursor getAllItems()
{
return mDatabase.query(
GroceryContract.GroceryEntry.TABLE_NAME,
null,
null,
null,
null,
null,
GroceryContract.GroceryEntry.COLUMN_TIMESTAMP + " DESC"
//decending order
);
}
}
GroceryDBHelper.java
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.playah.recyclerdatabase.GroceryContract.*;
public class GroceryDBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "grocerylist.db";
public static final int DATABASE_VERSION = 1;
//这个数字是database的scheme
public GroceryDBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_GROCERYLIST_TABLE = "CREATE TABLE "+
GroceryEntry.TABLE_NAME + " (" +
GroceryEntry._ID + "INTEGER PRIMARY KEY AUTOINCREMENT, "+
//ID没创建,但是已经GroceryContract.java里通过BaseColumn引入
//primary key:unique identify of the row
//autoincrement: 每往table加一行就添加一次
GroceryEntry.COLUMN_NAME + "TEXT NOT NULL," +
//not null: we have to provide a value
GroceryEntry.COLUMN_AMOUNT + "INTEGER NOT NULL," +
GroceryEntry.COLUMN_TIMESTAMP +"TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
//每往table加一行就在当前位置创建一次time stamp
");";
db.execSQL(SQL_CREATE_GROCERYLIST_TABLE);
//执行创建database
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + GroceryEntry.TABLE_NAME);
//如果database已存在,drop并重建
onCreate(db);
}
}
**GroceryContract**
import android.provider.BaseColumns;
public class GroceryContract
{ private GroceryContract() {}
//引入interface
public static final class GroceryEntry implements BaseColumns {
//展示table的id columns
public static final String TABLE_NAME = "groceryList";
//table name
public static final String COLUMN_NAME = "name";
//first column of the table
public static final String COLUMN_AMOUNT = "amount";
//amount of the item
public static final String COLUMN_TIMESTAMP = "timestemp";
}
}
GroceryContract.java
import android.provider.BaseColumns;
public class GroceryContract
//为table定义字符实体和行数
{ private GroceryContract() {}
//用的是下面的方法,用不到这个方法来构造函数
//引入interface
public static final class GroceryEntry implements BaseColumns {
//展示table的id columns
public static final String TABLE_NAME = "groceryList";
//table name
public static final String COLUMN_NAME = "name";
//first column of the table
public static final String COLUMN_AMOUNT = "amount";
//amount of the item
public static final String COLUMN_TIMESTAMP = "timestemp";
//改变item的数量
}
}
GroceryAdapter.java
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class GroceryAdapter extends RecyclerView.Adapter<GroceryAdapter.GroceryViewHolder> {
private Context mContext;
private Cursor mCursor;
public GroceryAdapter(Context context, Cursor cursor)
//cursor使能够从database取出数据
{
mContext = context;
mCursor = cursor;
}
public class GroceryViewHolder extends RecyclerView.ViewHolder
{
public TextView nameText;
public TextView countText;
public GroceryViewHolder(@NonNull View itemView) {
super(itemView);
nameText = itemView.findViewById(R.id.textview_name_item);
countText = itemView.findViewById(R.id.textview_amount_item);
}
}
@NonNull
@Override
public GroceryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.grocery_item, parent, false);
//使用recyclervew的layout
return new GroceryViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull GroceryViewHolder holder, int position) {
if (!mCursor.moveToPosition(position)){
//判断cursor是否存在
return;
}
String name = mCursor.getString(mCursor.getColumnIndex(GroceryContract.GroceryEntry.COLUMN_NAME));
int amount = mCursor.getInt(mCursor.getColumnIndex(GroceryContract.GroceryEntry.COLUMN_AMOUNT));
//从database中取得上面两种数据
holder.nameText.setText(name);
holder.countText.setText(String.valueOf(amount));
}
@Override
public int getItemCount() {
return mCursor.getCount();
}
public void swapCursor(Cursor newCursor)
//转换cursor,为了使数据库更新后cursor在正确的位置
{
if (mCursor !=null){
mCursor.close();
}
mCursor = newCursor;
if(newCursor != null){
notifyDataSetChanged();
}
}
这是日志猫
--------- beginning of crash
08-13 15:51:49.125 5572-5572/com.playah.recyclerdatabase E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.playah.recyclerdatabase, PID: 5572
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.playah.recyclerdatabase/com.playah.recyclerdatabase.MainActivity}: android.database.sqlite.SQLiteException: AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY (code 1): , while compiling: CREATE TABLE groceryList (_idINTEGER PRIMARY KEY AUTOINCREMENT, nameTEXT NOT NULL,amountINTEGER NOT NULL,timestempTIMESTAMP DEFAULT CURRENT_TIMESTAMP);
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: android.database.sqlite.SQLiteException: AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY (code 1): , while compiling: CREATE TABLE groceryList (_idINTEGER PRIMARY KEY AUTOINCREMENT, nameTEXT NOT NULL,amountINTEGER NOT NULL,timestempTIMESTAMP DEFAULT CURRENT_TIMESTAMP);
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:889)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:500)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31)
at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1677)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1608)
at com.playah.recyclerdatabase.GroceryDBHelper.onCreate(GroceryDBHelper.java:33)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:294)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:194)
at com.playah.recyclerdatabase.MainActivity.onCreate(MainActivity.java:35)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
解决方案
在双引号中的 INTEGER 之前添加空格
GroceryEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
推荐阅读
- c# - Unity 测试运行程序中未调用设置/拆卸,测试被阻止
- vuejs2 - 在 v-for 循环中使用插槽或插槽范围来访问属性?
- pandas - 将 Pandas 子图组合成一个图形
- css - 如何将标题定位在某个位置?
- netbeans - 为 tinylog 创建 Netbeans 代码模板
- python - 创建一个包含超过 3 个元音的所有单词(包括连字符的单词)的列表
- git - 检查远程 git 标签是否已更改
- javascript - 如何设置jQuery中图像的宽度?
- c++ - 如何检测 QWidget 的关闭按钮被按下?
- javascript - 为什么模糊事件反复触发?有时您无法关闭警报窗口