《Android Studio实战 快速、高效地构建Android应用》--五、备忘录实验(1/2)

  • 通过开发App熟悉Android Studio的用法
  • 开发一款用于管理备忘事项列表的App,核心功能:
    • 创建、删除备忘
    • 将某些备忘标记为重要(左侧带颜色标签突出显示)
  • 涉及:操作栏菜单、上下文菜单、用于持久化的本地数据库、支持多选的设备上的多项选择

启动新项目

File|New|New project命名为Reminders,选择Empty Activity

初始化Git仓库

安装、配置Git:https://www.cnblogs.com/hhhqqq/p/12273696.html

  • 创建Git仓库

然后选择项目根目录来创建Git仓库

  • 在Version Control工具窗口中右击Unversioned Files选择Add to VCS将这些文件添加到Git索引
  • 提交文件(Ctrl+K|将项目的修改记录到Git版本控制系统的过程)

构建用户界面

visual designer布局

可视化设计器

将左侧Palette\Legacy中的ListView拖到编辑区域放置该控件,右侧Attributes修改该控件各项属性

编辑布局的原始XML

单击底部Text,编辑原始XML

更改背景颜色:

 android:background="#181818" //设置RelativeLayout背景颜色

在XML布局文件中硬编码颜色值并不是最佳方案,更好的方法:在values资源文件夹下定义colors.xml,在里面定义自己的颜色。这样便于编辑而且可以很容易地在整个项目中引用。

将代码修改为:

  android:background="@color/dark_grey"

在错误提示中选择第二项(创建颜色资源)

修改ListView列表项的显示方式

在res\layout文件夹中新建reminders_row,选择LinearLayout(LinearLayout是布局中的最外层元素)作为根ViewGroup来为单个列表项行创建布局,

<?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="50dp"
           android:orientation="vertical"
           android:background="@color/dark_grey">

 <LinearLayout
     android:orientation="horizontal"
     android:layout_width="match_parent"
     android:layout_height="48dp">

 <view
     android:layout_width="10dp"
     android:layout_height="match_parent"
     class="android.view.View"
     android:id="@+id/row_tab"
     android:background="@color/green"/>

 <TextView
     android:layout_width="match_parent"
     android:layout_height="50dp"
     android:text="Reminder Text"
     android:id="@+id/row_text"
     android:textColor="@color/white"
     android:textSize="18sp"
     android:gravity="center_vertical"
     android:padding="10dp"
     android:ellipsize="end"
     android:maxLines="1"/>
</LinearLayout>
 <view
     class="android.view.View"
     android:layout_width="fill_parent"
     android:layout_height="1dp"
     android:background="#000"/>
 <view
     class="android.view.View"
     android:layout_width="fill_parent"
     android:layout_height="1dp"
     android:background="#333"/>
</LinearLayout>

向ListView添加条目

修改相应的Activity文件(RemindersActivity.java),声明一个ListView成员、修改onCreate()方法

private ListView mListView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_reminders);
     mListView = (ListView)findViewById(R.id.reminders_list_view);
     ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
             this,   //  当前Activity的Context对象
             R.layout.reminders_row, //使用哪个布局
             R.id.row_text,     //布局中的哪个字段来显示数据
             new String[]{"first record","second record"}    //示例条目
     );
     mListView.setAdapter(arrayAdapter);
 }

添加操作栏溢出菜单

在res文件夹下新建menu文件夹,右键New|Menu resource file命名为menu_reminders

menu/menu_reminders.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/action_new"
        android:title="New Reminder"
        android:orderInCategory="100"
        app:showAsAction="never"/>
    <item android:id="@+id/action_exit"
        android:title="exit"
        android:orderInCategory="200"
        app:showAsAction="never"/>
</menu>

在RemindersActivity.java中添加创建菜单的方法和菜单的点击事件

/**
     *创建菜单
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_reminders,menu); //通过getMenuInflater()方法得到MenuInflater对象,再调用它的inflate()方法就可以给当前活动创建菜单了,第一个参数:用于指定我们通过哪一个资源文件来创建菜单;第二个参数:用于指定我们的菜单项将添加到哪一个Menu对象当中。
        return true; // true:允许创建的菜单显示出来,false:创建的菜单将无法显示。
    }

    /**
     *菜单的点击事件
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()){
            case R.id.action_new:
                Log.d(getLocalClassName(),"create new Reminder");
                return true;
            case R.id.action_exit:
                finish();
                return true;
            default:
                return false;
        }
    }

持久化备忘录

  • SQLite数据库
  • 数据模型、数据库代理类、CursorAdapter
    • 数据模型:保存从数据库读取以及写入数据库的数据
    • 数据库代理类:适配器类,把来自App的简单调用转换为对SQLite数据库的API调用
    • CursorAdapter:继承以抽象方式处理数据访问的标准Android类

数据模型

创建数据模型

com.example.dell.reminders右键New Java Class命名为Reminder

Reminder类:

public class Reminder {
    private int mId;
    private String mContent;
    private int mImportant;

    public Reminder(int id, String content, int important) {
        mId = id;
        mContent = content;
        mImportant = important;
    }

    public int getId() {
        return mId;
    }

    public void setId(int id) {
        mId = id;
    }

    public String getContent() {
        return mContent;
    }

    public void setContent(String content) {
        mContent = content;
    }

    public int getImportant() {
        return mImportant;
    }

    public void setImportant(int important) {
        mImportant = important;
    }
}

创建数据库代理

com.example.dell.reminders右键New Java Class命名为RemindersDbAdapter

public class RemindersDbAdapter {
    //定义列名
    public static final String COL_ID = "_id";
    public static final String COL_CONTENT = "content";
    public static final String COL_IMPORTANT = "important";
    //定义索引值
    public static final int INDEX_ID = 0;
    public static final int INDEX_CONTENT = INDEX_ID + 1;
    public static final int INDEX_IMPORTANT = INDEX_ID + 2;
    //用于日志的TAG
    public static final String TAG = "RemindersDbAdapter";

    //两个数据库API对象
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    //数据库名称、主表名称、版本的常量
    public static final String DATABASE_NAME = "dba_remdrs";
    public static final String TABLE_NAME = "tbl_remdrs";
    public static final int DATABASE_VERSION = 1;

    //上下文对象
    private final Context mCtx;

    //用于创建数据库的SQL语句
    private static final String DATABASE_CREATE =
            "CREATE TABLE if not exists " + TABLE_NAME + " ( " +
                    COL_ID + "INTEGER PRIMARY KEY autoincrement, " +
                    COL_CONTENT + " TEXT, " +
                    COL_IMPORTANT + " INTEGER );";
}

SQLite API

DatabaseHelper:用于打开、关闭数据库的SQLite API类,是一个自定义的类,将其实现为RemindersDbAdapter的内部类

private static class DatabaseHelper extends SQLiteOpenHelper{
        //构造函数完成数据库初始化
        DatabaseHelper(Context context){
            //将数据库名和版本号传给超类,由超类完成建立数据库的繁重工作
            super(context,DATABASE_NAME,null,DATABASE_VERSION);
        }

        public void onCreate(SQLiteDatabase db){
            Log.w(TAG,DATABASE_CREATE);
            db.execSQL(DATABASE_CREATE);
        }

        public void onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion){
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }
    }

使用DatabaseHelper打开和关闭数据库,RemindersDbAdapter构造函数保存了Context实例,并传给DatabaseHelper

public RemindersDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    public void open() throws SQLException{
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
    }

    public void close(){
        if(mDbHelper != null){
            mDbHelper.close();
        }
    }

数据库的增删改查:

创建方法使用特殊的ContentValues对象,用于将数据值传给数据库对象的insert方法,数据库会将这些对象转换为SQL insert语句并执行

//数据库的增删改查
    public void createReminder(String name, boolean important){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT, name);
        values.put(COL_IMPORTANT, important?1:0);
        mDb.insert(TABLE_NAME,null,values);
    }

    public long createReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        return mDb.insert(TABLE_NAME,null,values);
    }

    public Reminder fetchReminderById(int id){
        Cursor cursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
                COL_CONTENT,COL_IMPORTANT},COL_ID + "=?",
                new String[]{String.valueOf(id)},null,null,null,null);
        if(cursor != null)
            cursor.moveToFirst();

            return new Reminder(
              cursor.getInt(INDEX_ID),
              cursor.getString(INDEX_CONTENT),
                    cursor.getInt(INDEX_IMPORTANT)
            );
    }

    public Cursor fetchAllReminders(){
        Cursor mCursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
        COL_CONTENT,COL_IMPORTANT},null,null,null,null,null);

        if (mCursor != null){
            mCursor.moveToFirst();
        }

        return mCursor;
    }

    public void updateReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        mDb.update(TABLE_NAME,values,
                COL_ID+"=?",new String[]{String.valueOf(reminder.getId())});
    }

    public void deleteReminderById(int nId){
        mDb.delete(TABLE_NAME,COL_ID+"=?",new String[]{String.valueOf(nId)});
    }

    public void deleteAllReminders(){
        mDb.delete(TABLE_NAME,null,null);
    }

RemindersDbAdapter最终代码

public class RemindersDbAdapter {
    //定义列名
    public static final String COL_ID = "_id";
    public static final String COL_CONTENT = "content";
    public static final String COL_IMPORTANT = "important";
    //定义索引值
    public static final int INDEX_ID = 0;
    public static final int INDEX_CONTENT = INDEX_ID + 1;
    public static final int INDEX_IMPORTANT = INDEX_ID + 2;
    //用于日志的TAG
    public static final String TAG = "RemindersDbAdapter";

    //两个数据库API对象
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    //数据库名称、主表名称、版本的常量
    public static final String DATABASE_NAME = "dba_remdrs";
    public static final String TABLE_NAME = "tbl_remdrs";
    public static final int DATABASE_VERSION = 1;

    //上下文对象
    private final Context mCtx;

    //用于创建数据库的SQL语句
    private static final String DATABASE_CREATE =
            "CREATE TABLE if not exists " + TABLE_NAME + " ( " +
                    COL_ID + "INTEGER PRIMARY KEY autoincrement, " +
                    COL_CONTENT + " TEXT, " +
                    COL_IMPORTANT + " INTEGER );";

    public RemindersDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    public void open() throws SQLException{
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
    }

    public void close(){
        if(mDbHelper != null){
            mDbHelper.close();
        }
    }

    //数据库的增删改查
    public void createReminder(String name, boolean important){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT, name);
        values.put(COL_IMPORTANT, important?1:0);
        mDb.insert(TABLE_NAME,null,values);
    }

    public long createReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        return mDb.insert(TABLE_NAME,null,values);
    }

    public Reminder fetchReminderById(int id){
        Cursor cursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
                COL_CONTENT,COL_IMPORTANT},COL_ID + "=?",
                new String[]{String.valueOf(id)},null,null,null,null);
        if(cursor != null)
            cursor.moveToFirst();

            return new Reminder(
              cursor.getInt(INDEX_ID),
              cursor.getString(INDEX_CONTENT),
                    cursor.getInt(INDEX_IMPORTANT)
            );
    }

    public Cursor fetchAllReminders(){
        Cursor mCursor = mDb.query(TABLE_NAME,new String[]{COL_ID,
        COL_CONTENT,COL_IMPORTANT},null,null,null,null,null);

        if (mCursor != null){
            mCursor.moveToFirst();
        }

        return mCursor;
    }

    public void updateReminder(Reminder reminder){
        ContentValues values = new ContentValues();
        values.put(COL_CONTENT,reminder.getContent());
        values.put(COL_IMPORTANT,reminder.getImportant());
        mDb.update(TABLE_NAME,values,
                COL_ID+"=?",new String[]{String.valueOf(reminder.getId())});
    }

    public void deleteReminderById(int nId){
        mDb.delete(TABLE_NAME,COL_ID+"=?",new String[]{String.valueOf(nId)});
    }

    public void deleteAllReminders(){
        mDb.delete(TABLE_NAME,null,null);
    }

    private static class DatabaseHelper extends SQLiteOpenHelper{
        //构造函数完成数据库初始化
        DatabaseHelper(Context context){
            //将数据库名和版本号传给超类,由超类完成建立数据库的繁重工作
            super(context,DATABASE_NAME,null,DATABASE_VERSION);
        }

        public void onCreate(SQLiteDatabase db){
            Log.w(TAG,DATABASE_CREATE);
            db.execSQL(DATABASE_CREATE);
        }

        public void onUpgrade(SQLiteDatabase db,int oldVersion, int newVersion){
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
            onCreate(db);
        }
    }
}

CursorAdapter

最后,需要一种从数据库获取备忘并加入到ListView中的方法,新建java类RemindersSimpleCursorAdapter

public class RemindersSimpleCursorAdapter extends SimpleCursorAdapter {
    public RemindersSimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
        super(context, layout, c, from, to, flags);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return super.newView(context, cursor, parent);
    }

    //ListView会利用屏幕上的单个View对象反复调用此方法,Adapter的职责就是使用列表项来填充这些视图
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        super.bindView(view, context, cursor);//调用超类方法,通过游标cursor获取到的值映射到View中的元素

        ViewHolder holder = (ViewHolder)view.getTag();
        if(holder == null){ //检查holder是否绑定到了标签
            holder = new ViewHolder();
            holder.colImp = cursor.getColumnIndexOrThrow(RemindersDbAdapter.COL_IMPORTANT);
            holder.listTab = view.findViewById(R.id.row_tab);
            view.setTag(holder);
        }

        //使用当前备忘COL_IMPORTANT常量对应的值来决定颜色1:重要 0:次要
        if(cursor.getInt(holder.colImp) > 0){
            holder.listTab.setBackgroundColor(context.getResources().getColor(R.color.orange));
        }
        else{
            holder.listTab.setBackgroundColor(context.getResources().getColor(R.color.green));
        }
    }

    //静态内部类
    static class ViewHolder{
        int colImp; //Important表列的索引
        View listTab;   //在布局中定义的row_tab视图
    }
}

调整ReminderActivity

public class RemindersActivity extends AppCompatActivity {

    private ListView mListView;
    private RemindersDbAdapter mDbAdapter;
    private RemindersSimpleCursorAdapter mCursorAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reminders);
        mListView = (ListView)findViewById(R.id.reminders_list_view);
        mListView.setDivider(null);
        mDbAdapter = new RemindersDbAdapter(this);
        mDbAdapter.open();

        Cursor cursor = mDbAdapter.fetchAllReminders();

        String[] from = new String[]{RemindersDbAdapter.COL_CONTENT};

        int[] to = new int[]{R.id.row_text};

        mCursorAdapter = new RemindersSimpleCursorAdapter(
                RemindersActivity.this,
                R.layout.reminders_row,
                cursor,
                from,
                to,
                0
        );
        mListView.setAdapter(mCursorAdapter);
    }
}

调整完运行app,将不会在列表中看到任何内容,因为最后的修改插入的是SQLite功能而非示例数据

Ctrl+K提交备忘录实验1的最后一次修改

原文地址:https://www.cnblogs.com/hhhqqq/p/12273743.html

时间: 2024-07-30 12:14:12

《Android Studio实战 快速、高效地构建Android应用》--五、备忘录实验(1/2)的相关文章

工(程师)欲善其事,必先利其(编译)器——《Android Studio实战——快速、高效地构建Android应用》

Android Studio 是改变Android开发方式的编译器,<Android Studio实战--快速.高效地构建Android应用>是一本教人如何改变Android开发方式的书. 这本书无微不至讲述了如何用Android Studio编写代码,除了理论介绍外还有备忘录实验和货币实验告诉读者Android Studio并不是一个有编译功能的记事本而是带有代码补全.代码生成和设计代码风格等功能的编译器. 一样东西过时了,自然会有新的替代它.除了Android Studio取代eclips

《Android Studio实战 快速、高效地构建Android应用》--四、Git入门

Git版本控制系统(VCS)是分布式的,仓库的每一个副本均包含项目的完整历史 安装Git 下载 下载地址:http://git-scm.com/downloads 选择适合自己操作系统的来下载 如果下载太慢,解决方法: 国内镜像:https://github.com/waylau/git-for-win 复制下载链接,然后打开迅雷,使用迅雷下载 安装 自己选择路径安装,假设到D:\java下,装完后将D:\java\git\bin添加到PATH环境变量中,无误安装后右击选单出现: 打开Git B

Android Studio官方文档之构建和运行你的APP

Android Studio官方文档之构建和运行你的APP 本文由MTJH翻译,jkYishon审校. 前言 默认情况下,Android Studio设置新的项目并且部署到模拟器或者真机设备上,只需要点击几下.使用即时运行,你并不需要构建一个新的APK即可将改变后的方法和现有的应用资源应用到一个正在运行的应用程序中,所以代码的改变是即时可见的. 点击Run来构建并运行你的APP.Android Studio通过Gradle构建你的App,选择一个部署的设备(模拟器或连接的设备),然后把你的APP

【Android Studio探索之路系列】之六:Android Studio加入依赖

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWell [Android Studio探索之路系列]章节列表 [Android Studio探索之路系列]之中的一个:Android Studio开篇 [Android Studio探索之路系列]之二:Android Studio软件安装 [Android Studio探索之路系列]之

Android Studio编译运行Fresco Sample。Android缓存新境界。 (a problem occurred start process &#39;command &#39;ndk-build&#39;&#39;)

今天闲逛知乎,偶遇一篇“Android应用开发难点”,作为安卓程序猿,本能点进去,想看看究竟能有什么难点自己不知道的(夜郎自大..面壁中). 插件化,H5容器优化,网络.图片缓存..感觉都还好.直到看到“Fresco出来之前,你是不是觉得图片缓存已经到头了?” Fresco究竟是何方神圣!! 询问度娘得知,2015.3.27日之前就已经发布了(度娘的结果最早是3.27).通过查看GitHub: Version 0.1.0  tyronen released this 16 days ago · 

Android Studio 主题及字体修改,只需五步

在我们刚学习Android Studio时,最初可能就是Android Studio主题及字体的修改.其实很简单,只需要简单的五步. 1.打开设置功能窗口.两种方式: a. [File]菜单-->打开[Settings]选项: b. 工具栏中选中[Settings]图标打开. 2.找到[Appearance &Behavior]-->Appearance选项,IDE默认[theme]为Intellij主题.如图: 3.如上图,点击[Theme]下拉选项,选中[Darcula]主题,点击

Android studio: Gradle DSL method found: &#39;android()&#39;!及Gradle DSL method not found: &#39;runProguard()&#39;错误

Gradle DSL method not found: 'runProguard()'错误 出现这个错误是因为在新版本的Gradle中runProguard()方法已经被废弃,取而代之的是minifyEnabled,因此只要将每个module下的build.gradlew文件中的runProguard改成minifyEmabled即可.如下图: Gradle DSL method found: 'android()'错误 和上个错误一样这个也是因为在新版本的Gradle中android()方法

Android Studio 3.0找不到Android Device Monitor

因为自Android Studio 3.0开始弃用Android Device Monitor,Android Developers官网上的原话是: Android Device Monitor is a standalone tool that provides a UI for several Android app debugging and analysis tools. However, most components of the Android Device Monitor are

Android Studio 中快速提取方法

在开发过程中,有时在一个方法内部写了过多的代码,然后想要把一些代码提取出来封装下,分离开放在一个单独的方法里,可能你的做法是直接选中后Ctrl + 叉,或者 Ctrl + C,但在Android Studio中有了一个快速提取的方法:Ctrl + Alt + M