《第一行代码 第二版》Android开发学习笔记 java

Posted -avocado-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《第一行代码 第二版》Android开发学习笔记 java相关的知识,希望对你有一定的参考价值。

持续更新中…

zcy 2021/8/14

一、开始启程

1. 认识android

Android 大致可以分为4层架构:Linux内核层、系统运行库层、应用框架层和应用层

Android系统四大组件分别是Activity、Service、BroadcastReceiver和 ContentProvider

Android系统还自带了这种轻量级、运算速度极快的嵌入式关系型数据库, SQLite数据库,它不仅支持标准 的SQL语法,还可以通过Android封装好的API进行操作

2. 创建项目

Package name:表示项目的包名,Android系统就是通过包名来区分不同应用程序的,因此包名一定要具有唯一性

Language:这里默认选择了Kotlin。在过去,Android应用程序只能使用 Java来进行开发,本书的前两个版本也都是用Java语言讲解的。然而在2017年,Google引入 了一款新的开发语言——Kotlin,并在2019年正式向广大开发者公布了Kotlin First的消息

Minimum API level:设置项目的最低兼容版本,Android 5.0以 上的系统已经占据了超过85%的Android市场份额,因此这里我们将Minimum SDK指定成API 21就可以了

3. 分析第一个Android程序结构

3.1 Project模式的项目结构

.gradle和.idea:放置的都是Android Studio自动生成的一些文件

app:项目中的代码、资源等内容都是放置在这个目录下的,我们后面的开发工作也基本是在这 个目录下进行的

gradle:这个目录下包含了gradle wrapper的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是会自动根据本地的缓存情况决定是否需要联网下载gradle。Android Studio默认就是启用gradle wrapper方式的,如果需要更改成离线模式,可以点击Android Studio导航栏→ File → Settings → Build, Execution, Deployment → Gradle,进行配置更改

gitignore:用来将指定的目录或文件排除在版本控制之外的

build.gradle:项目全局的gradle构建脚本

gradle.properties:全局的gradle配置文件

gradlew和gradlew.bat:用来在命令行界面中执行gradle命令的,其中gradlew是在Linux或Mac系统,gradlew.bat是在Windows系统

local.properties:指定本机中的Android SDK路径

settings.gradle:指定项目中所有引入的模块

3.2 app目录下的结构

build:包含了一些在编译时自动生成的文件

libs:放置第三方jar包

androidTest:编写测试用例

java:放置我们所有Java代码的地方(Kotlin代码也放在这里)

res:项目中使用到的所有图片、布局、字符串等资源

AndroidManifest.xml:是整个Android项目的配置文件,你在程序中定义的所有四大组件都需要在这个文件里注册

test:编写Unit Test测试用例

接下来分析一下HelloWorld项目究竟是如何运行起来的:

首先打开 AndroidManifest.xml,这段代码表示对MainActivity进行注册,intent-filter里的两行代码表示MainActivity是这个项目的主Activity

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

打开 MainActivity, 首先MainActivity是继承自AppCompatActivity (AndroidX中提供的一种向下兼容的Activity),MainActivity中有一个onCreate()方法,里面调用了setContentView()方法,给当前的Activity引入了一个activity_main布局

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

打开 activity_main.xml,在 TextView中看到了“Hello World”的字样,因为Android程序的设计讲究逻辑和视图分离,不推荐在Activity中直接编写界面,而是在布局文件中编写界面

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.3 res目录下的结构

以“drawable”开头的目录:用来放图片的

以“mipmap”开头的目录:用来放应用图标的

以“values”开头的目录:放字符串、样式、颜色等配置的

以“layout”开头的目录:放布局文件

我们应该如何使用这些资源呢,以字符串为例,这里定义了一个应用程序名的字符串,我们有以下两种方式来引用它

  1. 在代码中通过R.string.app_name可以获得该字符串的引用。
  2. 在XML中通过@string/app_name可以获得该字符串的引用

4. 一些error解决方法

ERROR: SSL peer shut down incorrectly错误解决(Android Studio)

错误信息:ERROR: SSL peer shut down incorrectly

错误原因:是studio工具不支持https请求

方法一:右上角 SDK Manager 进入到窗口里面 → 选择 SDK Update Sites 这个选项 → 勾选下方的 Force https// sources to be fetched using http// 选项 → 重启Android Studio → 点击右上大象图标重新下载

方法二:

将 gradle-wrapper.properties 文件里面的 distributionUrl=https 中的 https 改成 http,然后重新点击上方的按钮大象重新下载gradle文件

二、探究Activity

Activity是一种可以包含用户界面的组件,主要用于和用户进行交互

1. 活动的基本用法

1.1 创建活动

Generate Layout File:会自动创建一个对应的布局文件

Launcher Activity:会自动将这个Activity设置为当前项目的主Activity

项目中的任何Activity都应该重写onCreate()方法,调用setContentView()方法来给当前的Activity加载一个布局,我们一般会传入一个布局文件的id

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }
}

1.2 Toast

在程序中可以使用它将一些短小的信息通知给用户,一段时间后自动消失

在activity_main中添加一个button,并赋予id button_1

<Button
    android:id="@+id/button_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="click"/>

在 onCreate() 方法中添加如下代码:

通过 findViewById() 获取在布局文件中定义的button_1,该方法返回的是一个继承自View的泛型对象,因此需要向下转型成Button对象

通过调用 setOnClickListener() 方法为按钮注册一个监听器,点击按钮时就会执行监听器中的 onClick() 方法,Toast的功能在 onClick() 方法中编写了

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button btn = (Button) findViewById(R.id.button_1);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(MainActivity.this,"you clicked btn1",
                           Toast.LENGTH_SHORT).show();
        }
    });
}

Toast的用法:

通过静态方法 makeText() 创建出一个Toast对象,然后调用 show() 将Toast显示出来

第一个参数是Context,就是Toast要求的上下文,Activity本身就是一个Context对象,因此这里直接传入this。第二个参数是Toast显示的文本内容。第三个参数是Toast显示的时长,有两个内置常量可以选择:Toast.LENGTH_SHORT 和 Toast.LENGTH_LONG

1.3 Menu

在res目录下新建一个menu文件夹,在menu文件夹下新建一个菜单文件 main.xml,代码如下,我们用 创建了两个菜单项

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/add_item"
        android:title="add"></item>
    <item
        android:id="@+id/remove_item"
        android:title="remove"></item>
</menu>

接着回到 MainActivity 来重写 onCreateOptionsMenu() 方法,getMenuInflater() 方法能够得到一 个MenuInflater 对象,再调用它的 inflate() 方法,就可以给当前Activity创建菜单了

inflate() 两个参数:第一个参数指定我们通过哪一个资源文件来创建菜单,第二个参数指定菜单项将添加到哪一个Menu对象当中,这里直接使用 onCreateOptionsMenu() 方法中传入的menu参数

返回 true,表示允许创建的菜单显示出来

@Override
public boolean onCreateOptionsMenu(Menu menu){
    getMenuInflater().inflate(R.menu.main,menu);
    return true;
}

我们继续给菜单定义响应事件

在 MainActivity中重写 onOptionsItemSelected() 方法,调用item.itemId来判断点击的是哪一个菜单项

@Override
public boolean onOptionsItemSelected(MenuItem item){
    switch (item.getItemId()) {
        case R.id.add_item:
            Toast.makeText(this, "You clicked Add",
                           Toast.LENGTH_SHORT).show();
            break;
        case R.id.remove_item:
            Toast.makeText(this, "You clicked Remove",
                           Toast.LENGTH_SHORT).show();
            break;
        default:
    }
    return true;
}

效果图如下

1.4 销毁一个活动

按一下Back键,或者z在代码里使用 finish() 方法

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        finishi();
    }
});

2. Intent 在活动之间穿梭

由一个Activity跳转到另一个Activity

2.1 Intent简介

Intent是Android程序中各组件之间进行交互的一种方式,不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据,一般可用于启动Activity、启动Service以及发送广播等场景

2.2 显式Intent

新建 SecondActivity.java 和 acitvity_second.xml,在布局文件里加上按钮id button_2

通过 Intent() 构造函数就可以构建出 Intent 对象的“意图”,第一个参数传入 MainActivity.this 作为上下文,第二个参数传入 SecondActivity.class 作为目标 Activity

Activity类中提供了一个 startActivity() 方法,接收一个Intent参数,专门用于启动Activity

Button btn = (Button) findViewById(R.id.button_1);
btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        startActivity(intent);
    }
});

2.3 隐式Intent

指定了一系列的action和category等信息,交由系统去分析这个Intent,找出合适的Activity去启动

打开 AndroidManifest.xml,通过在标签 下配置 的内容,可以指定 SecondActivity 能够响应的 action 和 category

<activity android:name=".SecondActivity">
    <intent-filter>
        <action android:name="com.example.chapter2.ACTION_START" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

修改MainActivity中按钮的点击事件,只有 和 中的内容同时匹配Intent构造函数中指定的action和category时,这个Activity才能响应该Intent

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.example.chapter2.ACTION_START");
        startActivity(intent);
    }
});

这里没有指定 category 是因为 DEFAULT 是一种默认的 category,调用函数时会自动添加进去

每个Intent中只能指定一个action,但能指定多个 category,在 MainActivity.java 里面调用 intent.addCategory(),再在SecondActivity的 加上一个新的 ,否则会报错

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.addCategory("com.example.chapter2.MY_CATEGORY");
startActivity(intent);
<action android:name="com.example.chapter2.ACTION_START" />
<category android:name="com.example.chapter2.MY_CATEGORY"/>
<category android:name="android.intent.category.DEFAULT" />

2.4 更多隐式Intent的用法

不仅可以启动自己程序内的Activity,还可以启动其他程序的Activity

  • Intent.ACTION_VIEW

    首先指定了Intent的action是Intent.ACTION_VIEW,然后通过==Uri.parse()==方法将一个网址字符串解析成一个Uri对象,再调用Intent的 setData() 方法将这个Uri对象传递进去

    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse("https://www.baidu.com"));
            startActivity(intent);
        }
    });
    
  • Intent.ACTION_DIAL

    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(Intent.ACTION_DIAL);
            intent.setData(Uri.parse("tel:10086"));
            startActivity(intent);
        }
    });
    

    点击按钮效果如图

2.5 向下一个activity传递数据

Intent中提供了一系列 putExtra() 方法的重载,可以把我们想要传递的数据暂存在Intent中,在启动另一个Activity后,只需要把这些数据从Intent中取出就可以了

例如要把MainActivity中的字符串传递到SecondActivity中:

使用显式Intent的方式来启动SecondActivity,并通过 putExtra() 方法传递了一个字符串,第一个参数是键,用于之后从 Intent 中取值,第二个参数才是真正要传递的数据

btn1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        String data = "this is from MainActivity";
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        intent.putExtra("extra_data",data);
        startActivity(intent);
    }
});

在 SecondActivity中,调用父类的 getIntent() 方法,获取用于启动 SecondActivity的Intent,然后调用 getStringExtra() 方法传入相应的键值

传递字符串:getStringExtra()

传递整型数据:getIntExtra()

传递布尔值:getBooleanExtra()

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        
        Intent intent = getIntent();
        String data = intent.getStringExtra("extra_data");
        Log.d("SecondActivity",data);
    }
}

2.6 返回数据给上一个活动

Activity有一个用于启动Activity的 startActivityForResult() 方法,它期望在Activity销毁的时候能够返回一个结果给上一个Activity

① 修改MainActivity代码

startActivityForResult() 来启动活动,第一个参数还是Intent,第二个参数是请求码,用于在之后的回调中判断数据的来源

btn1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        startActivityForResult(intent,1);
    }
});

在 SecondActivity被销毁之后会回调上一个Activity的 onActivityResult() 方法,第一个参数是在启动 Activity 时传入的请求码,第二个参数是在返回数据时传入的处理结果;第三个参数即携带着返回数据的Intent

@Override
protected void onActivityResult(int rqCode, int resCode, Intent data) {
    switch (rqCode) {
        case 1:
            if(resCode == RESULT_OK) {
                String returnData = data.getStringExtra("data_return");
                Log.d("MainActivity",returnData);
            }
    }
}

② 修改SecondActivity代码:

构建一个Intent对象,仅用来传递数据,不指定任何意图

后调用了==setResult()==方法,专门用于向上一个Activity返回数据。第一个参数用于向上一个Activity返回处理结果(RESULT_OK 或 RESULT_CANCELED),第二个参数把带有数据的Intent传递回去

Button btn2 = (Button) findViewById(R.id.button_2);
btn2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent();
        intent.putExtra("data_return","this is from SecondActivity");
        setResult(RESULT_OK,intent);
        finish();
    }
});

如果用户在SecondActivity中并不是通过点击按钮,而是通过按下Back键回到 FirstActivity,通过在SecondActivity中重写 onBackPressed() 方法来解决

@Override
public void onBackPressed(){
    Intent intent = new Intent();
    intent.putExtra("data_return","this is from SecondActivity");
    setResult(RESULT_OK,intent);
    finish();
}

3. 活动的生命周期

3.1 返回栈

Android中的Activity是可以层叠的,新活动覆盖在旧活动上

Android是使用任务(task)来管理Activity的,一个任务就是一组存放在栈里的Activity 的集合,这个栈称作返回栈(back stack)

3.2 Activity状态

  • 运行:位于返回栈的顶部,系统最不愿意回收
  • 暂停:不在栈顶,但仍可见(例如对话框的背后),暂停状态仍是完全存活的,系统不愿意回收
  • 停止:不在栈顶,完全不可见,系统仍保为这个活动保存状态和成员变量,但在内存紧张会被回收
  • 销毁:从返回栈移除,系统倾向回收,节约内存

3.3 Activity生存期

完整生存期 ,onCreate —— onDestroy,初始化 —— 释放内存

可见生存期,onStart —— onStop,不可见 —— 可见时调用

前台展示生存期,onResume —— onPause

  • onCreate,初始化,比如加载布局、绑定事件

  • onStart,不可见到可见时调用

  • onResume,和用户交互前进行调用,此时一定位于栈顶,处于运行状态

  • onPause,在系统切换到另一个ACT时调用

  • onStop,完全不可见时调用

  • onDestroy,在销毁前被调用

  • onRestart,从停止变为运行时调用,也就是Activity 被重新启动了

3.4 活动被回收了怎么办

Activity被回收前,系统会调用一个方法==onSaveInstanceState()==来保存用户的数据,在 onPause 和onStop 之间,解决活动被回收临时数据得不到保留的问题

在 MainActivity 中写入函数

@Override
protected void onSaveInstanceState(Bundle outState){
    super.onSaveInstanceState(outState);
    String temp = "something you just typed";
    outState.putString("data_kay",temp);
}

我们一直使用的onCreate()方法也有一个Bundle参数,如果在Activity被系统回收之前,通过onSaveInstanceState()方法保存数据,这个参数就会带有之前保存的全部数据,只需要将数据取出即可

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if(savedInstanceState != null){
        String temp = savedInstanceState.getString("data_key");
        log.d(TAG,temp);
    }
}

4. 活动的启动模式

在实际项目中我们应该根据特定的需求为每个Activity指定恰当的启动模式,可以在AndroidManifest.xml中通过给标签指定 android:launchMode 属性来选择启动模式

  • standard,默认的启动模式,每次创建都会产生一个新的,放在栈顶,不在乎以前是否创建过(单体可循环,默认情况)

  • singleTop,每次创建时,如果发现栈顶是本身,就不再创建,否则就创建(交替可循环)

    android:launchMode="singleTop"
    
  • singleTask,每次启动,系统首先会在返回栈中检查是否存在该Activity,如果已经存在则直接使用该实例, 并把在这个Activity之上的所有其他Activity统统出栈

  • singleInstance,真正单态模式,自身拥有一个返回栈

三、UI开发的点滴

1. 常用控件的使用方法

1.1 TextView

<TextView
          android:id="@+id/textView"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:gravity以上是关于《第一行代码 第二版》Android开发学习笔记 java的主要内容,如果未能解决你的问题,请参考以下文章

第一行代码 Android 第二版到货啦

Android第一行代码第二版过时操作解决方法

Android LBS 百度地图(参考: 《第一行代码》第二版(郭霖著)11.3.4 显示看得懂的定位信息:准确文字地址)

求《android应用案例开发大全第二版》PDF和源码

第一章 开始 Lua程序设计第二版笔记

android开发学习——day1