天天看点

Android开发学习笔记(二)——Android与MVC设计模式创建Question类MVC设计模式

现在即将升级上一节的应用,在之前的基础上,我们要做到

  • 提供更多的测试题目
  • 能够通过前进、后退按钮(使用ImageButton)实现题目的切换

创建Question类

由于要提供更多的测试题目,我们在这里会创建一个Question类,很容易地可以想到,该类中应该保存题目和答案,以及构造函数和对应的get方法和set方法。Question类的代码如下

public class Question {
    private int mTextResId;
    private boolean mAnswerTrue;

    public Question(int textResId, boolean answerTrue) {
        mTextResId = textResId;
        mAnswerTrue = answerTrue;
    }

    public int getTextResId() {
        return mTextResId;
    }

    public void setTextResId(int textResId) {
        mTextResId = textResId;
    }

    public boolean getAnswerTrue() {
        return mAnswerTrue;
    }

    public void setAnswerTrue(boolean answerTrue) {
        mAnswerTrue = answerTrue;
    }
}
           

快速生成get、set方法和构造函数

get、set和构造函数可以有Android Studio自动为我们生成,具体方法如下

1.配置Android Studio识别成员变量的m前缀

打开Android Studio,点击File→Settings菜单,选择Editor→Code Style选项,在Java选项卡下选择Code Generation选项页。如下图所示添加Field和Static field,这个作用是识别成员变量的前缀m,在为mTextResId生成get方法时,它生成的是getTextResId()而不是getMTextResId()。在这里我们暂时不会用到s前缀。

Android开发学习笔记(二)——Android与MVC设计模式创建Question类MVC设计模式

2.自动生成方法

现在我们可以在代码编辑页中点击右键-Generate-Constructor自动生成构造函数了。

Android开发学习笔记(二)——Android与MVC设计模式创建Question类MVC设计模式

按住CTRL键,选择mTextResId和mAnswerTrue成员变量,点击OK,构造函数就生成好了同样地,我们点击右键-Generate-Getter and Setter,选择两个成员变量,这样它们的get方法和set方法就已经自动生成好了,到这里为止,Question类也已经编写完了。

MVC设计模式

现在应用的整体结构如下图所示,我们使用Activity创建Question数组对象,通过TextView和Button的交互,在屏幕上显示问题,并根据用户的回答作出反馈

Android开发学习笔记(二)——Android与MVC设计模式创建Question类MVC设计模式

这就是MVC的架构模式(模型-视图-控制器),MVC设计模式表明,应用的任何对象,归根结底都属于模型对象、视图对象和控制器对象中的一种。

  • 模型对象存储着应用的数据和业务逻辑,模型类通常用来映射与应用相关的一些事物,如用户、商品、图片等,还有就是这里的Question类。
  • 视图对象知道如何在屏幕上绘制自己,以及如何响应用户的输入,凡是能够在屏幕上见到的对象,都属于视图对象。所有视图对象组成了应用的视图层,而视图层由XML文件中定义的各种组件构成
  • 控制器对象含有应用的逻辑单元,是视图对象和模型对象的联系纽带,控制器对象响应视图对象触发的各类事件,还管理着模型对象与视图层之间的数据流动
Android开发学习笔记(二)——Android与MVC设计模式创建Question类MVC设计模式

我们已经设计好了模型对象——Question类,现在要做的就是更新视图层和控制器对象了。

更新视图层

<?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:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/true_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            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"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/pre_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/arrow_left"/>

        <ImageButton
            android:id="@+id/next_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/arrow_right"/>
    </LinearLayout>

</LinearLayout>
           

我们的XML代码如上所示,与最开始的XML代码,我们为TextView组件添加了android:id属性,这是因为在切换题目时,我们需要更新TextView显示的内容。原有的True和False按钮保持不变,再添加了两个ImageButton按钮(我们当然可以继续使用Button按钮)

ImageButton的使用

顾名思义,ImageButton就是图像按钮,它不是以文字的方式显示,而是以图片的形式。它的使用方法与Button按钮类似,我们为其添加资源ID、高度和宽度,同时要为其添加android:src属性,该属性指定了ImageButton使用的是什么图片(图片资源保存在app/res/drawable目录下,我们需要提前将图片资源添加到此目录下),我们可以预览一下我们的布局

Android开发学习笔记(二)——Android与MVC设计模式创建Question类MVC设计模式

更新控制器层

创建Question数组

首先,我们要创建一个Question数组,Question的构造函数的参数有题目的资源ID和正确答案,题目的字符串资源已经添加到字符串资源文件中,这里不再赘述。

private Question[] mQuestionBank = new Question[]{
        new Question(R.string.question_tall, true),
        new Question(R.string.question_fat, false),
        new Question(R.string.question_single, false),
        new Question(R.string.question_yellow, true),
        new Question(R.string.question_happy, true),
        new Question(R.string.question_cold, true)
    };
           

更新TextView

在预览布局的时候可以发现,目前TextView组件是没有显示任何的内容的。下一步我们要做的就是更新TextView了。我们需要创建一个成员变量来绑定TextView组件。然后使用updateQuestion()函数更新题目(因为在点击Next和Pre按钮时,也需要更新TextView,所以我们使用这个函数封装相同的代码)

private TextView mTextView;
    mTextView = (TextView) findViewById(R.id.text_view);
    updateQuestion();
           

updateQuestion()函数的代码如下,其中mCurrentIndex是一个整型变量,初始值设为0,记录目前显示的题目Question数组中的索引。

private void updateQuestion(){
        int question = mQuestionBank[mCurrentIndex].getTextResId();
        mTextView.setText(question);
    }
           

现在我们运行应用,已经可以正常显示第一个题目了,下面我们要实现当点击Next和Pre按钮时更新题目,我们需要为这两个ImageButton设置监听器。代码如下,两个按钮的监听器代码有些许不同,这是因为,点击Pre按钮,我们需要将mCurrentIndex往前移一位,这有可能使它为负数,导致无法在Question数组中找到对应的问题。

mPreButton = (ImageButton) findViewById(R.id.pre_button);
    mNextButton = (ImageButton) findViewById(R.id.next_button);

    mNextButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
            updateQuestion();
        }
    });

    mPreButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(mCurrentIndex > 0) {
                mCurrentIndex = (mCurrentIndex - 1) % mQuestionBank.length;
                updateQuestion();
            }
            else{
                mCurrentIndex = mQuestionBank.length - 1;
                updateQuestion();
            }
        }
    });
           

检查答案

现在距离完成任务还差最后一步,我们要检查用户回答题目的正确性,因为每一个题目的答案可能有不一样,所以我们不能根据用户点击哪一个按钮来弹出Toast消息。我们实现了一个checkAnswer()函数来做这件事情,该函数的代码如下,它会比较用户的答案和问题的正确答案,如果相同,则提示Correct,如果不相同则提示Incorrect。

private boolean checkAnswer(boolean userPressTrue){
        boolean answerIsTrue = mQuestionBank[mCurrentIndex].getAnswerTrue();
        int messageResId = 0;
        messageResId = (userPressTrue == answerIsTrue) ? R.string.correct_toast:R.string.incorrect_toast;
        Toast.makeText(MainActivity.this, messageResId, Toast.LENGTH_SHORT).show();
        return userPressTrue == answerIsTrue;
    }
           

现在我们可以为True和False按钮设置监听器了~到这里我们的任务就已经完成啦。(其实这里还有一个BUG,你可以尝试旋转屏幕,看看能否发现。)

mTrueButton = (Button) findViewById(R.id.true_button);
    mFalseButton = (Button) findViewById(R.id.false_button);

    mTrueButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            checkAnswer(true);
        }
    });

    mFalseButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            checkAnswer(false);
        }
    });
           

文章内容出自《Android变成权威指南》(第3版)

内容经过删改处理,仅作为学习用途

如有侵权,联系删除

2021/06/06