第一行代码 Android 第三版读后感

Posted lin513

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一行代码 Android 第三版读后感相关的知识,希望对你有一定的参考价值。

《第一行代码Android 第三版》是一本非常好的Android开发入门书籍。本书结合作者的丰富经验和实际案例,通过一步一步的介绍,详细地讲解了Android开发的各个方面,包括Android开发环境的搭建、Android应用程序的结构、界面设计、数据存储、网络通信、多媒体处理等方面。

首先,本书从零开始讲解了Android的开发环境搭建,让读者了解Android的开发工具和环境,并为后面的学习打下了基础。接下来,本书详细讲解了Android应用程序的结构、界面设计以及常用的控件使用方法,通过实例演示和详细说明,让读者能够快速上手开发。

本书的一个亮点是对数据存储的讲解,包括SQLite数据库的使用以及SharedPreferences、文件存储等方法的介绍。这些都是Android开发中非常重要的内容,而本书的讲解非常详细,让读者能够深入理解。

此外,本书还讲解了网络通信和多媒体处理等方面的内容,这些都是Android应用开发中非常重要的知识点,而本书的讲解也非常清晰明了,易于理解和上手。

总之,我认为《第一行代码Android 第三版》是一本非常实用的Android开发入门书籍,对初学者来说是一本非常好的入门读物,也适合有一定开发经验的开发者作为参考书籍。

《第一行代码》 第三版 - 第四章(笔记)

软件也要拼脸蛋,UI开发的点点滴滴

1 常用控件

之后有空我将会一个一个的写出来,现在的话,我觉得菜鸟教程的很详细,可以阅读菜鸟教程来进行学习。
2.1 View与ViewGroup的概念
菜鸟教程的第二章都是View和ViewGroup的控件讲解。
TextView
Button
EditText
AlertDialog
ProgressBar
ImageView

2 布局

布局有7种布局:
相对布局
线性布局
约束布局
网格布局
帧布局
表格布局
绝对布局

2.1常用的布局:相对布局、线性布局和帧布局,另加一个约束布局

推荐优先使用约束布局,它是一个非常强大的布局,他可以实现相对布局和线性布局的所实现的页面布局

3.自定义控件

上面的所有基础控件都是 View的直接或间接之类,而所有布局都是ViewGroup的直接或间接之类,同时ViewGroup是一种特殊的View,可以包含多个View或ViewGroup

3.1引入布局

引入布局是自定义控件中最简单的一种,就是把其他的XML布局引入到主布局,但被引入的布局监听等用法和主布局的用法一样。主要的作用是复用布局,减少一样的布局代码
先写一个要引入的自定义布局XML: item_title.xml
这里使用的布局是约束布局,不了解的小伙伴可以在这了解↓
约束布局ConstraintLayout看这一篇就够了

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#669999"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <ImageButton
        android:id="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="12dp"
        android:src="@drawable/ic_back"
        android:background="@drawable/ic_kong"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    <TextView
        android:id="@+id/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:padding="12dp"
        android:textSize="16sp"
        android:textColor="#333333"
        android:textStyle="bold"
        android:text="这个是标题了啦"
        android:gravity="center"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@id/back"
        app:layout_constraintRight_toLeftOf="@id/setting"/>
    <ImageButton
        android:id="@+id/setting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="12dp"
        android:src="@drawable/ic_setting"
        android:background="@drawable/ic_kong"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

写好布局文件之后,我们在activity_main中通过引入布局

<?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"
    tools:context=".MainActivity">
    <include layout="@layout/item_title" android:id="@+id/title"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>


Activity对其监听

 class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val img : ImageButton = findViewById(R.id.back)
        img.setOnClickListener
            Toast.makeText(this, "点击了返回", Toast.LENGTH_SHORT).show()
        
        val title : TextView = findViewById(R.id.title)
        title.setOnClickListener 
            Toast.makeText(this, "点击了标题", Toast.LENGTH_SHORT).show()
        
        val setting : ImageButton = findViewById(R.id.setting)
        setting.setOnClickListener
            Toast.makeText(this, "点击了设置", Toast.LENGTH_SHORT).show()
        
    

3.2创建自定义控件

上面虽然减少了代码重复性的问题,但里面的事件处理逻辑也要一次次的写,所以最好的办法就是制作自定义控件,处理逻辑写在自定义控件里面

引入布局文件后,在init结构体中对需要的标题布局进行动态加载
LayoutInflater 的from()方法可以构建出一个LayoutInflater对象,再通过对象的inflate()方法动态加载一个布局文件,该方法有两个参数,第一个是布局文件的id,第二个是父布局。
然后再实现他们的处理逻辑。那这样以后只要我们将TitleLayout这个控件写在XML上面,我们运行,点击对应的控件,那就会触发里面的监听事件

class TitleLayout(context : Context, attrs : AttributeSet) : LinearLayout(context, attrs) 
    init 
        LayoutInflater.from(context).inflate(R.layout.item_title, this)
        val img: ImageButton = findViewById(R.id.back)
        img.setOnClickListener 
            Toast.makeText(context, "点击了返回", Toast.LENGTH_SHORT).show()
        
        val title: TextView = findViewById(R.id.title)
        title.setOnClickListener 
            Toast.makeText(context, "点击了标题", Toast.LENGTH_SHORT).show()
        
        val setting: ImageButton = findViewById(R.id.setting)
        setting.setOnClickListener 
            Toast.makeText(context, "点击了设置", Toast.LENGTH_SHORT).show()
        
    

activity_main.xml的修改

<?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"
    tools:context=".MainActivity">

    <com.example.mytitle.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

自定义控件是默认携带包名的。
一个小问题
在activity_main.xml中修改控件之后,就有人说了,为啥预览看不到呀,是不是我写错了,你没写错,你运行一次,之后预览就出现了,这个问题我暂时不知道是什么,懂的小伙伴可以在评论区说明一下或私信本人。

3.3强制转换

as是Kotlin中强制转换使用的关键字

context as Activity

4.ListView

ListView个人认为用的地方不多,更多是推荐使用RecyclerView,因为它能实现的布局,RecyclerView也能实现,RecyclerView能实现的布局,它不一定能实现,而且RecyclerView更加灵活,用法更多,更好使用。
所以此处讲解:略

5.强大的RecyclerView

RecyclerView单独写了一篇笔记,详情请看:
Android - 强大的RecyclerView

6.制作9-Path图片(.9图片)

所谓的9-Patch图片,就是将图片用两条"横线"与两条"竖线"分割成9个部分,上下切割的两条竖线包裹的部分,就是左右拉伸的内容,左右切割的两条线包裹的部分,就是上下拉伸的内容,其余部分是不会发生变化,这就是.9图片的原理,最后我们把这张图片作为背景图,那么他就会自适应你的控件大小,主动取拉伸中间部分

创建一张.9图片

找到一张图片,放入drawable中,然后右键图片,选择Create 9-Patch file,命名.9图片的名字,我们需要修改与原图不一样的名字,因为AS不允许有同样名字的文件,不同后缀也不行。

修改.9图片的拉伸位置

看到文件的上下和左右,各有两条线,移动至你只想要拉伸的那一部分即可,他们中间包裹着一个矩形,这个矩形的地方就是他可以拉伸的位置

7 定义常量的关键字const

只有在单例类,companion object 或顶层方法才可以使用const关键字

class Student(val name : String, val age : Int, val grade : Int)
	companion object
		const val TYPE_EXCELLENT = 4 //优秀
		const val TYPE_GOOD = 3 //良好
		const val TYPE_PASS = 2 //及格
		const val TYPE_FAIL = 1 //失败
	

8. 延迟初始化

当类中存在多个全局变量,但为了保证他们能满足Kotlin的空指针检查语法标准,你必须做出许多的非空判断保护,即便你确定他肯定不会为空。
当我们设置的全局变量在onCreate()方法中进行初始化,那么赋值就必须为null,同时类型声明还必须是MsgAdapter? ,即便在onCreate()初始化之后,才会调用onClick(),但onClick()中还是必须要进行判空处理。否则编译无法通过
所以现在有一个解决的办法,就是延迟初始化,延迟初始化使用的是lateinit关键字,告诉Kotlin编译,我会迟点对这些变量进行初始化,那么他们一开始定义就不需要赋值为null了

class MainActivity : AppCompatActivity(), View.OnClickListener 
	//原写法
	private var adapter: MsgAdapter? = null 
	//延迟初始化写法
	private lateinit var adapter1: MsgAdapter 
	override fun onCreate(savedInstanceState: Bundle?)  
		super.onCreate(savedInstanceState) 
		setContentView(R.layout.activity_main) 
		adapter = MsgAdapter(msgList) 
		adapter1 = MsgAdapter(msgList)
	 
	
	override fun onClick(v: View?)  	
		//原代码 	 
		adapter?.notifyItemInserted(msgList.size - 1) 	 
		//延迟后的使用,不需要?. 	 
		adapter1.notifyItemInserted(msgList.size - 1) 
	 
 

当然了,用lateinit关键字也不是没有风险,因为adapter还可能在没初始化之前使用,那程序一定会崩溃
所以也有一种语法用于判断是否进行了初始化
::adapter.isInitialized //判断adapter是否已经初始化了
//加个!表示,若没初始化,那就进行初始化

if(!::adapter.isInitialized) 	 //判断adapter是否已经初始化
	adapter = MsgAdapter(msgList) 
 

9. 密封类

密封类,可以帮你写出更规范和安全的代码
创建一个Result.kt文件

interface Result //无内容的接口
class Success(val msg: String) : Result //实现Result接口的成功类
class Failure(val error: Exception) : Result //实现Result接口的失败类

很简单的类,就是用于表示一个成功的结果和一个失败的结果

平时使用的方法,一定会有一个else,因为他会认定缺少条件分支,编译无法通过,但结果只有 成功或失败,不会走到else。而这直添加一个else 用于抛出一个异常,就是为了满足编译的要求。
但这样做会有一个潜在风险,而这是如果新增一个类去实现Result接口,用于执行 未知结果,那么getResultMsg方法忘记加对应条件的时候,我们会直接走else,从而直接报错

fun getResultMsg(result: Result) = when(result) 	
	is Success -> result.msg 	
	is Failure -> result.error.message 	
	else -> throw IllegalArgumentException()
 

这时候我们可以使用密封类
密封类是用sealed class关键字
我们将上面的Result接口,写成一个密封类
密封类是一个可以继承的类,所以继承的时候,需要在后面加个括号

sealed class Result 
class Success(val msg: String) : Result() 
class Failure(val error: Exception): Result() 

这里我们没有在写else了,这是因为Kotlin会自动检查,该类是不是密封类的子类,如果是,那么会检查是否所有子类所对应的条件都写了处理逻辑,如果没有,那么就会在编译阶段报错,它要求你必须将每一个子类的所对应的条件都全部处理

fun getResultMsg(result: Result) = when(result)  	
	is Success -> result.msg
	is Failure -> "Error is $result.error.message" 
 

//这时候我们创建一个未知类

sealed class Result
class Success(val msg : String): Result()
class Failure(val error: Exception): Result()
class Unknow(val unknow: String)Result()

//这时候如果上面的getResultMsg()方法不修改的话,会报错提示,需添加该条件

fun getResultMsg(result: Result) = when(result)  	
	is Success -> result.msg 	
	is Unknow -> result.unknow
	is Failure -> "Error is $result.error.message" 
 

以上是关于第一行代码 Android 第三版读后感的主要内容,如果未能解决你的问题,请参考以下文章

《第一行代码》 第三版 - 第二章(笔记)

Android 学习之《第一行代码》第三版 笔记Kotlin 继承时的括号到底写不写

《第一行代码》 第三版 - 第三章(笔记)

《第一行代码》 第三版 - 第四章(笔记)

android 中怎么用ca代码实现美图放贴纸

第一行代码和android编程权威指南哪个好