Android与 MVC设计模式&activity的生命周期
仍然接着以上篇的项目工程为例进行学习总结。
一.创建新类
在项目工具窗口中,右键单击com.bignerdranch.android.geoquiz类包,选择New → Java Class 菜单项,并填入类名。
例:添加一个Question类
public class Question {
private int mTextResId;
private boolean mAnswerTrue;
public Question(int textResId, boolean answerTrue){
mTextResId = textResId;
mAnswerTrue = answerTrue;
}
}
Question类中封装两部门数据:问题文本和问题答案(true或false)。mTextResId是int类型的原因是因为变量mTextResId用来保存地理知识问题字符串的资源ID,而资源ID总是int类型。
新增的两个变量可设置由Android Studio自动 生成getter方法与setter方法。
目前为止,本应用各个类协同工作如下图所示:
二.Android与 MVC设计模式
MVC(Model-View-Controller):基于模型-视图-控制器。MVC设计模式表明,应用的任何对象, 归根结底都属于模型对象、视图对象以及控制器对象中的一种。
模型对象存储着应用的数据和业务逻辑。模型对象不关心用户界面,它为存储和管理应用数据而生。
视图对象知道如何在屏幕上绘制自己,以及如何响应用户的输入,如触摸动作等。一个 简单的经验法则是,凡是能够在屏幕上看见的对象,就是视图对象。
控制器对象含有应用的逻辑单元,是视图对象与模型对象的联系纽带。控制器对象响应 视图对象触发的各类事件,此外还管理着模型对象与视图层间的数据流动。
至此我们可以继续更新视图层添加视图对象。如添加一个按钮:
<Button android:id="@+id/next_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/next_button" />
同时给按钮添加字符串资源以及对问题字符串进行更新。
如同之前一样,接下来为按钮设置监听器:
mNextButton = (Button) findViewById(R.id.next_button); mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
int question = mQuestionBank[mCurrentIndex].getTextResId(); mQuestionTextView.setText(question);
}
});
现在,更新mQuestionTextView变量的相同代码分布在两个不同的地方,可以把公共代码放在单独的私有方法里,然后在mNextButton监听器里以及onCreate (Bundle)方法的末尾分别调用它,以初步设置activity视图中的文本。
再在QuizActivity.java文件中,QuizActivity类下,添加checkAnswer(boolean)方法。
private void checkAnswer(boolean userPressedTrue) {
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
int messageResId = 0;
if (userPressedTrue == answerIsTrue) {
messageResId = R.string.correct_toast;
} else {
messageResId = R.string.incorrect_toast;
}
Toast.makeText(this, messageResId, Toast.LENGTH_SHORT) .show();
}
三.添加图标资源
选择并复制图片目录,然后粘贴到Android Studio的app/src/main/res目录中。完成后,在Android Studio的项目工具窗口,就可以看到这些目录。任何添加到res/drawable目录中、后缀名为.png、.jpg或者.gif的文件都会自动获得资源ID。
在XML文件中引用资源。在XML资源文件中,通过资源类型和资源名称,可引用其他资源。以@string/开头的定义 是引用字符串资源。以@drawable/开头的定义是引用drawable资源。
四.activity的生命周期
状态图解:
Activity的状态:
任何时候只能有一个 activity处于用户能交互的运行状态。
通常,通过覆盖onCreate(Bundle)方法,activity可以预处理以下UI相关工作: q 实例化组件并将它们放置在屏幕上(调用setContentView(int)方法) ; q 引用已实例化的组件; q
为组件设置监听器以处理用户交互; q
访问外部模型数据。
五.日志跟踪理解 activity生命周期
输出日志信息:
public static int d(String tag, String msg)
d代表“debug”,用来表示日志信息的级别。第一个参数是日志的来源,第二个参数是日志的具体内容。第一个参数通常以类名为值的TAG常量传入。这样,就很容易看出日志信息的来源。
在QuizActivity.java中新增一个TAG常量:
private static final String TAG = "QuizActivity";
再为onCreate(Bundle)方法添加日志输出代码:
接下来,在QuizActivity类的onCreate(Bundle)之后,覆盖其他五个生命周期方法。
注意:首先调用超类计算方法,再调用其他方法。
应用运行时,可以使用LogCat工具查看日志。
六.设备旋转与 activity生命周期
旋转设备会改变设备配置(device configuration)。设备配置实际是一系列特征组合,用来描述设备当前状态。这些特征有:屏幕方向、屏幕像素密度、屏幕尺寸、键盘类型、底座模式以及语言等。为匹配不同的设备配置,应用会提供不同的备选资源。
在运行时配置变更(runtime configuration change)发生时,可能会有更合适的资源来匹配新的设备配置。于是,Android销毁当前activity,为新配置寻找佳资源,然后创建新实例使用这些资源。
所以这就要创建水平模式布局。右键单击res目录后选择New → Android resource directory菜单项,从资源类型(Resource type) 列表中选择layout,保持Source set的main选项不变。接下来选中待选资源特征列表中的Orientation,然后单击>>按钮将其移动至已选资源特征 (Chosen qualifiers)区域。 最后,确认选中Screen orientation下拉列表中的Landscape选项,并确保目录名(Directory name)显示为layout-land。从res/layout目录复制activity_quiz.xml文件至res/layout-land目录,并修改代码:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/question_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:padding="24dp"
/>
<LinearLayout
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:orientation = "horizontal">
<Button
android:id="@+id/true_button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_gravity="bottom|right"
android:text = "@string/true_button" />
<Button
android:id="@+id/false_button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "@string/false_button" />
</FrameLayout>
但是现在设备旋转后还是回到第一题,所以下面要做的就是保存数据,即旋转后新建的QuizActivity需要知道mCurrentIndex变量的原值。覆盖以下Activity 方法就是一种解决方案:
protected void onSaveInstanceState(Bundle outState)
设置一个KEY_INDEX ,在它不为null时,旋转之后也显示当前正在回答的问题。