Learn Android Apprentice in 10 days

开始学习使用Android Studio

  • 在iOS的学习告一段落了之后,现在开始学习Android应用的开发

  • 他总共包括六个部分,前四个部分每一部分教你编写一个app,难度从低到高。后面两部分会告诉你怎么向下兼容和发布。跟之前的iOS教程很像。

  1. TimeFighter
  2. Checklist
  3. Conclusion

TimeFighter

  • 这个app会从怎么set up Android Studio开始,我们直接跳过,到最开始写代码的部分

Constraint Layouts

  • 和iOS App很像,手机app必须考虑到对象在手机屏幕上的位置问题,Android有提供很多种layoutdexuanze,其中Constraint是最常用的一种,他可以规定目标到屏幕的相对位置

Constraint Layouts

Constraint Layouts

Activities

  • 在确定了诸如Textview,Button等对象的位置之后,我们需要在代码层面对其进行操作,我们可以在Aciticity中创建这些变量对象,然后通过在Layout中设置的ID找到他们,Activity其实就是iOS中的ViewController。

Activities TextView and Button

Activities find by id

Strings

  • 我们会将一个app中用到的所有文字集中在一个文件中,这个文件叫做Strings.xml,这样以后本地化加其他语言或者更改一个单词,这个单词虽然在app中可能出现了很多次,但是我们只需要在Strings里面改一次就可以了

Strings

Oriendtation changes

  • 在手机屏幕方向改变时,系统会做三件事,1.save properties, 2. destroys current activity, 3. recreates the activity for the new orientation by calling onCreate and resets any properties specified by the developer

  • 所以在改变方向时,我们需要及时保存需要用到的变量,在接下来现实的Activity中显示,保证过程不会丢失。

val and var

  • Basically, val and var both are used to declare a variable. var is like a general variable and can be assigned multiple times and is known as the mutable variable in Kotlin. Whereas val is a constant variable and can not be assigned multiple times and can be Initialized only single time and is known as the immutable variable in Kotlin.

Save properties

pass properties to next screen

App colors and styles

  • Android project中有许多文件夹,其中res包含着app需要用到的所有资源resources,常用的Strings,Animations,Menus,Colors and Styles都在这里, 通过直接更改Colors里面颜色的hex值来改变app中元素的颜色,更方便的管理同一种类型的东西

Colors and styles

Animations

  • Animations也在res文件夹里,她负责调用以及调配动画,图片里的动画效果是使用内置的bounce_interpolator,在2秒钟内把目标元素增大2倍,以50%处为中心,并缩小至原来的大小。

Animations

  • Menu同样他也在res文件夹中,他管理所有跟系统菜单相关的元素,比如我们想在屏幕上方的菜单栏中加入一个button,就需要在这里定义

Menu

  • 我们加了一个Menu,当点击时会冒出一个AlertDialog

AlertDialog

AlertDialog in app

  • 这就是第一个app所讲的全部内容了,基本就是把project中需要用到的功能讲了一遍,没有用到任何复杂的语法,更多的讲的是Android Studio这个IDE的使用。接下来进行第二个app

Checklist

ListMaker

RecyclerView

    1. The RecyclerView asks the Adapter for an item, or a ViewHolder at a given position. 2. The Adapter reaches into a pool of ViewHolders that have been created. 3. Either a a new ViewHolder is returned, or a new one is created. 4. The Adapter then binds this ViewHolder to a data item at the given position. 5. The ViewHolder is returned back to the RecyclerView for display

RecyclerView

  • In general, Adapters give your RecyclerView the data it wants to show. They have a clever way to calculate how many rows of data you want to show, which you’ll cover shortly. ViewHolders are the visual containers for your item. Think of them as cells in the table. This is where you tell your RecyclerView what each item should look like. These are basically little tiny layout items used to display the data at any given position in the list of data. As you scroll through a RecyclerView, instead of creating new ViewHolders, RecyclerView will recycle ViewHolders that have moved offscreen and populate them with new data, ready to be shown at the bottom of the list. This process repeats endlessly as you scroll through your RecyclerView. This recycling of ViewHolder to display list items helps to avoid janking in your app.

  • RecycleView 的本质是循环使用table中的cell,当一个cell网上滑出屏幕时,我们可以让他重新出现在底部,但是显示不同的数据,这就要求我们在写代码时要把table,cell和数据分开来,每个部分各司其职

cell

  • 这个是cell部分,它由两个textview组成。对应上图中的viewHolder

table

  • 这个是table部分,它包含多个cell,并可以循环利用cell。对应上图中的adapter。我们需要在adapter中implement recycleview必须的成分例如包含多少个viewholder,每个viewholder的数据应该从哪里取。。

data input

  • 除了recycleview本身,我们应该把data source设置成动态的,也就是说我们可以输入自己的数据,并加进recycleview中。

  • 下面我们在代码层面解释一下如何构建一个recycleView

recycleView functions

  • 首先我们需要继承RecyclerView Adapter并implement这三个function,RecyclerView还有很多其他function可以override但是这三个是必须的
1
2
3
override fun getItemCount(): Int {
return accounts.size
}
  • getItemCount return一个Int,它表示这个表格中有几行,一般是return数据的size
1
2
3
4
5
6
7
8
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CustomViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.view_holder_custom, parent, false)

return CustomViewHolder(view)
}
  • onCreateViewHolder定义一个table cell的layout,我们返回一个layout文件,所以对于每个不同的recyclerView,我们还需要新建一个Layout file用于储存cell的layout
1
2
3
4
5
6
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.label.text = "Some text"
holder.itemView.setOnClickListener {
clickListener.listItemClicked(position)
}
}
  • onBindViewHolder会把我们的数据放到我们新建的cell layout中,另外注意到我加了一个ClickListener,所以之后我们点击每行的时候就会运行listItemClicked function,然后在那里就可以添加另外的代码,可以跳到另一个View等。
1
2
3
class CustomViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val sampleData = itemView.findViewById(R.id.sampleData) as TextView
}
  • 我们还需要一个class去连接table cell layout file

  • 这样基本上一个RecyclerView就完成了,之后我们只需要将数据从Activity或者Fragment传入RecyclerView Adapter就可以了

data input 2

SharedPreferences

  • SharedPreferences lets you save small collections of key-value pairs that you can retrieve later. If you need a way to quickly save small bits of data in your app, SharedPreferences is one of the first solutions you should consider

  • 这就需要用到sharedpreference,它类似于一个dictionary,里面由key-value pairs组成。可以储存size较小的数据。类似于表格信息

SharedPreferences

EditText

1
2
val listTitleEditText = EditText(this) 
listTitleEditText.inputType = InputType.TYPE_CLASS_TEXT
  • 创建一个input text field,并给它指明一个InputType, 这样Android就会显示合适的keyboard

Intent

Intent communication

  • 当我们需要让一个页面与另一个页面进行交流时,我们需要通过Intent将数据传送过去。
1
2
3
4
5
6
7
8
private fun showListDetail(list: TaskList) {
val listDetailIntent = Intent(this, ListDetailActivity::class.java)
listDetailIntent.putExtra(INTENT_LIST_KEY, list)
startActivity(listDetailIntent)
}

// get from other Activity
list = intent.getParcelableExtra(MainActivity.INTENT_LIST_KEY)
  • 在上面这个function中,this是我们现在所在的Activity,ListDetailActivity是我们将要过去的Activity,TaskList是我们要传送的数据。

  • 我们需要规定一个Key,这样的新的页面中我们就知道用Key来获取相应的数据。

  • 但是还有一个问题,就是自定义的object不能直接通过intent传送,我们需要把他变成Parcelable的object

Parcelable

Implement Parcelable

  • 在定义object的class中,implement parcelable,Android Studio会自动把需要的function写好

Fragment

  • Fragment是android语言中一个非常重要的部分,他必须附属于一个Activity,Fragment的本质是可以让相同的部分用同一个Fragment表示,并在多处使用,以节省代码长度,让App保持整洁一致。

Create Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class ListSelectionFragment : Fragment() {
// 1
private var listener: OnListItemFragmentInteractionListener? = null
interface OnListItemFragmentInteractionListener {
fun onListItemClicked(list: TaskList)
}
// 2
companion object {
fun newInstance(): ListSelectionFragment {
val fragment = ListSelectionFragment()
return fragment
}
}
// 3
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnListItemFragmentInteractionListener) {
listener = context
} else {
throw RuntimeException(context.toString() + " must implement
OnListItemFragmentInteractionListener")
}
}
// 4
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
// 5
override fun onCreateView(inflater: LayoutInflater, container:
ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_list_selection, container,
false)
}
// 6
override fun onDetach() {
super.onDetach()
listener = null
}
}

Fragment lifecycle

  • Fragment由几个重要部分组成,首先,当Fragment第一次被附属于某个Activity时onAttach会被执行。然后onCreate, 在这两个地方你可以initialize一些变量等
1
2
3
4
5
6
7
8
9
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

transactions = arguments?.getParcelableArrayList<WestpacTransaction>("Transactions")!!
refundedAmount = arguments?.getDouble("RefundedAmount")!!
refundAmount = arguments?.getDouble("RefundAmount")!!
updatedBalance = arguments?.getDouble("UpdatedBalance")!!
primaryAccount = paperCutAccountManager.readPrimaryAccount()
}
  • 之后onCreateView,这个地方会把数据等object显示在View中,所以需要在这里把变量和UI object绑定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val activity = activity as AppCompatActivity?
if (activity != null) {
activity.supportActionBar!!.show()
activity.supportActionBar?.setDisplayHomeAsUpEnabled(false)
activity.supportActionBar?.title = "Refund Complete"
activity.nav_view.isVisible = false
}

// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_refund_complete, container, false)
view.AccountNameLabel.text = primaryAccount.AccountName
view.AccountBalanceLabel.text = "$" + "%.2f".format(updatedBalance)
view.refundAmount.text = "$" + "%.2f".format(refundedAmount)
view.DoneButton.setOnClickListener { v ->
doneButtonPressed()
}

view.refundCompleteTable.adapter = RefundCompleteTableViewAdapter(transactions, this)
view.refundCompleteTable.layoutManager = LinearLayoutManager(activity)

return view
}
  • 之后是Companion object,当这个Fragment被创建时,如果你需要给它传值,它需要在这里被定义,有点像是Fragment的Constructor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
companion object {
val TAG = RefundCompleteFragment::class.java.simpleName
@JvmStatic
fun newInstance(transactions: ArrayList<WestpacTransaction>, refundedAmount: Double, refundAmount: Double, updatedBalance: Double): RefundCompleteFragment {
val fragment = RefundCompleteFragment()
val args = Bundle()
args.putParcelableArrayList("Transactions", transactions)
args.putDouble("RefundedAmount", refundedAmount)
args.putDouble("RefundAmount", refundAmount)
args.putDouble("UpdatedBalance", updatedBalance)
fragment.arguments = args
return fragment
}
}