1、在util包下看到了幾個文件,是文件不是class,打開一看
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.architecture.blueprints.todoapp.util
/**
* Various extension functions for AppCompatActivity.
*/
import android.app.Activity
import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProviders
import android.support.annotation.IdRes
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentTransaction
import android.support.v7.app.ActionBar
import android.support.v7.app.AppCompatActivity
import com.example.android.architecture.blueprints.todoapp.ViewModelFactory
const val ADD_EDIT_RESULT_OK = Activity.RESULT_FIRST_USER + 1
const val DELETE_RESULT_OK = Activity.RESULT_FIRST_USER + 2
const val EDIT_RESULT_OK = Activity.RESULT_FIRST_USER + 3
/**
* The `fragment` is added to the container view with id `frameId`. The operation is
* performed by the `fragmentManager`.
*/
fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, frameId: Int) {
supportFragmentManager.transact {
replace(frameId, fragment)
}
}
/**
* The `fragment` is added to the container view with tag. The operation is
* performed by the `fragmentManager`.
*/
fun AppCompatActivity.addFragmentToActivity(fragment: Fragment, tag: String) {
supportFragmentManager.transact {
add(fragment, tag)
}
}
fun AppCompatActivity.setupActionBar(@IdRes toolbarId: Int, action: ActionBar.() -> Unit) {
setSupportActionBar(findViewById(toolbarId))
supportActionBar?.run {
action()
}
}
fun <T : ViewModel> AppCompatActivity.obtainViewModel(viewModelClass: Class<T>) =
ViewModelProviders.of(this, ViewModelFactory.getInstance(application)).get(viewModelClass)
/**
* Runs a FragmentTransaction, then calls commit().
*/
private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) {
beginTransaction().apply {
action()
}.commit()
}
里面存放著一些擴展函數(shù),供activity直接方便的調用。還有最后的inline內聯(lián)函數(shù)等都是kotlin的特點。這個文件可以看成是java的一個工具類了。
如果是對view進行操作的工具類 ,那就建立ViewExt.kt,里面是
/**
* Transforms static java function Snackbar.make() to an extension function on View.
*/
fun View.showSnackbar(snackbarText: String, timeLength: Int) {
Snackbar.make(this, snackbarText, timeLength).show()
}
/**
* Triggers a snackbar message when the value contained by snackbarTaskMessageLiveEvent is modified.
*/
fun View.setupSnackbar(lifecycleOwner: LifecycleOwner,
snackbarMessageLiveEvent: SingleLiveEvent<Int>, timeLength: Int) {
snackbarMessageLiveEvent.observe(lifecycleOwner, Observer {
// 這個it是singleLiveEvent.value,存放的是snackbar要展示的文本的resId
// 如果it不是null,才做showSnackbar的操作
it?.let { showSnackbar(context.getString(it), timeLength) }
})
}
/**
* Reloads the data when the pull-to-refresh is triggered.
*
* Creates the `android:onRefresh` for a [SwipeRefreshLayout].
*/
@BindingAdapter("android:onRefresh")
fun ScrollChildSwipeRefreshLayout.setSwipeRefreshLayoutOnRefreshListener(
viewModel: TasksViewModel) {
setOnRefreshListener { viewModel.loadTasks(true) }
}
除了擴展函數(shù),還包含了DataBinding的一些自定義的BindingAdapter比如上面的那個SwipeRefreshLayout的刷新監(jiān)聽。
2、 viewmodle在生成完之后,可以直接通過.apply{}對vm存放的一些LiveData的監(jiān)聽放在里面。
3、fragment的onCreateView里設置setHasOptionsMenu(true)之后,可以對onOptionsItemSelected onCreateOptionsMenu進行重寫來操作toolbar.
4、SwipeRefreshLayout的第一個child如果不是list的話會不能觸發(fā)refresh的監(jiān)聽,可以繼承他來設置監(jiān)聽具體哪個childView
class ScrollChildSwipeRefreshLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {
var scrollUpChild: View? = null
override fun canChildScrollUp() =
scrollUpChild?.canScrollVertically(-1) ?: super.canChildScrollUp()
}
直接綁定上刷新監(jiān)聽需要先弄兩個靜態(tài)方法,標上@BindingAdapter
object TestBindingAdapter {
@BindingAdapter("android:items")
@JvmStatic
fun setItems(rv: RecyclerView, items: List<String>) {
with(rv.adapter as BaseQuickAdapter<String,BaseBindingViewHolder>){
notifyDataSetChanged()
}
}
@BindingAdapter("android:onRefresh")
@JvmStatic fun SwipeRefreshLayout.setOnRefreshListener(
viewModel: Vm) {
setOnRefreshListener { viewModel.refresh() }
}
}
然后在xml里
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl"
android:onRefresh="@{vm}" //就直接可以這么用了
app:refreshing="@{vm.dataLoading}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar" >
<android.support.v7.widget.RecyclerView
android:items="@{vm.datas}"
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.SwipeRefreshLayout>
如果要自動更新刷新狀態(tài)app:refreshing="@{vm.dataLoading}",那么vm.dataLoading是個ObservableBoolean(),且在attrs.xml中加上下面的
<resources>
<declare-styleable name="SwipeRefreshLayout">
<attr name="refreshing" format="boolean" />
</declare-styleable>
</resources>
5、在fragment中對activity里的fab(floatActionButton)進行操作,就直接通過
activity.findViewById<FloatingActionButton>(R.id.fab_add_task).run {
setImageResource(R.drawable.ic_add)
setOnClickListener {
viewDataBinding.viewmodel?.addNewTask()
}
}
去設置圖標和點擊事件,不要寫在activity里再傳進frag。
6、Toolbar用得少?;径际亲远x,頭一次看見除了popupWindow還有個東西叫popupMenu。。用來在ActionBar上顯示一個窗口
7、對于listview和databinding的使用:
首先申明一個單例TaskListBindings
/**
* Contains [BindingAdapter]s for the [Task] list.
*/
object TasksListBindings {
@BindingAdapter("app:items")
@JvmStatic fun setItems(listView: ListView, items: List<Task>) {
with(listView.adapter as TasksAdapter) {
replaceData(items)
}
}
}
在xml里配置上
<ListView
android:id="@+id/tasks_list"
app:items="@{viewmodel.items}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
注意這個items是一個ObservableArrayList
val items: ObservableList<Task> = ObservableArrayList()
當他發(fā)生變化的時候,會去上面的bindingadapter里調用tasksAdapter.replaceData(items)方法進行刷新
8、因為binding與xml是綁定的,所以可以直接FragmentXXXBinding.inflate(),不用寫R.layout.xxx了。
9、事件的流轉通過調用vm的方法,改變vm.liveData,從而通知到注冊了liveData的地方進行處理事件。比如頁面的跳轉,彈個snackbar,彈個dialog,彈個popupWindow,都從liveData通知到Activity,讓activity去做。
10、intent包含的key 以及requestCode,往哪里去,就把這些常量寫在哪里。
11、設置actionbar左上角返回
//這個方法在第一個代碼段中定義的,往上翻↑
setupActionBar(R.id.toolbar) {
setDisplayHomeAsUpEnabled(true) //顯示返回圖標
setDisplayShowHomeEnabled(true) //顯示左上圖標
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
12、接受單個事件或數(shù)據(jù)變動
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
// 判斷是否曾經(jīng)注冊過監(jiān)聽了
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, Observer<T> { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
companion object {
private const val TAG = "SingleLiveEvent"
}
}
在vm中建立一個對象 val newTaskEvent= SingleLiveEvent<Void>()
在某個Activity或Fragment中注冊監(jiān)聽。
viewModel = obtainViewModel().apply {
// Subscribe to "new task" event
newTaskEvent.observe(this@TasksActivity, Observer<Void> {
this@TasksActivity.addNewTask()
})
}
13、
可以看到分包是根據(jù)PBF(package by feature)的,而不是PBL (package by layer),根據(jù)業(yè)務特征來分類。而其中有一個data包是這樣的

分為了本地數(shù)據(jù)和遠程數(shù)據(jù)。
在TasksDataSource中定義了操作數(shù)據(jù)的接口。在LocalDataSource和RemoteDataSource中具體實現(xiàn)。比如本地就用room去數(shù)據(jù)庫拿,remote就去操作retrofit去網(wǎng)絡獲取。
TaskRepository數(shù)據(jù)倉庫 也是繼承自TasksDataSource,且同時持有本地和遠程的DataSource,綜合獲取、處理并對外提供數(shù)據(jù)。
可以看到層次非常清晰。我想起了一張圖,等我去google官網(wǎng)找下。。
找到了,是這個
