Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "androidx.navigation.safeargs.kotlin"

android {
compileSdkVersion 29
Expand Down Expand Up @@ -42,6 +43,10 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"

// navigation component
implementation "androidx.navigation:navigation-fragment-ktx:2.3.0"
implementation "androidx.navigation:navigation-ui-ktx:2.3.0"

testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<activity
android:name=".MainActivity"
android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package tw.andyang.kotlinandroidworkshop

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_add_todo.*

class AddTodoFragment : Fragment() {

private val args by navArgs<AddTodoFragmentArgs>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_add_todo, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// auto open soft keyboard
editTodo.requestFocus()
val inputMethodManager =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.toggleSoftInput(
InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY
)

// setup argument
editTodo.setText(args.memo)
editTodo.setSelection(args.memo.length)

val todoViewModel = ViewModelProvider(requireActivity()).get(TodoViewModel::class.java)

buttonAdd.setOnClickListener {
if (editTodo.text.isNullOrEmpty()) {
editTodo.error = "請輸入你的代辦事項"
} else {
// clear error
editTodo.error = null
// post data to view model
todoViewModel.onNewTodo.postValue(editTodo.text.toString())
// hide soft keyboard when item added
view.clearFocus()
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
// back to list page
findNavController().popBackStack()
}
}
}
}
19 changes: 0 additions & 19 deletions app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,11 @@ package tw.andyang.kotlinandroidworkshop

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.*
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

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

val adapter = TodoAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))

val todoViewModel = ViewModelProvider(this).get<TodoViewModel>()

todoViewModel.todoLiveData.observe(this, Observer { todos: List<Todo> ->
adapter.submitList(todos)
})

buttonAdd.setOnClickListener {
todoViewModel.onNewTodo.postValue(Unit)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package tw.andyang.kotlinandroidworkshop

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.fragment_todo_list.*

class TodoListFragment : Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_todo_list, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val adapter = TodoAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
recyclerView.addItemDecoration(
DividerItemDecoration(
requireContext(),
LinearLayoutManager.VERTICAL
)
)

val todoViewModel = ViewModelProvider(requireActivity()).get(TodoViewModel::class.java)

todoViewModel.todoLiveData.observe(viewLifecycleOwner, Observer { todos: List<Todo> ->
adapter.submitList(todos)
})

buttonAdd.setOnClickListener {
findNavController().navigate(TodoListFragmentDirections.actionMainFragmentToAddTodoFragment())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ import androidx.lifecycle.ViewModel

class TodoViewModel : ViewModel() {

val onNewTodo = MutableLiveData<Unit>()
val onNewTodo = MutableLiveData<String>()

val todoLiveData: LiveData<List<Todo>> = MediatorLiveData<List<Todo>>().apply {
addSource(onNewTodo) {
val todo = Todo.Item("note $count", false)
addSource(onNewTodo) { text ->
val todo = Todo.Item(text, false)
this.value = this.value!! + listOf(todo)
count++
}
value = mutableListOf(Todo.Title("This is a title"))
}

private var count = 0
}
21 changes: 6 additions & 15 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
android:clipToPadding="false"
tools:context=".MainActivity">
android:layout_height="match_parent">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
<androidx.fragment.app.FragmentContainerView
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="60dp"
tools:listitem="@layout/item_todo" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/buttonAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_todo"
app:layout_constraintBottom_toBottomOf="parent" />
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>
20 changes: 20 additions & 0 deletions app/src/main/res/layout/fragment_add_todo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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:padding="8dp">

<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/editTodo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/add_todo_hint" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/buttonAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_todo" />

</LinearLayout>
24 changes: 24 additions & 0 deletions app/src/main/res/layout/fragment_todo_list.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
android:clipToPadding="false"
tools:context=".TodoListFragment">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="60dp"
tools:listitem="@layout/item_todo" />

<androidx.appcompat.widget.AppCompatButton
android:id="@+id/buttonAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_todo"
app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
33 changes: 33 additions & 0 deletions app/src/main/res/navigation/nav_graph.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph.xml"
app:startDestination="@id/mainFragment">

<fragment
android:id="@+id/mainFragment"
android:name="tw.andyang.kotlinandroidworkshop.TodoListFragment"
tools:layout="@layout/fragment_todo_list">

<action
android:id="@+id/actionMainFragmentToAddTodoFragment"
app:destination="@id/addTodoFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_close_exit" />

</fragment>

<fragment
android:id="@+id/addTodoFragment"
android:name="tw.andyang.kotlinandroidworkshop.AddTodoFragment"
tools:layout="@layout/fragment_add_todo">

<argument
android:name="memo"
android:defaultValue="是在哈囉?"
app:argType="string" />

</fragment>

</navigation>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<string name="app_name">KotlinAndroidWorkshop</string>
<string name="todo_list_title">備忘錄</string>
<string name="add_todo">新增</string>
<string name="add_todo_hint">你的待辦事項</string>
</resources>
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Expand Down