Kotlin学习之旅(D12)-第一个App之功能实现

版权声明:本文作者为BlueLzy,原出处为CSDN博客 - http://blog.csdn.net/blue_zy,转载请注明出处。 https://blog.csdn.net/blue_zy/article/details/83242891

Kotlin学习之旅第十二天

今天主要是Koltin编写代码,进行功能实现

前言

Kotlin学习之旅(D1)-学习计划&基本语法

Kotlin学习之旅(D2)-基本语法

Kotlin学习之旅(D3)-类与继承

Kotlin学习之旅(D4)-函数与Lambda表达式

Kotlin学习之旅(D5)-高级语法

Kotlin学习之旅(D6)-Kotlin Idioms part 1

Kotlin学习之旅(D7)-Kotlin Idioms part 2

Kotlin学习之旅(D8)-From Java to Kotlin

Kotlin学习之旅(D9)-Android Extensions

Kotlin学习之旅(D10)- Unit Test with Kotlin

Kotlin学习之旅(D11)-第一个App之项目介绍

新建项目

开发环境:

  • MacOS Mojave Version 10.14
  • Android Studio 3.2

创建NoteKeeper

  1. 打开Android Studio -> New Project -> 输入项目名称NoteKeeper
  2. 到Add an Activity to Mobile 的时候,选择 Basic Activity

Snip20181021_13.png

  1. 别忘了勾上 include kotlin support
  2. 完成项目创建

管理实体类

我们要记录笔记,那么首先就需要有一个实体类用于记录课程,还有一个实体类用于记录笔记信息,这个时候我们可以创建一个 数据管理类 来统一管理这些实体类

创建NoteKeeperData

在Java文件夹下的com.blue.notekeeper文件夹(你们的应该是各自的项目名称) 右键,new -> kotlin file

Snip20181021_14.png

创建一个名为NoteKeeperData 的Kotlin File

注意:在新建文件的时候,下面有个kind选项,我们选择的是File 而不是Class

Snip20181021_15.png

此时我们的需求是:

  1. 课程有id,有title
  2. 笔记有title,text,还需要课程的信息

所以在NoteKeeperData里面编写代码:

/**
 *   author : BlueLzy
 *   e-mail : [email protected]
 *   date   : 2018/10/20 09:58
 *   desc   :
 */

data class CourseInfo (val courseId: String, val title: String) {
    override fun toString(): String {
        return title
    }
}

data class NoteInfo(var course: CourseInfo? = null, var title: String? = null, var text: String? = null){
    override fun toString(): String {
        return "课程: $course \n课题: $title \n笔记: $text"
    }
}

CourseInfoNoteInfo 都是Data Class,然后重写toString()方法,返回我们需要的信息和指定的格式

知识点:Data Class , 字符串模板,构造函数,默认参数,空安全

数据管理类

有个实体管理类,我们就可以开始加入模拟数据了

首先创建一个Kotlin Class -> DataManager

定义存储数据类型

我们使用HashMap来存储课程(CourseInfo),使用ArrayList来存储笔记(NoteInfo),因此定义两个val

    val courses = HashMap<String, CourseInfo>()   // use courseId to find CourseInfo class
    val notes = ArrayList<NoteInfo>()   // Arraylist for NoteInfo

加入模拟的课程数据

我们通过courses.set() 方法加入数据

这里有好几种方式,还记得我们的CourseInfo类吗

class CourseInfo (val courseId: String, val title: String) 

我们实例化一个CourseInfo对象的时候,需要传入courseIdtitle 两个参数,但是传参也有好几种方式

不指定参数名,指定参数名,指定参数名但是不按顺序 都是没问题的,可以看代码注释

        var course = CourseInfo("android_intent", "使用Intent进行Activity跳转")
        courses.set(course.courseId,course)

        // 指定参数名
        course = CourseInfo(courseId = "android_async", title = "Android中的异步编程")
        courses.set(course.courseId, course)

        // title 作为第一个参数
        course = CourseInfo(title = "Java 基础", courseId = "java_lang")
        courses.set(course.courseId, course)

		// 不指定参数名
        course = CourseInfo("java_core", "Java 核心编程")
        courses.set(course.courseId, course)

加入模拟的笔记数据

  		var course = courses["android_intent"]!!
        var note = NoteInfo(course, "我是Intent笔记",
                "Intent里面可以传值,通过这种方式和其他页面交互")
        notes.add(note)
        note = NoteInfo(course, "Intent 的方式",
                "除了普通的Intent,还有PendingIntents")
        notes.add(note)

        course = courses["android_async"]!!
        note = NoteInfo(course, "Service 的使用",
                "Service 主要是运行在哪个线程?")
        notes.add(note)
        note = NoteInfo(course, "Service 的类型",
                "前台Service和后台Service")
        notes.add(note)

        course = courses["java_lang"]!!
        note = NoteInfo(course, "集合",
                "List,Array,Map等")
        notes.add(note)
        note = NoteInfo(course, "面向对象",
                "是不是这样我们就不怕找不到对象了?")
        notes.add(note)

        course = courses["java_core"]!!
        note = NoteInfo(course, "编译选项",
                "-jar 不兼容 -cp 命令")
        notes.add(note)
        note = NoteInfo(course, "序列化",
                "记得包含 SerialVersionUID ")
        notes.add(note)

DataManager完整代码

package com.blue.notekeeper

/**
 *   author : BlueLzy
 *   e-mail : [email protected]
 *   date   : 2018/10/20 10:00
 *   desc   : Manage the data
 */
object DataManager {
    val courses = HashMap<String, CourseInfo>()   // use courseId to find CourseInfo class
    val notes = ArrayList<NoteInfo>()   // Arraylist for NoteInfo

    // 构造函数,进行私有属性的初始化,通常是代码块
    init {
        initializeCourses()
        initializeNotes()
    }

    // 私有的初始化方法
    private fun initializeCourses() {
        var course = CourseInfo("android_intent", "使用Intent进行Activity跳转")
        courses.set(course.courseId,course)

         // 指定参数名
        course = CourseInfo(courseId = "android_async", title = "Android中的异步编程")
        courses.set(course.courseId, course)

        // title 作为第一个参数
        course = CourseInfo(title = "Java 基础", courseId = "java_lang")
        courses.set(course.courseId, course)

		// 不指定参数名
        course = CourseInfo("java_core", "Java 核心编程")
        courses.set(course.courseId, course)

    }

    // 初始化Notes数据
    private fun initializeNotes(){
        var course = courses["android_intent"]!!
        var note = NoteInfo(course, "我是Intent笔记",
                "Intent里面可以传值,通过这种方式和其他页面交互")
        notes.add(note)
        note = NoteInfo(course, "Intent 的方式",
                "除了普通的Intent,还有PendingIntents")
        notes.add(note)

        course = courses["android_async"]!!
        note = NoteInfo(course, "Service 的使用",
                "Service 主要是运行在哪个线程?")
        notes.add(note)
        note = NoteInfo(course, "Service 的类型",
                "前台Service和后台Service")
        notes.add(note)

        course = courses["java_lang"]!!
        note = NoteInfo(course, "集合",
                "List,Array,Map等")
        notes.add(note)
        note = NoteInfo(course, "面向对象",
                "是不是这样我们就不怕找不到对象了?")
        notes.add(note)

        course = courses["java_core"]!!
        note = NoteInfo(course, "编译选项",
                "-jar 不兼容 -cp 命令")
        notes.add(note)
        note = NoteInfo(course, "序列化",
                "记得包含 SerialVersionUID ")
        notes.add(note)
    }
}

数据展示页-NoteListActivity

在完成了所需要的实体类和模拟数据之后,我们终于可以进入到界面的编写阶段了,首先创建NoteListActivity

我们通过这种方式来创建:

Snip20181021_16.png

然后选择 Empty Activity 就可以了,Android Studio会自动帮我们生成

  • 继承自CompatActivity的NoteListActivity文件
  • activity_note_list.xml文件

OK,让我们进入activity_note_list.xml,把代码改成:

activity_note_list.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NoteListActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_note_list" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@drawable/ic_add_white_24dp" />

</android.support.design.widget.CoordinatorLayout>

然后再创建一个名为: content_note_list的xml文件,代码为:

content_note_list.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/textNoteTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="@string/note_title_hint"
        android:inputType="textMultiLine"
        app:layout_constraintEnd_toEndOf="@+id/spinner"
        app:layout_constraintStart_toStartOf="@+id/spinner"
        app:layout_constraintTop_toBottomOf="@+id/spinner" />

    <EditText
        android:id="@+id/textNoteText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="@string/note_text_hint"
        android:inputType="textMultiLine"
        app:layout_constraintEnd_toEndOf="@+id/spinner"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/spinner"
        app:layout_constraintTop_toBottomOf="@+id/textNoteTitle" />

</android.support.constraint.ConstraintLayout>

然后,编写NoteListActivity的代码

class NoteListActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_note_list)
        setSupportActionBar(toolbar)

        listNotes.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1,
                DataManager.notes)
    }

}

最后,修改我们的AndroidMainfest.xml文件,让NoteListActivity成为启动项

 <activity
            android:name=".MainActivity"
            android:label="@string/edit_note"
            android:theme="@style/AppTheme.NoActionBar"/>
        <activity
            android:name=".NoteListActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

一切就绪,此时运行App,看到的效果应该是这样的:

Snip20181021_17.png

只有一个ListView用于展示模拟数据,FAB和Item都没有点击效果,接下来我们要往里面加入其它功能了。

新建笔记

修改NoteListActivity

NoteListActivity加入FAB和Item的点击事件,跳转到MainActivityonCreate方法加上:

  fab.setOnClickListener { view ->
           val activityIntent = Intent(this, MainActivity::class.java)
            startActivity(activityIntent)
        }

        
        listNotes.setOnItemClickListener{ parent, view, position, id ->
            val activityIntent = Intent(this, MainActivity::class.java)
            activityIntent.putExtra(NOTE_POSITIONI, position)
            startActivity(activityIntent)
        }

创建名为Constants的Kotlin File

NOTE_POSITIONIPOSITION_NOT_SET 是无关界面的常量,因此我们需要创建一个文件来保存,这样可以方便管理,代码的结构也会比较清晰。

加入以下代码:

const val NOTE_POSITIONI = "NOTE_POSITIONI"
const val POSITION_NOT_SET = -1

到目前为止,数据列表页的工作就完成了,接下来就是跳转后的编辑页面的工作

修改MainActivity

我们现在回到MainActivity,把MainActivity当成是新建和编辑笔记的页面。首先修改布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>

然后修改content_main

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/textNoteTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="@string/note_title_hint"
        android:inputType="textMultiLine"
        app:layout_constraintEnd_toEndOf="@+id/spinner"
        app:layout_constraintStart_toStartOf="@+id/spinner"
        app:layout_constraintTop_toBottomOf="@+id/spinner" />

    <EditText
        android:id="@+id/textNoteText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="@string/note_text_hint"
        android:inputType="textMultiLine"
        app:layout_constraintEnd_toEndOf="@+id/spinner"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@+id/spinner"
        app:layout_constraintTop_toBottomOf="@+id/textNoteTitle" />

</android.support.constraint.ConstraintLayout>

由于布局不是我们的重点,所以在这里编写和调整的过程就先忽略,大家只要能看懂大概是什么意思就可以了

OK,回到MainActivity

我们需要处理几个事情:

  1. 判断进来的是新笔记还是旧笔记
  2. 新笔记不用处理
  3. 旧笔记需要把数据放到对应的位置

代码如下:


class MainActivity : AppCompatActivity() {

    private var notePosition = POSITION_NOT_SET  // 定义变量记录当前位置

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


        val coursesAdapter = ArrayAdapter<CourseInfo>(this,
                android.R.layout.simple_spinner_item,
                DataManager.courses.values.toList())
        coursesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

        spinner.adapter = coursesAdapter

        notePosition = savedInstanceState?.getInt(NOTE_POSITIONI, POSITION_NOT_SET) ?:
                intent.getIntExtra(NOTE_POSITIONI, POSITION_NOT_SET)

        // 判断是否点击进来的Item,是的话填充数据
        if (notePosition != POSITION_NOT_SET)
            displayNote()
        else {
            DataManager.notes.add(NoteInfo())
            notePosition = DataManager.notes.lastIndex
        }
    }

    override fun onSaveInstanceState(outState: Bundle?) {
        super.onSaveInstanceState(outState)
        outState?.putInt(NOTE_POSITIONI, notePosition)
    }

    private fun displayNote() {
        // 设置 textView 填充内容
        val note = DataManager.notes[notePosition]
        textNoteTitle.setText(note.title)
        textNoteText.setText(note.text)

        // 设置 spinner
        val coursePosition = DataManager.courses.values.indexOf(note.course)
        spinner.setSelection(coursePosition)
    }

    override fun onPause() {
        super.onPause()
        saveNote()
    }

    private fun saveNote() {
        val note = DataManager.notes[notePosition]
        note.title = textNoteTitle.text.toString()
        note.text = textNoteText.text.toString()
        note.course = spinner.selectedItem as CourseInfo
    }
}

我们除了处理新建/编辑笔记两种情况,还做了一点特殊处理

  • 通过onPause()保存数据
  • 通过onSaveInstanceState()保存状态

OK,这个时候再运行一下App,效果应该是:

  • 进入App通过列表形式展示模拟数据
  • 点击FAB进入一个新的页面,填入数据后返回,在列表页可以看到新增笔记
  • 点击列表中的任意一项,进入编辑页面,笔记数据在对应位置展示出来

总结

其实到这里我们就已经完成了大部分的功能了,明天我们主要的任务就是在ActionBar上加入一个Next按钮,可以查看下一条笔记,这个功能有利于提高用户体验,这种交互也是Android官方所提倡的,通过按钮或者手势来进行交互。

Day 12 - Learn Kotlin Trip, Completed.


猜你喜欢

转载自blog.csdn.net/blue_zy/article/details/83242891
D12