从无到有,一步一步实现记事本APP(一)

        俺的碎碎念:写了个简单的记事本APP 复习了下SQLite的使用以及各种边角知识,希望能对各位带来一定的启发。

        功能简介:本项目实现了记事本的基本功能,支持创建笔记,编辑笔记,长按删除笔记等功能;后续新增功能也会同步更新到博客和公众号。

        主要功能演示:

        创建新笔记:

         编辑笔记:

          删除笔记:

本项目的笔记APP核心的存储逻辑是基于SQLite数据库开发的,说白了就是增删改查的简单封装和使用。

思路也比较简单,RecycleView负责展示对应的笔记列表,而实际的存储是依靠SQLite。

废话不多说来看下关键代码。

SQLite数据库代码讲解 

DBUtils工具类代码如下:

//工具类的存在主要是为了创建Helper文件时 引用的数据库参数更加便捷,使逻辑更清楚。
public class DBUtils {

    public static final String SQL_NAME = "Notepad.db";//数据库名
    public static final String SQL_TABLE = "notemane";//表名
    public static final int DATABASE_VERSION = 1;//数据库版本

    //数据库表中的列名
    public static final String NOTEPAD_ID = "id";
    public static final String NOTEPAD_CONTENT = "content";//item内容
    public static final String NOTEPAD_TIME = "time";//item的修改时间
    public static final String NOTEPAD_TITLE = "title";//标题


    //获取时间
    public static final String getTime() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        Date date = new Date(System.currentTimeMillis());
        return simpleDateFormat.format(date);
    }

}
 

Android 提供了抽象类SQLiteOpenHelper来帮助我们使用SQLite数据库,如果我们想要使用SQLite数据库,那么就必须新建一个Helper类来继承SQLiteOpenHelper这个抽象类。

SQLiteHelper代码如下:

public class SQLiteHelper extends SQLiteOpenHelper {
    SQLiteDatabase mSQLiteDatabase;

    public SQLiteHelper(Context context) {
        super(context, DBUtils.SQL_NAME, null, DBUtils.DATABASE_VERSION);
        mSQLiteDatabase = this.getWritableDatabase();
        //在数据库创建时即允许写入
    }

    /**
     * 构造函数
     *
     * @param context
     * @param name
     * @param factory
     * @param version
     * @param errorHandler
     */
    public SQLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
        super(context, name, factory, version, errorHandler);
    }

    public SQLiteHelper(Context context, String name, int version, @NonNull SQLiteDatabase.OpenParams openParams) {
        super(context, name, version, openParams);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建表
        String sql = "create table notemane(id integer primary key autoincrement,content varchar(20),time varchar(20),title varchar(20))";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    //增
    public boolean insertDate(String userContent, String userTime, String userTitle) {
        mSQLiteDatabase = this.getWritableDatabase();//允许写入
        ContentValues values = new ContentValues();
        values.put(DBUtils.NOTEPAD_CONTENT, userContent);
        values.put(DBUtils.NOTEPAD_TIME, userTime);
        values.put(DBUtils.NOTEPAD_TITLE, userTitle);
        return mSQLiteDatabase.insert(DBUtils.SQL_TABLE, null, values) > 0;
    }

    //删
    public boolean deleteData(String id) {
        mSQLiteDatabase = this.getWritableDatabase();//允许写入
        String sql = DBUtils.NOTEPAD_TIME + "=?";
        //删除是根据时间删除的,也可以根据id删除,但是根据id删除要考虑到recycleView的position会变化
        String[] contentValues = new String[]{String.valueOf(id)};
        return mSQLiteDatabase.delete(DBUtils.SQL_TABLE, sql, contentValues) > 0;
    }

    //改
    public boolean updateData(String id, String userTitle, String userContent, String userTime) {
        mSQLiteDatabase = this.getWritableDatabase();//允许写入
        ContentValues values = new ContentValues();
        values.put(DBUtils.NOTEPAD_TITLE, userTitle);
        values.put(DBUtils.NOTEPAD_CONTENT, userContent);
        values.put(DBUtils.NOTEPAD_TIME, userTime);
        String sql = DBUtils.NOTEPAD_ID + "=?";
        String[] strings = new String[]{id};
        return mSQLiteDatabase.update(DBUtils.SQL_TABLE, values, sql, strings) > 0;
    }


}

主界面代码讲解

主界面的主要功能是展示笔记列表,列表这里我选用的是RecycleView。

RecycleView的使用流程: 在主布局中引用RecycleView控件 -> 搭建RecycleView中item的布局 -> 搭建RecycleView的适配器 -> 在MainActivity中使用。

RecycleViewAdapter代码如下

public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder> {
    private static final String TAG = "RecycleViewAdapter";
    Context mContext;

    private ArrayList<NoteMessageBean> mList;
    NoteMessageBean mNoteMessageBean = new NoteMessageBean();//笔记的bean类


    onRecycleItemClickListener monRecycleItemClickListener;
    onRecycleItemCheckChangeListener mRecycleItemCheckChangeListener;

    //构造函数,一个参数
    public RecycleViewAdapter(Context mContext) {
        this.mContext = mContext;
    }

    //构造函数,两个参数
    public RecycleViewAdapter(Context mContext, ArrayList<NoteMessageBean> mList) {
        this.mContext = mContext;
        this.mList = mList;
    }

    @Override
    public RecycleViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycle_item, parent, false);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(RecycleViewAdapter.ViewHolder holder, int position) {
        mNoteMessageBean = (mList.get(position));
        holder.text.setText(mNoteMessageBean.getContent());//每个item中的简单介绍
        holder.title.setText(mNoteMessageBean.getTitle());//每个item中的标题
        holder.time.setText(mNoteMessageBean.getmTime());//每个item中的时间
        holder.itemView.setOnClickListener(v -> {
            if (monRecycleItemClickListener != null) {
                NoteMessageBean messageBean = mList.get(position);
                Log.d(TAG, "onBindViewHolder-> itemView -> onClick: " + position + messageBean.getTitle() + messageBean.getContent() + messageBean.getmTime());
                monRecycleItemClickListener.onItemClick(messageBean.getId(), messageBean.getTitle(), messageBean.getContent(), messageBean.getmTime());
            }
        });
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (mRecycleItemCheckChangeListener != null) {
                    holder.mCheckBox.setVisibility(View.VISIBLE);
                }
                return true;
            }
        });

        holder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Log.d(TAG, "onCheckedChanged: !isChecked" + isChecked);
                if (!isChecked) {
                    buttonView.setChecked(false);
                    //从选中 -> 未选中状态
                } else {
                    buttonView.setChecked(true);
                    //从未选中 -> 选中状态
                    //当选中CheckBox时 弹出弹窗提示 是否删除
                    Dialog mDialog = new Dialog(mContext, R.style.ThemeOverlay_AppCompat_Dialog);
                    View view = View.inflate(mContext, R.layout.delete_dialog, null);
                    ImageView mImageView = (ImageView) view.findViewById(R.id.delete_item_image);
                    mDialog.setContentView(view);
                    mDialog.setCanceledOnTouchOutside(false);
                    mDialog.show();

                    Window window = mDialog.getWindow();
                    window.setContentView(view);
                    window.setGravity(Gravity.BOTTOM);

                    WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes();
                    lp.width = WindowManager.LayoutParams.MATCH_PARENT; //设置宽度
                    lp.height = WindowManager.LayoutParams.WRAP_CONTENT; //设置宽度
                    mDialog.getWindow().setAttributes(lp);

                    mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
                        @Override
                        public void onCancel(DialogInterface dialog) {
                            //如果手势操作返回键 取消了弹窗显示
                            //则 弹窗取消显示时 对 CheckBox进行隐藏
                            holder.mCheckBox.setChecked(false);
                            holder.mCheckBox.setVisibility(View.GONE);
                        }
                    });

                    //点击删除图标
                    mImageView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            NoteMessageBean messageBean = mList.get(position);
                            mRecycleItemCheckChangeListener.onImageViewOnClick((messageBean.getmTime()), position);
                            mDialog.dismiss();
                            holder.mCheckBox.setVisibility(View.GONE);
                            holder.mCheckBox.setChecked(false);
                            notifyDataSetChanged();
                        }
                    });

                }
            }
        });
        //这里是给不同的item设置不同的颜色
        //用position的个位数进行约束
        int mNumber = position % 10;
        Log.d(TAG, "onBindViewHolder: " + mNumber);
        switch (mNumber) {
            case 0:
            case 6:
                holder.itemView.setBackgroundResource(R.drawable.shape_rounded_corners);
                break;
            case 1:
            case 7:
                holder.itemView.setBackgroundResource(R.drawable.itemstyle0);
                break;
            case 2:
            case 8:
                holder.itemView.setBackgroundResource(R.drawable.itemstyle1);
                break;
            case 3:
            case 9:
                holder.itemView.setBackgroundResource(R.drawable.itemstyle2);
                break;
            case 4:
                holder.itemView.setBackgroundResource(R.drawable.itemstyle3);
                break;
            case 5:
                holder.itemView.setBackgroundResource(R.drawable.itemstyle4);
                break;
        }


    }

    @Override
    public int getItemCount() {
        return mList.size();//item数量
    }

    /**
     * 为列表设置数据源
     *
     * @param data
     */
    public void setList(ArrayList<NoteMessageBean> data) {
        this.mList = data;
        notifyDataSetChanged();
    }


    public class ViewHolder extends RecyclerView.ViewHolder {
        private TextView title, text, time;
        private ConstraintLayout mLinearLayout;
        private CheckBox mCheckBox;

        public ViewHolder(View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.text_note);
            title = itemView.findViewById(R.id.title_note);
            time = itemView.findViewById(R.id.time_note);
            mLinearLayout = itemView.findViewById(R.id.item_layout);
            mCheckBox = itemView.findViewById(R.id.radioButton);
        }
    }

    /**
     * RecycleView的点击回调
     */
    public interface onRecycleItemClickListener {
        void onItemClick(String id, String Title, String content, String time);
    }

    public void setOnItemClickListener(onRecycleItemClickListener listener) {
        monRecycleItemClickListener = listener;
    }

    /**
     * 删除动作的回调
     */
    public interface onRecycleItemCheckChangeListener {
        void onImageViewOnClick(String time, int position);
    }

    public void setOnItemLongClickListener(onRecycleItemCheckChangeListener listener) {
        mRecycleItemCheckChangeListener = listener;
    }

}

RecycleView在MainActivity中使用流程如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, RecycleViewAdapter.onRecycleItemClickListener, RecycleViewAdapter.onRecycleItemCheckChangeListener {

private RecycleViewAdapter mAdapter;
private LinearLayoutManager manager;
private RecyclerView mRecyclerView;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate: ");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
      
    }

    private void initView() {
        mRecyclerView = findViewById(R.id.recycler_view);
        mAdapter = new RecycleViewAdapter(this, mList);
        mAdapter.setOnItemClickListener(this);
        mAdapter.setOnItemLongClickListener(this);
        manager = new LinearLayoutManager(getApplicationContext());
        mRecyclerView.setLayoutManager(manager);
        mRecyclerView.setAdapter(mAdapter);
    }

    //代码篇幅略长,仅展示RecycleView在MainActivity如何使用

}

编辑界面代码讲解

编辑界面的主要功能:1.新建笔记时写入内容标题等。2.打开笔记时对原有笔记进行修改。

其主要的布局为EditText。

编辑界面主要代码如下

public class EditActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "EditActivity";
    private ImageView mBack, mCommit;
    private EditText mTitle, mContent;
    private TextView mTime;

    private SQLiteHelper mSQLiteHelper;
    private Context mContext;

    private boolean flag;
    private String id;


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.edit_activity_layout);
        initView();
        initData();
    }


    private void initView() {
        mBack = findViewById(R.id.bt_back);
        mBack.setOnClickListener(this);
        mCommit = findViewById(R.id.bt_commit);
        mCommit.setOnClickListener(this);

        mTitle = findViewById(R.id.edit_title);
        mContent = findViewById(R.id.edit_text);
        mTime = findViewById(R.id.time_date);

    }

    private void initData() {
        Log.d(TAG, "initData: ");
        mSQLiteHelper = new SQLiteHelper(getApplicationContext());
        mTime.setText(DBUtils.getTime());
        Intent intent = getIntent();
        flag = intent.getBooleanExtra("flag", false);
        //以flag为基准,有且仅有点击item之后才会调用 intent.putExtra("flag", true);
        if (flag) {
            id = intent.getStringExtra("id");
            mTitle.setText(intent.getStringExtra("title"));
            mContent.setText(intent.getStringExtra("content"));
            mTime.setText(intent.getStringExtra("time"));
        }
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_back:
                Intent intent = new Intent(EditActivity.this, MainActivity.class);
                startActivity(intent);
                break;
            case R.id.bt_commit:
                if (mContent.length() == 0) {
                    //当内容为空时,提醒一下,不创建新的item
                    Toast.makeText(this.getApplicationContext(), "请输入文本内容后再进行提交操作!", Toast.LENGTH_SHORT).show();
                } else {
                    if (flag) {
                        //为true表示现在是打开了一个item进行修改编辑 并不是新建笔记
                        mSQLiteHelper.updateData(id, mTitle.getText().toString(), mContent.getText().toString(), mTime.getText().toString());
                        //更新数据后 跳转到MainActivity页面刷新数据
                        Intent mIntent = new Intent(EditActivity.this, MainActivity.class);
                        startActivity(mIntent);
                    } else {
                        //内容不为空则调用对应的数据库插入操作
                        mSQLiteHelper.insertDate(mContent.getText().toString(), DBUtils.getTime(), mTitle.getText().toString());
                        //插入数据之后 跳转到main页面 更新列表视图
                        Intent mIntent = new Intent(EditActivity.this, MainActivity.class);
                        startActivity(mIntent);
                    }

                }
                break;

        }
    }


}

至此,笔记APP的基本功能完成,扩展功能目前的计划加入 手写输入功能,图片插入功能,语音输入功能,搜索功能。

扩展功能以及源码我会逐渐更新在公众号中(二两仙气儿),毕竟我也不能一直摸鱼(狗头)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值