数据库实现一个轻量的背单词APP


在使用背单词APP的过程中,我发现我就没专心背单词,而是想着:咦,这功能可以!唉?这个地方好像有点小BUG!这里为啥不加点这个功能?那里为啥不加点那个功能?然后我就点开了我的AndroidStudio,开始了接来下的编码之旅!(本文持续更新,感兴趣的小伙伴可收藏一波)

项目地址(GitHub):https://github.com/YJX666666/MyDictionary

总体功能目标:

前期功能:

添加单词:能够添加单词

词库管理:能显示词库所有单词、能删除以及修改单词

选择模式:两种模式(中 - 英 & 英 - 中),选项中有一个正确答案以及三个非正确答案,选择正确和不正确都有相应提示

填空模式:两种模式(中 - 英 & 英 - 中),填写正确和不正确都有相应提示

后期功能:

词库选择:软件内自带几种常用的词库,如四六级等等,选择后添加进当前词库

背诵记录:可以查看单词背诵情况,如:单词出现次数、正确次数、错误次数

智能化:通过背诵记录来智能筛选背诵的单词,如:更多地出现正确率低的单词以加深记忆

大致思路:

利用数据库来保存单词,每次添加单词就直接添加进数据库,选择题是获取四组数据,然后选一组作为当前的主角(当前考核的单词),其余三组作为非正确答案放在按钮上,填空题则比较简单,随机选取一组数据,然后给出单词(或翻译),让你在输入框中填写答案。后期功能思路暂时没有考虑,先把前期功能做完!

当前效果图:

在这里插入图片描述
在这里插入图片描述

开始编码:

词库:

先写一个SQWordsHelps继承自SQLiteOpenHelper,这里定义两个数据(WORD 和 CHINESE)

public class SQWordsHelper extends SQLiteOpenHelper {
    public static final String WORD = "word";
    public static final String DB_NAME = WORD + "s.db";
    public static final String TABLE_NAME = WORD + "s";
    public static final String CHINESE = "chinese";
    
    public SQWordsHelper(Context context) {
        super(context, DB_NAME, null, 1);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table " + TABLE_NAME + "(" + WORD + " text," + CHINESE + " text)");
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
    
}

然后是添加数据的功能,也就是数据库中的 增

XML,Button我用的自己自定义的Button(文末符代码)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <EditText
        android:id="@+id/edit_english"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:background="@drawable/edit_stroke"
        android:gravity="center"
        android:hint="@string/str_english"
        android:padding="10dp"
        android:singleLine="true"
        android:textSize="22sp" />

    <EditText
        android:id="@+id/edit_chinese"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="20sp"
        android:layout_marginRight="30dp"
        android:background="@drawable/edit_stroke"
        android:gravity="center"
        android:hint="@string/str_chinese"
        android:padding="10dp"
        android:singleLine="true"
        android:textSize="22sp" />

    <com.yjx.androidword.MyView.MyFirstButton
        android:id="@+id/btn_add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="20sp"
        android:layout_marginRight="30dp"
        android:text="@string/str_addwords" />

</LinearLayout>

Java,BaseActivity是我写的基类(文末符代码)

点击按钮后进行判断,如果两个输入框中有空的则无反应(可以写一个提示),都不为空则调用插入数据的方法(initSQLiteData())

补充一个小知识点:数据库的 .insert() 方法是有返回值的,当返回 -1 时则为插入数据失败,返回其他则为插入数据成功(返回值是新插入数据所在的行数),这样我们就可以通过返回值来判断并提示用户插入结果

public class AddWordsActivity extends BaseActivity {
    private android.widget.EditText mEditEnglish;
    private android.widget.EditText mEditChinese;
    private android.widget.Button mBtnAdd;

    SQWordsHelper mSQHelper;
    SQLiteDatabase mSQLiteDatabase;
    ContentValues mContentValues;

    @Override
    protected void initData() {
        mSQHelper = new SQWordsHelper(mContext);
        mSQLiteDatabase = mSQHelper.getWritableDatabase();

        mBtnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String english = mEditEnglish.getText().toString();
                String chinese = mEditChinese.getText().toString();

                if (!TextUtils.isEmpty(english) && !TextUtils.isEmpty(chinese)) {
                    initSQLiteData(english, chinese);
                }

            }
        });

    }

    private void initSQLiteData(String english, String chinese) {
        mContentValues = new ContentValues();
        mContentValues.put(SQWordsHelper.WORD, english);
        mContentValues.put(SQWordsHelper.CHINESE, chinese);
        if (mSQLiteDatabase.insert(SQWordsHelper.TABLE_NAME, null, mContentValues) == -1) {
            Toast.makeText(mContext, "添加失败!", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(mContext, "添加成功!", Toast.LENGTH_SHORT).show();
            mEditEnglish.setText("");
            mEditChinese.setText("");
        }
    }

    @Override
    protected int initLayout() {
        return R.layout.addwords;
    }

    @Override
    protected void initView() {
        mEditEnglish = findViewById(R.id.edit_english);
        mEditChinese = findViewById(R.id.edit_chinese);
        mBtnAdd = findViewById(R.id.btn_add);
    }
}
选择题:

主体是很简单的一个TextView加上四个Button的布局,然后在按钮的右下角放了一个小一点的Buton,用来获取下一个单词(当然也可以做成选完直接换的那种,不过那种感觉不如这种好),最下面是一个 “掌握了单词” 的TextView,有时候背单词遇到那种已经背的特别熟的单词,就可以通过这种方式直接在背单词的时候删掉,而不是去词库删。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/txv_word"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="26sp"
        android:textStyle="bold" />

    <com.yjx.androidword.MyView.MyFirstButton
        android:id="@+id/btn_a"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="50dp"
        android:layout_marginRight="80dp" />

    <com.yjx.androidword.MyView.MyFirstButton
        android:id="@+id/btn_b"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="80dp" />

    <com.yjx.androidword.MyView.MyFirstButton
        android:id="@+id/btn_c"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="80dp" />

    <com.yjx.androidword.MyView.MyFirstButton
        android:id="@+id/btn_d"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="80dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="80dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="20sp"
        android:layout_weight="1"
        android:orientation="vertical">

        <com.yjx.androidword.MyView.MyFirstButton
            android:id="@+id/btn_next"
            android:layout_width="100dp"
            android:layout_height="35dp"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:layout_marginEnd="10dp"
            android:text="@string/str_next"
            android:textSize="12sp" />

    </LinearLayout>

    <TextView
        android:id="@+id/txv_grasp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="30dp"
        android:shadowColor="@color/colorShadowBlack"
        android:shadowDx="3"
        android:shadowDy="3"
        android:shadowRadius="6"
        android:text="@string/str_grasp" />

</LinearLayout>

Java,后台代码就稍微多了起来,主要代码功能是:通过工具类获取四组数据然后传到TextView和Button上,然后就是判断当前选项是否正确,以及选择正确和不正确时,对按钮进行背景色的替换(选对变绿,选错变红),以及点击 “下一个” 按钮后,所有按钮重置为初始状态并重新获取数据。

public class ChooseActivity extends BaseActivity implements View.OnClickListener {

    private android.widget.TextView mTxvWord;
    private android.widget.Button mBtnA;
    private android.widget.Button mBtnB;
    private android.widget.Button mBtnC;
    private android.widget.Button mBtnD;
    
    private String mString_English = "";
    private String mString_Chinese = "";

    private Random mRandom = new Random();
    private android.widget.TextView mTxvGrasp;
    private SQWordsHelper mSQWordsHelper;
    private SQLiteDatabase mSQLiteDatabase;

    private TextView mTxvEnglish;
    private TextView mTxvChinese;
    private TextView mTxvDel;
    private TextView mTxvModify;
    private List<WordsBean> mListWords = new ArrayList<>();
    private Button mBtnNext;

    @Override
    protected void initData() {
        mBtnA.setOnClickListener(this);
        mBtnB.setOnClickListener(this);
        mBtnC.setOnClickListener(this);
        mBtnD.setOnClickListener(this);
        mBtnNext.setOnClickListener(this);
        mTxvGrasp.setOnClickListener(this);
        mSQWordsHelper = new SQWordsHelper(mContext);
        mSQLiteDatabase = mSQWordsHelper.getWritableDatabase();
        mBtnNext.setVisibility(View.GONE);
        //获取需要测试的单词和翻译
        setAnswer();
    }

    private void setAnswer() {
        //获取四组数据
        mListWords = WordsUtils.get(mContext);
        //需要测试的组赋值
        mString_English = mListWords.get(0).getEnglish();
        mString_Chinese = mListWords.get(0).getChinses();
        //单词发送到TextView上
        mTxvWord.setText(mString_English);
        //随机选取一个按钮放中文答案,其他按钮放上获取的随机非正确答案
        switch (mRandom.nextInt(4) % (4) + 1) {
            case 1:
                mBtnA.setText(mString_Chinese);
                mBtnB.setText(mListWords.get(1).getChinses());
                mBtnC.setText(mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getChinses());
                break;
            case 2:
                mBtnB.setText(mString_Chinese);
                mBtnA.setText(mListWords.get(1).getChinses());
                mBtnC.setText(mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getChinses());
                break;
            case 3:
                mBtnC.setText(mString_Chinese);
                mBtnA.setText(mListWords.get(1).getChinses());
                mBtnB.setText(mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getChinses());
                break;
            case 4:
                mBtnD.setText(mString_Chinese);
                mBtnA.setText(mListWords.get(1).getChinses());
                mBtnB.setText(mListWords.get(2).getChinses());
                mBtnC.setText(mListWords.get(3).getChinses());
                break;
        }
    }

    //重置按钮
    private void reSet() {
        mBtnA.setBackground(getResources().getDrawable(R.drawable.btn_background));
        mBtnB.setBackground(getResources().getDrawable(R.drawable.btn_background));
        mBtnC.setBackground(getResources().getDrawable(R.drawable.btn_background));
        mBtnD.setBackground(getResources().getDrawable(R.drawable.btn_background));
        mBtnA.setEnabled(true);
        mBtnB.setEnabled(true);
        mBtnC.setEnabled(true);
        mBtnD.setEnabled(true);
    }

    //选择判断
    @SuppressLint("SetTextI18n")
    private void getJudg(Button btn, String str_btn) {

        if (str_btn.equals(mString_Chinese)) {

            //选择正确
            btn.setBackground(getResources().getDrawable(R.drawable.btn_green));

            if (btn == mBtnA) {
                mBtnB.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnC.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            } else if (btn == mBtnB) {
                mBtnA.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnC.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            } else if (btn == mBtnC) {
                mBtnA.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnB.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            } else if (btn == mBtnD) {
                mBtnA.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnB.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnC.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            }

        } else {

            //选择错误
            btn.setBackground(getResources().getDrawable(R.drawable.btn_red));

            if (btn != mBtnA && mBtnA.getText().toString().equals(mString_Chinese)) {
                mBtnA.setBackground(getResources().getDrawable(R.drawable.btn_green2));
                mBtnB.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnC.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            } else if (btn != mBtnB && mBtnB.getText().toString().equals(mString_Chinese)) {
                mBtnB.setBackground(getResources().getDrawable(R.drawable.btn_green2));
                mBtnA.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnC.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            } else if (btn != mBtnC && mBtnC.getText().toString().equals(mString_Chinese)) {
                mBtnC.setBackground(getResources().getDrawable(R.drawable.btn_green2));
                mBtnA.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnB.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnD.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            } else if (btn != mBtnD && mBtnD.getText().toString().equals(mString_Chinese)) {
                mBtnD.setBackground(getResources().getDrawable(R.drawable.btn_green2));
                mBtnA.setText(mListWords.get(1).getEnglish() + " : " + mListWords.get(1).getChinses());
                mBtnB.setText(mListWords.get(2).getEnglish() + " : " + mListWords.get(2).getChinses());
                mBtnC.setText(mListWords.get(3).getEnglish() + " : " + mListWords.get(3).getChinses());
            }

        }
        //选择完以后设置按钮不可点击
        mBtnA.setEnabled(false);
        mBtnB.setEnabled(false);
        mBtnC.setEnabled(false);
        mBtnD.setEnabled(false);
        //选择完以后把右下角 下一个 按钮显示出来
        mBtnNext.setVisibility(View.VISIBLE);

    }

    @Override
    protected int initLayout() {
        return R.layout.layout_choose;
    }

    @Override
    protected void initView() {
        mTxvWord = findViewById(R.id.txv_word);
        mBtnA = findViewById(R.id.btn_a);
        mBtnB = findViewById(R.id.btn_b);
        mBtnC = findViewById(R.id.btn_c);
        mBtnD = findViewById(R.id.btn_d);
        mTxvGrasp = findViewById(R.id.txv_grasp);
        mBtnNext = findViewById(R.id.btn_next);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_a:
                getJudg(mBtnA, mBtnA.getText().toString());
                break;
            case R.id.btn_b:
                getJudg(mBtnB, mBtnB.getText().toString());
                break;
            case R.id.btn_c:
                getJudg(mBtnC, mBtnC.getText().toString());
                break;
            case R.id.btn_d:
                getJudg(mBtnD, mBtnD.getText().toString());
                break;
            case R.id.btn_next://下一个
                reSet();
                setAnswer();
                mBtnNext.setVisibility(View.GONE);
                break;
            case R.id.txv_grasp:
                View view = LayoutInflater.from(mContext).inflate(R.layout.dialog_words_menu, null);
                mTxvEnglish = view.findViewById(R.id.txv_english);
                mTxvChinese = view.findViewById(R.id.txv_chinese);
                mTxvDel = view.findViewById(R.id.txv_del);
                mTxvModify = view.findViewById(R.id.txv_modify);
                mTxvEnglish.setText("English:" + mString_English);
                mTxvChinese.setText("中文:" + mString_Chinese);
                mTxvDel.setText("掌握(删除)");
                mTxvModify.setText("取消");
                final Dialog dialog = DialogUtils.show(mContext, view);
                mTxvDel.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        removeData(mString_English);
                        reSet();
                        dialog.dismiss();
                    }
                });
                mTxvModify.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dialog.dismiss();
                    }
                });
                break;
        }
    }

    private void removeData(String str_del) {
        //数据库同步删除
        String clause = SQWordsHelper.WORD + "=?";
        mSQLiteDatabase.delete(SQWordsHelper.TABLE_NAME, clause, new String[]{str_del});
    }

}

到这就算完成了:添加单词、词库管理、选择模式了!逻辑比较简单,然后本人也是初学者,很多代码写得比较冗杂,有很多可以优化的地方,这些都等前期功能全都实现了再进行一波优化!

用到的基类、自定义Button、工具类

基类BaseActivity

public abstract class BaseActivity extends AppCompatActivity {

    protected Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        initWindow();
        super.onCreate(savedInstanceState);
        mContext = BaseActivity.this;
        setContentView(initLayout());
        //控件声明
        initView();
        //数据
        initData();

    }

    protected abstract void initData();

    protected void initWindow() {
    }

    protected abstract int initLayout();

    protected abstract void initView();

}

自定义Button,MyFirstButton

@SuppressLint("AppCompatCustomView")
public class MyFirstButton extends Button {

    //字体阴影
    private int mShadowRadius =6;
    private int mShadowDx = 3;
    private int mShadowDy= 3;
    private int mShadowColor= getResources().getColor(R.color.colorShadowBlack);
    //字体样式
    private Typeface mTypeface= Typeface.DEFAULT;
    private int mStyle= Typeface.BOLD;
    //背景
    private Drawable mBackground= getResources().getDrawable(R.drawable.btn_background);
    //字体颜色
    private int mTextColor= getResources().getColor(R.color.colorWhite);

    private void set() {
        //阴影
        super.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
        //字体样式
        super.setTypeface(mTypeface, mStyle);
        //背景
        super.setBackground(mBackground);
        //字体颜色
        super.setTextColor(mTextColor);
    }

    public MyFirstButton(Context context) {
        super(context);
        set();
    }
    public MyFirstButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        set();
    }
    public MyFirstButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        set();
    }
}

获取单词的工具类,这里的功能是随机获取词库中不相同的四组数据返回,实现原理是:首先获取词库所有的数据,然后通过获取四个不相同的随机数,最后用这四个随机数作为下标来获取数组中的四组数据,就相当于是随机获取了数组中的四组数据了!

public class WordsUtils {

    private static List<WordsBean> sList;
    private static List<WordsBean> sList_Return;
    private static SQWordsHelper sMSQHelper;
    private static Cursor sCursor;
    private static SQLiteDatabase sSQLiteDatabase;
    private static WordsBean sGetWordBean;
    private static Random sRandom = new Random();
    private static Set<Integer> sSetReturn;
    private static int sI;
    private static Object[] sArray;

    /**
     * 返回四组数据(单词 + 中文)
     */
    public static List<WordsBean> get(Context context) {
        sList = new ArrayList<>();
        sSetReturn = new HashSet<>();
        sList_Return = new ArrayList<>();
        sMSQHelper = new SQWordsHelper(context);
        sSQLiteDatabase = sMSQHelper.getWritableDatabase();
        sCursor = sSQLiteDatabase.query(SQWordsHelper.TABLE_NAME, null, null, null, null, null, null);

        //获取总词库
        while (sCursor.moveToNext()) {
            sGetWordBean = new WordsBean();
            String english = sCursor.getString(0);
            String chinese = sCursor.getString(1);
            sGetWordBean.setEnglish(english);
            sGetWordBean.setChinses(chinese);
            sList.add(sGetWordBean);
        }

        sSetReturn.clear();
        //四个随机数
        while (sSetReturn.size() <= 4) {
            sI = sRandom.nextInt(sList.size()) % (sList.size());
            sSetReturn.add(sI);
        }

        //随机的四组数据传入返回的数组中
        sArray = sSetReturn.toArray();
        for (int i = 0; i < sArray.length; i++) {
            sGetWordBean = new WordsBean();
            sGetWordBean.setEnglish(sList.get((Integer) sArray[i]).getEnglish());
            sGetWordBean.setChinses(sList.get((Integer) sArray[i]).getChinses());
            sList_Return.add(sGetWordBean);
        }

        return sList_Return;
    }

}

对话框工具类

public class DialogUtils {

    //自定义View对话框
    public static Dialog show(Context context, View view) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context)
                .setView(view)
                .setCancelable(true);
        Dialog dialog = builder.show();
        dialog.getWindow().getDecorView().setBackground(null);
        return dialog;
    }

}

BUG求助:随机获取的数据会莫名固定那么几个,暂未查明原因,希望有大佬评论区或者私聊帮助一下
同样欢迎小伙们一起研究探讨!
下一步将进阶填空题!

本文持续更新(感兴趣可以收藏一波)…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章