第六章:Reminders 实验:第二部分

优质
小牛编辑
131浏览
2023-12-01

这章涵括了通过对话框捕获用户输入。也继续演示适配器及SQLite数据库的使用。这章里,我们将完成从第五章开始的例子。

增加/删除提醒

第五章里这个例子的屏幕还没有任何提醒。为了让布局看到提醒清单,当app启动时加载些提醒的例子上去,这是很有用的。如果你想挑战这章处理过程,比较下清单6-1和你的代码。清单6-1检查是否有保存的实例,如果有,处理将设置例子数据。为此,代码调用了些DatabaseAdapter的方法;一个是清除所有的提醒,另一个则是加入些提醒。

Listing 6-1. Add Some Example Reminders

public class RemindersActivity extends ActionBarActivity {
    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();
        if (savedInstanceState == null) {
            //Clear all data
            mDbAdapter.deleteAllReminders();
            //Add some data
            mDbAdapter.createReminder("Buy Learn Android Studio", true);
            mDbAdapter.createReminder("Send Dad birthday gift", false);
            mDbAdapter.createReminder("Dinner at the Gage on Friday", false);
            mDbAdapter.createReminder("String squash racket", false);
            mDbAdapter.createReminder("Shovel and salt walkways", false);
            mDbAdapter.createReminder("Prepare Advanced Android syllabus", true);
            mDbAdapter.createReminder("Buy new office chair", false);
            mDbAdapter.createReminder("Call Auto-body shop for quote", false);
            mDbAdapter.createReminder("Renew membership to club", false);
            mDbAdapter.createReminder("Buy new Galaxy Android phone", true);
            mDbAdapter.createReminder("Sell old Android phone - auction", false);
            mDbAdapter.createReminder("Buy new paddles for kayaks", false);
            mDbAdapter.createReminder("Call accountant about tax returns", false);
            mDbAdapter.createReminder("Buy 300,000 shares of Google", false);
            mDbAdapter.createReminder("Call the Dalai Lama back", true);
            }
        //Removed remaining method code for brevity...
    }
    //Removed remaining method code for brevity...
}

有几个createReminder()方法的调用,每个都是用一个字符串作提醒文本,以及一个布尔值标记提醒是否重要。我们设置一些实体值让显示好看点。选中createReminder()方法调用的代码块按以Ctrl+Alt+M | Cmd+Alt+M汲取方法,如图6-1示。这是一个通过重构菜单和快捷键结合的重构操作。输入insertSomeReminders作为方法名并确认。这些代码块将以析出的方法代替,这些代码块将在方法体里了。

图6-1 析出方法对话框,创建一个insertSomeReminders()方法

运行app看到的界面,拥有提醒例子了。你的app应该看起来象图6-2的截屏那样。有些提醒显示绿色的行选项卡,而那些重要的提醒则显示橙色行选项卡。提交你的更改到Git,备注Adds Example reminders。

图6-2 插入了提醒例子的实时运行

响应用户的互动

没有app不响应输入。在这节,你将加入响应点击事件的逻辑并且最终允许用户编辑独立的提醒。在app里的主要元素是ListView,一个Android View的子类。直致此刻,除了把它放到布局里,你还没做过其它的什么。android.view.View是所有你看到的所有屏幕元素的超类。

把清单6-2的代码加到RemindersActivityonCreate()方法后面,即在方法结束花括号之前,并解决导入类的问题。这是一个匿名内部类实现OnItemClickListener接口,它只有一个方法,onItemClicked()。这个对象将用于你互动与它所跟踪的ListView元素的实时运行。当用户点击ListView时,匿名内部类的onCreate()方法将被调用。我们定义一个吐司,一个Android SDK的类。调用Toast.makeText()将导致在屏幕上弹出一个小菜单,显示出你传送给方法的文本。你可以看到清单6-2的代码,作为快速正确使用吐司的指引。

注意:吐司信息可能在一些设备上被隐藏。另一个替代途径是记录一个日志信息,用Android的日志记录器,那个在第十二章会有祥述。

Listing 6-2. Set an OnItemClickListener with a Toast

//when we click an individual item in the listview
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(RemindersActivity.this, "clicked " + position,
            Toast.LENGTH_SHORT).show();
    }
});

点击屏幕第一个提醒将调用onItemClick()方法,在清单里的位置是0,所以索引是0。这个逻辑将弹出位置信息的文本,如图6-3所示。

图6-3 点击第一条提醒弹出的吐司信息

用户对话框

大家所熟悉的一些点击事件,现在你可增强点击监听为显示一个对话框。用清单6-3的代码替换所有的onItemClick()方法。解决导入时,请用android.support.v7.app.AlertDialog类。

Listing 6-3. onItemClick( ) Modifications to Allow Edit/Delete

public void onItemClick(AdapterView<?> parent, View view, final int masterListPosition, long id) {
    AlertDialog.Builder builder = new AlertDialog.Builder(RemindersActivity.this);
    ListView modeListView = new ListView(RemindersActivity.this);
    String[] modes = new String[] { "Edit Reminder", "Delete Reminder" };
    ArrayAdapter<String> modeAdapter = new ArrayAdapter<>(RemindersActivity.this,
    android.R.layout.simple_list_item_1, android.R.id.text1, modes);
    modeListView.setAdapter(modeAdapter);
    builder.setView(modeListView);
    final Dialog dialog = builder.create();
    dialog.show();
    modeListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            //edit reminder
            if (position == 0) {
                Toast.makeText(RemindersActivity.this, "edit "
                    + masterListPosition, Toast.LENGTH_SHORT).show();
                //delete reminder
            } else {
                Toast.makeText(RemindersActivity.this, "delete "
                    + masterListPosition, Toast.LENGTH_SHORT).show();
            }
            dialog.dismiss();
        }
    });
}

在处理代码里你看到了另Android类在工作,AlertDialog.Builder。这个类创建器嵌入在AlertDialog类里,是个静态类,它用于创建AlertDialog

在这个实例里的代码远超于创建一个ListView以及用ArrayAdapter喂入条目到ListVite。你可能回想起第五章的这种模型。这个章节建立一个有两个潜在元素(选项)的数组,编辑提醒和删除提醒,在传送到ListView之前,这个是,轮流地,传送给AlertDialog.Builder。这个创建器然后用这些选项清单创建并显示一个对话框。

注意下代码清单6-3最后的选择部分。有点象之前的OnItemClickListener();不管怎样,我们附加了一个modeListView的监听器,那个在当前OnItemClickListener里创建的。你看到的是带有OnItemClickListenerListView(指的是activity_reminders.xml里的reminders_list_view),(译者:在它的onItemClick方法里)创建了另一个modeListView还有另一个嵌入的OnItemClickListener来响应来自modeListView的选项点击事件。

内嵌的点击监听器弹出一个吐司信息来指示是编辑还是删除所点击的条目。它也重命名来自外部调用者OnItemClickListener的位置参数叫做masterListPosition来区分内置的OnItemClickListener的位置参数。这个masterListPosition用于吐司指明哪条提醒被用于可能的编辑或删除。最后,dialog.dismiss()方法调用于点击监听器,用来完全移除对话框。

运行一下来测试新特性如图6-4所示。点击一条提醒接着在弹出对话框再点编辑或删除。如果吐司里报告的提醒位置和你点击的对不上,再确认一下你吐司里追加masterListPosition值而不是postion。按Ctrl+K | Cmd+K提交更改到Git,并附上信息Adds a ListView dialog for individual list items。

图6-4 模拟删除一条提醒

提供多选上下文菜单

随着这个app逐渐成形,现在将达成这种特性:在一次操作里允许多选提醒条目用于编辑。这种特性只能在API 11或更高版本上才有效。你将通过使用资源载入协定来有条件地达成这种特性。这个处理将在这章稍后解释并且第8章有所有的细节。你也将需要包含一个运行时检查看看是否支持这种特性。

为提醒行条目创建另一个备用的布局。打开项目工具窗口在资源夹上右击带出来的上下文。选择新的Android资源文件命名为reminders_row,如图6-5示。

图6-5 新资源文件reminders_row

选择资源类型为布局,这样就自动改变目录名为layout。在有效限定词部分选择相应的版本然后双击(>>)双V纹章按钮增加到选择的限定词里。输入11作为平台API级别并注意到目录名更新了,并反映了选择的限定词版本。那叫资源限定并且它们整合于运行时里,让你可以为特别的设备和平台版本定制你的用户界面。按回车(或点OK)接受这个新资源限定目录并继续。如果你打开项目工具窗并设为Android示图,如图6-6,你将看到layout文件夹下所有的reminders_row布局文件在一起。还有,Android示图的项目工具窗让相关联的文件聚集在一起使你有效地管理它们。

图6-6 聚集的布局

复制整个原始的reminders_row布局并粘贴到刚新建版本11的布局里。现在修改内层的水平线性布局的背景色属性如下:

android:background="?android:attr/activatedBackgroundIndicator"

这个值分配背景色属性带前缀?android:attr/,这是参考定义于Android SDK里的一种风格。Android SDK提供了很多这样的预定义属性,并且你可以使用它们到你的app上。在多选模式时activatedBackgroundIndicator属性使用了系统定义的有效背景色。

目标于早期的SDK

现在你将学习如何引入一个平台依赖的特性。打开项目工具窗并打开在Gradle剧本区下面的app模块 build.gradle文件(它会在第二个入口里)。Gradle文件含有编译的构建逻辑和app的包装。参照你的app所支持的平台所有配置存在于这些特别的文件里(第十三章深度探索了Gradle构建系统)。注意到最低SDK版本设置为8,这让你的app可以运行在99%的Android设备上。现在我们将创建的这个特性需要最低SDK版本是11。涵括这节的代码和特性将允许用户运行在SDK11或更高版本带来的更先进的特性,叫上下文动作模式。而且,低于SDK11的将不会有这个特性,但更重要的是,他们的app不会因此而崩溃。

加入上下文动作模式

接下来介绍的多选模式的上下文动作模式菜单,是一个动作清单可用于所有选择项的上下文。加载一个新的菜单资源,在res/menu目录上右击选择New ➤ Menu资源文件并命名为cam_menu。以下列代码清单完成它:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android";>
    <item android:id="@+id/menu_item_delete_reminder"
    android:icon="@android:drawable/ic_menu_delete"
    android:title="delete" />
</menu>

这个资源文件为上下文菜单定义了唯一的删除动作条目。这里你也正用了一点不同的属性值。这个特别的属性象之前的背景色属性一样也是访问Android内建的缺省值。不管怎样,在那前缀?android:attr/ prefix只用于参考风格属性。在这用在属性上的语法参照一个稍有不同的格式。用at符号@触发一个到资源值查询的命名空间。你可以用这种方式访问变量命名空间。Android命名空间是所有内建Android值所在的地方。用这个命名空间是多种变量资源所存在的如drawable,string以及布局。当你用了@+id为前缀,它会创建一个新的ID在你的项目的R.java文件里,且当你用到@id前缀,它会查找Andriod SDK的R.java文件存在的ID。这个例子里定义了一个新的ID名,menu_item_delete_reminder,它结合于菜单选项。它也从android:drawable命名空间里拉出一个图标,作为它的图标。

用新的上下文菜单和一个备用的布局运行于API 11或更高版本,你可以加载一个有条件的检查以允许带有上下文动作菜单的多选模式。打开RemindersActivity并加上下面的代码到onCreate方法的后面。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

}

构建类从android.os包里导入并且给你访问一系列的常量值,那些可用于以指定的API级别来匹配设备。在这个例子里,你期望API级别等于或高于HONEYCOMB而它实际包含整数11。把清单6-4的代码插入到刚定义的块里。IF块保护了运行OS低于HONEYCOMB的系统不让这个app崩溃。

Listing 6-4. MultiChoiceModeListener Example

mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
mListView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean
    checked) { }
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.cam_menu, menu);
        return true;
    }
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
    }
    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_item_delete_reminder:
                for (int nC = mCursorAdapter.getCount() - 1; nC >= 0; nC--) {
                    if (mListView.isItemChecked(nC)) {
                        mDbAdapter.deleteReminderById(getIdFromPosition(nC));
                    }
                }
                mode.finish();
                mCursorAdapter.changeCursor(mDbAdapter.fetchAllReminders());
                return true;
        }
        return false;
    }
    @Override
    public void onDestroyActionMode(ActionMode mode) { }
});

解决所有导入的问题。你将注意到getIdFromPosition()没有定义并标为红色(错误)。把光标放在这个方法上并按劳取酬Alt_Enter调用IntelliSense并选择创建方法。选择RemindersActivity作为目标类。选择整型作为返回类型。用清单6-5的代码完成这个方法。

Listing 6-5. getIdFromPosition() method

private int getIdFromPosition(int nC) {
    return (int)mCursorAdapter.getItemId(nC);
}

此处理逻辑定义了一个MultiChoiceModeListener并把它附到ListView。无论何时你长按ListView的一个条目时,运行时调用onCreateActionMode()方法在MultiChoiceModeListener上。如果方法返回真值,就进入多选动作模式。这里重写方法的逻辑填充了一个上下文菜单用于显示动作条。使用多选动作模式的好处是你可以选择多行。一次点击选中了某个条目,接下来再点击则会去选这个条目。当你点击上下文菜单里的每个条目,运行时将带着被点的条目调用onActionItemClicked()方法。

在这个方法里,通过比较itemId和你加到菜单项里的删除元素的id,检查下删除条目是否被点击了。(看看在本节开始时所描述有关删除条目ID的XML清单)。如果这个条目被选,轮询所有的清单条目并要求mDbAdapter来删除它们。在删除所选条目后,逻辑调用动作模式对象finish()方法,这将禁止多选动作模式并返回ListView到普通模式。接下来你调用fetchAllReminders()方法众数据库重新装载所有的提醒条并传送在mCursorAdapter对象changeCursor方法所返回的游标。最后,方法返回真值来表明动作被正确地处理了。所有其它的没被正确处理的逻辑,方法返回假,表明一些其它的事件监听器可能处理了这个点击事件。

Android Studio将高亮一堆错误语句,因为你正使用的API无效或低于Honeycomb。这外错误生成于Lint,一个状态分析工具内建于Android SDK并完全整合在Android Studio里。你需要加上下面的声明在RemindersActivity.onCreate()方法里,在@Override声明之上或之下都行,而且要目标API解决导入问题:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

这个特别的声明告诉Lint欺骗方法调用是提供的API级别的目标,不管构建配置所指定的。提交更改到Git用Adds Contextual Action Mode with context action menu作注释。图6-7描写了你可能看到的新特性。

图6-7 允许多选模式

Implementing Add, Edit, and Delete

迄今为止,你已经添加了从清单里删除提醒的逻辑。这个逻辑在上下文动作模式里也可以有效执行。目前还无法插入新提醒或编辑现有的提醒。无论如何,你将创建一个用户自定义对话框或添加提醒,另一个用来编辑现有的提醒。最终,你将绑定这些对话框到RemindersDbAdapter

在处理这个之前,先添加一些新的颜色。把这它们加到colors.xml文件里:

<color name="light_grey">#bababa</color>
<color name="black">#000000</color>
<color name="blue">#ff1118ff</color>

注意:通常,你的app可能有一个全面的颜色风格,这将保证所有屏幕和对话框的一致性。无论如何,颜色风格超出了这个简单例子的范围了。

策划一个用户对话框

开发的一个好习惯是:以简单工具优于执行它来描绘你的用户界面。这样在引入任何代码之胶,让你图形化元素如何适合屏幕。你可用一个编辑器如inkscape,它是跨平台的,或者你可以用笔记本的纸张和铅笔。在移动商务里,这些描绘称作线框图(wireframe)。

图6-8是我们的用户对话框的插图,完成于inkscape。这个线框图有意不正式,来强调元素的摆放好过一个精准的外观和感觉。

图6-8 线框图描绘用户自定义对话框

注意:这本书里的一些用户自定义的绘图和线框图正是用lnkspace创建的,一个多平台向量图形编辑器。在www.inkscape.org上,它是免费的。

把线框图就在那了,你可以开始计划如何排列屏幕的元素。因为大多数元素从上排到下,在最外层用一个竖直线性布局,这是最显然的选择。不管怎样,底下的两个按钮并排在一起。这样你可以用一个水平线性布局放在前面的竖直线性布局里。图6-9加上一些声明来描绘和高亮这些内嵌的元件。

图6-9 线框图描绘widget标签

从策划到代码

随着线框图在那,尽量用图形化设计器来开发布局。开始在res目录上右击并选择创建一个新的Android资源文件然后命名为dialog_custom,资源类型为布局资源。以线性布局作为根元素,完成对话框。接着我们的线框图,从调色板拖放视图控件到这个平台上。清单6-6包含了这个完成的XML布局文件,并带有将在代码中用到的ID值。

Listing 6-6. Completed dialog_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/custom_root_layout"
android:layout_width="300dp"
android:layout_height="fill_parent"
android:background="@color/green"
android:orientation="vertical"
>
    <TextView
    android:id="@+id/custom_title"
    android:layout_width="fill_parent"
    android:layout_height="60dp"
    android:gravity="center_vertical"
    android:padding="10dp"
    android:text="New Reminder:"
    android:textColor="@color/white"
    android:textSize="24sp" />
    <EditText
    android:id="@+id/custom_edit_reminder"
    android:layout_width="fill_parent"
    android:layout_height="100dp"
    android:layout_margin="4dp"
    android:background="@color/light_grey"
    android:gravity="start"
    android:textColor="@color/black">
    <requestFocus />
    </EditText>
    <CheckBox
    android:id="@+id/custom_check_box"
    android:layout_width="fill_parent"
    android:layout_height="30dp"
    android:layout_margin="4dp"
    android:background="@color/black"
    android:paddingLeft="32dp"
    android:text="Important"
    android:textColor="@color/white" />
    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
        <Button
        android:id="@+id/custom_button_cancel"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_weight="50"
        android:text="Cancel"
        android:textColor="@color/white"
        />
        <Button
        android:id="@+id/custom_button_commit"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_weight="50"
        android:text="Commit"
        android:textColor="@color/white"
        />
    </LinearLayout>
</LinearLayout>

生成一个用户对话框

现在RemindersActivity里用完成的对话框布局了。清单6-7实现了一个新的fireCcustomDialog()方法。把这些代码放到RemindersActivit文件里去,仅在onCreateOptonsMenu()方法上面,并解决好导入的问题。

Listing 6-7. The fireCustomDialog( ) Method

private void fireCustomDialog(final Reminder reminder){
    // custom dialog
    final Dialog dialog = new Dialog(this);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(R.layout.dialog_custom);
    TextView titleView = (TextView) dialog.findViewById(R.id.custom_title);
    final EditText editCustom = (EditText) dialog.findViewById(R.id.custom_edit_reminder);
    Button commitButton = (Button) dialog.findViewById(R.id.custom_button_commit);
    final CheckBox checkBox = (CheckBox) dialog.findViewById(R.id.custom_check_box);
    LinearLayout rootLayout = (LinearLayout) dialog.findViewById(R.id.custom_root_layout);
    final boolean isEditOperation = (reminder != null);
    //this is for an edit
    if (isEditOperation){
        titleView.setText("Edit Reminder");
        checkBox.setChecked(reminder.getImportant() == 1);
        editCustom.setText(reminder.getContent());
        rootLayout.setBackgroundColor(getResources().getColor(R.color.blue));
    }
    commitButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String reminderText = editCustom.getText().toString();
            if (isEditOperation) {
                Reminder reminderEdited = new Reminder(reminder.getId(),
                reminderText, checkBox.isChecked() ? 1 : 0);
                mDbAdapter.updateReminder(reminderEdited);
                //this is for new reminder
            } else {
                mDbAdapter.createReminder(reminderText, checkBox.isChecked());
            }
            mCursorAdapter.changeCursor(mDbAdapter.fetchAllReminders());
            dialog.dismiss();
        }
    });
    Button buttonCancel = (Button) dialog.findViewById(R.id.custom_button_cancel);
    buttonCancel.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            dialog.dismiss();
        }
    });
    dialog.show();
}

fireCustomDialog( )方法将用于插入和编辑,就算它们间有少许不同。方法的开始三行创建了一个Android对话框,没有标题并填充清单6-6的布局。fireCustomDialog( )方法接着从这个布局里查找所有重要的元素并储存到本地变量里。然后方法查看reminder参数是否为空,设置一个isEditOperation布尔变量。如果说有一个提醒被传入(或者值为不空),方法会假设这不是一个编辑操作并把这个变量设为假;否则,设为真。如果调用fireCustomDialog( )方法的是一个编辑操作,标题设为Edit Reminder而且CheckBoxEditText用提醒参数的值来设定。这个方法也设置了最外层容器布局的背景色为蓝色,为了从视觉上区别插入对话框和编辑对话框。

接下来的几行由一个代码块组成,为提交按钮设置及定义一个OnClickListener。这个监听器响应点击事件,更新数据库。再次,isEditOperation()被检查了,如果编辑操作进行中,那么一个新提醒创建了,用来自提醒参数的ID以及EditTex的值还有在屏的复选框值。这个提醒传送给mDbAdapter,通过updateReminder()方法。

如果编辑没有进行,这个逻辑查询mDbAdapter来创建一个新的提醒到数据库,用EditText的值还有在屏的复选框值。在无论是更新或创建调用后,所有提醒通过mCursorAdapter.changeCursor()方法会重新装载.这个逻辑有点象之前添加的清单6-5代码。点击监听器在提醒重新装载后解散对话框。

在配置提交按钮的点击行为后,这个例子设置了另一个取消按钮的监听器。这个监听器只是简单地解散对话框。所有按钮的行为定义后,这个例子结束于显示用户对话框。

现在你可以为在onCreate()方法里的modeListView用这个新方法在OnItemClickListener中。为这个监听器找到onItemClick()方法用下面的代码替换原来所有代码。

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    //edit reminder
    if (position == 0) {
        int nId = getIdFromPosition(masterListPosition);
        Reminder reminder = mDbAdapter.fetchReminderById(nId);
        fireCustomDialog(reminder);
        //delete reminder
        } else {
        mDbAdapter.deleteReminderById(getIdFromPosition(masterListPosition));
        mCursorAdapter.changeCursor(mDbAdapter.fetchAllReminders());
    }
    dialog.dismiss();
}

为了编辑提醒,去掉Toast.makeText()调用,替换为用ListView位置来找到提醒的调用。这价目提醒然后被传送到fireCustomDialog()方法来触发编辑行为。为删除提醒,用和清单6-5一样的代码逻辑。再次,mDbAdapter.deleteReminderById()用于删除提醒,并且changeCursor()用于从mDbAdapter.fetchAllReminders()调用中返回游标。

找到onOptionsItemSelected()方法,在RemindersActivity.java文件很底部的地方,编辑它如清单6-8。

Listing 6-8. onOptionsItemSelected Definition

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_new:
            //create new Reminder
            fireCustomDialog(null);
            return true;
        case R.id.action_exit:
            finish();
            return true;
        default:
            return false;
    }
}

这里,当你选择了菜单项是action_new,简单地调用了fireCustomDialog()。传送一个空值给它,象之前提及的空值检查并设置isEditOperation为假,然后征用一个新提醒对话框。运行app检查下新特性。你会看到新的用户对话框。当创建新提醒时,你将看到一个绿色对话框,而编辑提醒时是一个蓝色的对话框,如图6-10和6-11所分别对应的。测试菜单项确保创建和删除操作功能象它们应该的那样。提交更改到Git,附上提交信息:Adds database Create, Read, Update, and Delete support with custom dialogs。

图6-10 新提醒对话框

图6-11 编辑提醒对话框

加上用户图标

所有的特性都放置好了,你可以加上一个用户图标作为完成点缀。可以用任何图形编辑器来创建它,或者如果你不喜欢图形编辑,从网上下点免费的。我们的例子放置ic_launcher图标,它创建于Inkscape。找开项目工具窗并右击res/mipmap目录。选New ➤ Image Asset。将看到图6-12那样的对话框。点击Image File:选择框最右边的省略号图标,导航到你要的图片位置并插入进来。让其他的设置如图6-13所示。点下一步,接着点完成。

图6-12 新图片资源对话框

有不少目录使用了mipmap这个名字。这些目录每个都有着后缀,那个指派屏幕尺寸限定。Android运行时将从特别的目录里拉出资源,依照app所运行设备屏幕的解析度。资源夹和它们的后缀在第8章将有祥解。

插入下面的几行代码到onCreate()方法里,在填充布局之后,即setContentView(R.layout.activity_reminders);。这些代码将显示用户图标在你的动作条里:

ActionBar actionBar = getSupportActionBar();
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setIcon(R.mipmap.ic_launcher);

运行代码,将看到用户图标在动作条里。图6-13展示了以用户图标运行的app。

图6-13 动作条的用户图标

Ctrl+K | Cmd+K提交更改到Git,以Adds a custom icon作为提交信息。

小结

恭喜!你已实现了使用Android Studio完成的第一个Android app。在这个过程里,你学会了用图形化设计器编辑XML布局文件。你也学会了如何用文本模式编辑一个行XML布局。这章展示了在支持的平台上如何有条件地实现上下文动作模式。最后,你看到了如何加载一个用户图标到不同的分辨率的屏幕上。