首页 > 解决方案 > 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) 

标签: javaandroid

解决方案


在双引号中的 INTEGER 之前添加空格

    GroceryEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "

推荐阅读