《Android编程权威指南》之Android与MVC篇

升级 GeoQuiz 应用,展示更多的地理知识测试题目。

创建新类

New → Kotlin Class/File (Data Class),kt数据类很方便,比起 java,省去了很多代码。

data class Question(@StringRes val textResId: Int, val answer: Boolean)
  • Kotlin 数据类:
    只保存数据的类,关键字为 data,编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
    • equals() / hashCode()
    • toString() 格式如 "User(name=John, age=42)"
    • componentN() functions 对应于属性,按声明顺序排列
    • copy() 函数
  • 数据类的要求:
    • 主构造函数至少包含一个参数
    • 主构造函数的参数必须标识为val 或者 var
    • 数据类不可以声明为 abstract, open, sealed 或者 inner
    • 在1.1版本之前,数据类只实现接口。1.1版本之后,数据类可以扩展其他类

Android 与 MVC 设计模式

MVC图解
  • M —— 模型对象存储着应用的数据和业务逻辑。比如说一些JavaBean属于这层,不关心用户界面,目的是存储和管理应用数据。
  • V —— 视图对象知道如何在屏幕上绘制自己以及如何响应用户的输入。比如说layout中的xml文件,也可以自定义控件,反正是看得见的对象,就是视图对象。
  • C —— 控制对象含有应用的逻辑单元,是视图与模型对象的联系纽带。通常是Activity、Fragment 或 Service 的一个子类。
MVC数据控制流与用户交互

注意,模型对象与视图对象不直接交互。控制器作为它们之间的联系纽带,接收对象发送的消息,然后向其他对象发送操作指令。

更新视图层

activity_main.xml




    

    

        
string.xml


    GeoQuiz
    Canberra is the capital of Australia.
    True
    False
    Next

    Correct!
    Incorrect!

    The Pacific Ocean is larger than the Atlantic Ocean.
    The Suez Canal connects the Red Sea and the Indian Ocean.
    The source of the Nile River is in Egypt.
    The Amazon River is the longest river in the Americas.
    Lake Baikal is the world\'s oldest and deepest freshwater lake.

更新控制器层

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var trueButton: Button
    private lateinit var falseButton: Button
    private lateinit var nextButton: Button
    private lateinit var questionTextView: TextView

    private val questionBank = listOf(
        Question(R.string.question_australia, true),
        Question(R.string.question_oceans, true),
        Question(R.string.question_mideast, false),
        Question(R.string.question_africa, false),
        Question(R.string.question_americas, true),
        Question(R.string.question_asia, true)
    )
    private var currentIndex = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        trueButton = findViewById(R.id.true_button)
        falseButton = findViewById(R.id.false_button)
        nextButton = findViewById(R.id.next_button)
        questionTextView = findViewById(R.id.question_text_view)

        trueButton.setOnClickListener { checkAnswer(true) }
        falseButton.setOnClickListener { checkAnswer(false) }
        nextButton.setOnClickListener {
            currentIndex = (currentIndex + 1) % questionBank.size
            updateQuestion()
        }
        updateQuestion()
    }

    /**
     * 更新问题
     */
    private fun updateQuestion() {
        val questionTextResId = questionBank[currentIndex].textResId
        questionTextView.setText(questionTextResId)
    }

    private fun checkAnswer(userAnswer: Boolean) {
        val correctAnswer = questionBank[currentIndex].answer
        val messageResId = if (userAnswer == correctAnswer) {
            R.string.correct_toast
        } else {
            R.string.incorrect_toast
        }
        Toast.makeText(this, messageResId, Toast.LENGTH_SHORT).show()
    }
} 

添加图标资源

把图片资源放入 drawable 目录或者 mipmap 目录中,注意,文件名必须是小写字母且不能由任何空格符号。

  • ldpi:适用于低密度 (ldpi) 屏幕 (~ 120dpi) 的资源 | 36x36 (0.75x)
  • mdpi:中等像素密度屏幕(约160dpi)| 48x48(1.0x 基准)
  • hdpi:高像素密度屏幕(约240dpi)| 72x72 (1.5x)
  • xhdpi:超高像素密度屏幕(约320dpi)| 96x96 (2.0x)
  • xxhdpi:超超高像素密度屏幕(约480dpi)| 144x144 (3.0x)
  • xxxhdpi:超超超高像素密度屏幕(约640dpi)| 192x192 (4.0x)
  • nodpi:适用于所有密度的资源。这些是与密度无关的资源。无论当前屏幕的密度是多少,系统都不会缩放以此限定符标记的资源。
  • tvdpi:适用于密度介于 mdpi 和 hdpi 之间的屏幕(约 213dpi)的资源。这不属于“主要”密度组。它主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将视情况对其进行缩放。如果您发现有必要提供 tvdpi 资源,应按一个系数来确定其大小,即 1.33*mdpi。例如,如果某张图片在 mdpi 屏幕上的大小为 100px x 100px,那么它在 tvdpi 屏幕上的大小应该为 133px x 133px。

将应用图标放在 mipmap 目录中!

屏幕像素密度

要在密度不同的屏幕上保留界面的可见尺寸,您必须使用密度无关像素 (dp) 作为度量单位来设计界面。dp 是一个虚拟像素单位,1 dp 约等于中密度屏幕(160dpi;“基准”密度)上的 1 像素。对于其他每个密度,Android 会将此值转换为相应的实际像素数。

在定义文本大小时,您应改用可缩放像素 (sp) 作为单位(但切勿将 sp 用于布局尺寸)。默认情况下,sp 单位与 dp 大小相同,但它会根据用户的首选文本大小来调整大小。

  • 矢量图形:(适配用,可以缩放到任何尺寸而不会出现缩放失真,通常最适合图标等插图,而不太适合照片)

Android 仅仅支持将 SVG 文件转换为 Android 的矢量图格式。

    1. 在 Project 窗口中,右键点击 res 目录
    1. New > Vector Asset,选择 Local file (SVG, PSD)
    1. 找到要导入的文件并进行任何调整,点击下一步再到 Finish

针对所有像素密度测试

  • Android 模拟器
  • Firebase 测试实验室

在设备上运行

用真机测试安装应用,需要连接上真机噢。如果在Mac系统上开发,系统应该会立即识别出所有设备。如果是Windows系统,则可能要安装adb(Android Debug Bridger)驱动。

真机要打开USB调试模式:

  • Android 4.2或之后版本的设备,开发选项默认不可见。设置->关于手机,多点击几次版本号启动它,然后回到设置->更多设置->开发者选项,勾选USB调试 USB安装
  • Android 4.0或4.1版本,设置->开发,找到勾选USB调试.
  • Android 4.0版本以前的设备,设置->应用项目->开发,找到勾选USB调试。

当然,也可以用AS创建一个模拟器,去运行应用程序。

挑战练习:为Textview添加监听器

(单击应用的TextView文字区域,也可以跳转到下一道题)

questionTextView.setOnClickListener {
            currentIndex = (currentIndex + 1) % questionBank.size
            updateQuestion()
        }

挑战练习:添加后退按钮

在 XML 中加个Button,用 LinearLayout 将 NEXT 和 PRE 按钮包裹起来,Acitvity 中拿到 PRE 按钮,再加个点击事件。

preButton.setOnClickListener {
            currentIndex = (currentIndex + questionBank.size - 1) % questionBank.size
            updateQuestion()
        }
1

挑战练习:从按钮到图标按钮

将普通的 Button 替换成 ImageButton 即可,图片资源引用的话,用 src 。这里可为 ImageButton 添加android:contentDescription 属性,这样子,在用户点击图形按钮时,设备便会读出属性值的内容。

2
3

Demo 源码地址:https://github.com/visiongem/AndroidGuideApp


附微信公众号:妮K妮K妮(求关注~)

你可能感兴趣的:(《Android编程权威指南》之Android与MVC篇)