Skip to content

Commit

Permalink
Merge pull request #53 from Andonade/master
Browse files Browse the repository at this point in the history
Android doc complete
  • Loading branch information
abmfy authored Dec 21, 2023
2 parents 78592b9 + 6dbcd15 commit bcf98d0
Show file tree
Hide file tree
Showing 7 changed files with 670 additions and 0 deletions.
290 changes: 290 additions & 0 deletions docs/frontend/android/components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
# Android 的组件

编写 Android 应用程序就像搭积木,组件是构建应用程序的基本单元。对组件来说,最重要的方法是围绕着生命周期和交互进行的。我们下面将对这些概念进行介绍。

## Activity & Fragment

活动(Activity)代表应用程序的一个屏幕或一个交互页面。Android 应用程序通常由多个活动组成,并且可以通过意图(Intent)在活动之间进行切换。通过启动新的活动,可以实现屏幕之间的导航和交互。片段(Fragment)是一种可重复使用的 UI 组件,通常嵌入在活动中,每个 Activity 可以包含多个 Fragment。

Activity 和 Fragment 通过布局文件定义其用户界面的外观和结构。布局文件使用 XML 格式编写,其中包含各种视图组件,如按钮、文本框、图像等。通过调用 `setContentView()` 方法,可以将布局文件与活动关联起来。下面是一个最简单的例子。我们使用 `R.<path>` 来获取资源库的对应文件(如果你不确定写法,不妨打一个 `R.`,然后让强大的 Android Studio 补全),这样你就可以看见你的组件了。

```java
// MyActivity1.java
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MyActivity1 extends Activity {

private TextView textView;
private Button button;

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

textView = findViewById(R.id.textView);
button = findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Button Clicked", Toast.LENGTH_SHORT).show();
}
});
}
}
```

```xml
<!-- activity_1.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, World!"
android:textSize="24sp" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me" />

</LinearLayout>
```

看见组件之后,你需要给组件定义实际的行为。每个 Activity 和 Fragment 都有其生命周期,描述了从创建到销毁的整个过程。在生命周期中,系统会调用特定的生命周期回调方法,以便程序可以在适当的时机执行相关操作。例如,在 `onCreate()` 中进行初始化,`onResume()` 中启动动画,`onPause()` 中保存数据等。Activity 和 Fragment 可以响应用户的触摸事件、按键事件等,并执行相应的操作。开发者可以通过重写事件处理方法,如 `onTouch()``onKeyDown()` 等,来处理这些事件。下面就是一个重载 `onTouch()` 的 Fragment 的例子。

```java
// CustomFragment.java
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class CustomFragment extends Fragment {

private TextView textView;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_custom, container, false);

Button button = view.findViewById(R.id.button);
textView = view.findViewById(R.id.textView);

button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
textView.setText("Pressing");
return true;
case MotionEvent.ACTION_MOVE:
textView.setText("Moving");
return true;
case MotionEvent.ACTION_UP:
textView.setText("");
return true;
default:
return false;
}
}
});
/* or use lambda function:
button.setOnTouchListener((v, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
textView.setText("Pressing");
return true;
case MotionEvent.ACTION_MOVE:
textView.setText("Moving");
return true;
case MotionEvent.ACTION_UP:
textView.setText("");
return true;
default:
return false;
}
});
*/

return view;
}
}
```

```xml
<!-- fragment_custom.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
tools:context=".CustomFragment">

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="24sp" />

</LinearLayout>
```

### Adapter

你可能想到,在上面的例子,组件显示的内容是静态的,在实际的场景中,数据量的大小是不定的,因此我们需要一个可以动态扩展的组件,并将组件与数据绑定。这就是 Adapter 的作用。Adapter 是一个中间层,用于将数据与组件进行绑定。在 Android 中,ListView、GridView、RecyclerView 等组件都是通过 Adapter 来实现数据绑定的。下面是一个简单显示文字列表的 RecyclerView 的 Adapter 的例子。

```java
// MyAapter.java
List<String> textList = new ArrayList<>();

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.text_item, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int pos) {
String text = textList.get(pos);
holder.text.setText(text);
}

public int getItemCount() {
return textList.size();
}
}

class MyViewHolder extends RecyclerView.ViewHolder {
TextView text;

public MyViewHolder(@NonNull View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text);
}
}
```

```xml
<!-- text_item.xml -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textSize="24sp" />
```

在上面的例子中,我们使用了 List 来存储文本并使用 RecyclerView 来显示列表。RecyclerView 是一个高度可定制的组件,可以实现各种各样的列表。它的工作原理是,当列表中的某个元素需要显示时,RecyclerView 会调用 Adapter 的 `onCreateViewHolder()` 方法,创建一个 ViewHolder,然后调用 `onBindViewHolder()` 方法,将数据与 ViewHolder 绑定。ViewHolder 是一个包含了列表元素的视图组件的容器,它可以缓存列表元素的视图组件,以便重复使用。

在数据发生变化时,我们需要调用 Adapter 的 `notifyDataSetChanged()` 方法,通知 RecyclerView 更新列表,你也可以使用 `notifyItemInserted()``notifyItemRemoved()` 等方法,来更新列表的某个元素。

## Intent

假如我们现在有了一堆 Activity 等组件,一个很自然的需求就是让他们之间可以进行通信。最简单的例子是,一个活动调用另一个(我们可以在活动之间传递信息,就像函数一样)。

```java
Intent intent = new Intent(CurrentActivity.this, TargetActivity.class);
/* optional:
Bundle bundle = new Bundle();
bundle.putString("string1", "YOUR_STRING");
intent.putExtras(bundle);
*/
startActivity(intent);

/* get your bundle in the target activity:
Bundle bundle = this.getIntent().getExtras();
String str1 = bundle.getString("string1");
*/
```

## Service

并不是所有组件都是 Activity 和 Fragment,还有很多组件和界面无关,比如播放音乐或下载文件的组件。这就需要用到 Service。它的生命周期与 Activity 等有些差异。这里是一个音乐播放的 Service,在 `onStartCommand()` 方法中,我们通过接收 Intent 中的音乐 URL,使用 MediaPlayer 播放音乐。在服务销毁时,我们则需要释放 MediaPlayer 的资源。

```java
// MusicPlayerService.java
public class MusicPlayerService extends Service {

private MediaPlayer mediaPlayer;

@Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String musicUrl = intent.getStringExtra("music_url");
try {
mediaPlayer.setDataSource(musicUrl);
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
return START_NOT_STICKY; // don't restart when killed by system
}

@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

/*
Intent intent = new Intent(CurrentActivity.this, MusicPlayerService.class);
Bundle bundle = new Bundle();
bundle.putString("music_url", "YOUR_URL");
intent.putExtras(bundle);
startService(intent);
*/
```
6 changes: 6 additions & 0 deletions docs/frontend/android/extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 后续学习

到这里文档就算结束了,但是如果想更加深入的学习 Android 开发,可以参考下面的内容

- [Android 开发者官网](https://developer.android.com/docs?hl=zh-cn):官方网站是最权威的学习资料,里面有很多教程和详尽的 API 文档,还有一些示例代码。除此之外,官网还有一些关于 Android 开发的最新动态,如新的 API 版本、新的开发工具等。
- [Android Weekly](https://androidweekly.net/):每周更新,汇集了互联网上有关 Android 开发的技术文章、开源项目、工具等,截至文档完成时已经更新了接近 600 期。
37 changes: 37 additions & 0 deletions docs/frontend/android/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Android 开发入门

!!! info "说明"

本文基于 2023 年学生科协暑培朱煜章同学主讲的 Android 课程的讲义完成。

Android 是一个开源的、基于 Linux 开发的移动设备操作系统,编写一个 Android 应用可以使用的编程语言有 Java, Kotlin, C++ 等,本文档将以 Java 为例进行讲解。

## 前置知识

- Java 语言基础知识,可以参考 [Java 语言基础](../../languages/java/index.md)

## 开发准备

- Java 语言环境
- 我们选择 Android Studio 作为开发平台,可以在 [Download Android Studio & App Tools - Android Developers (google.cn)](https://developer.android.google.cn/studio/) 下载。Android Studio 是一个基于 IntelliJ IDEA 的 Android 开发工具,为开发人员提供了广泛的功能和工具,用于设计、编写、调试和测试 Android 应用程序。开发中常用的功能有:
- 布局编辑器:Android Studio 提供了一个可视化布局编辑器,用于设计应用程序的用户界面。开发人员可以直观地添加 UI 元素、调整布局和设置属性。
- 调试工具:Android Studio 具有内置的调试工具,开发人员可以在代码中设置断点,跟踪变量的值,并执行逐行调试。
- 虚拟设备管理器:Android Studio 包含一个虚拟设备管理器,用于创建和管理模拟的 Android 设备。这使开发人员能够在没有实际设备的情况下进行应用程序的测试和调试。
- APK 构建工具:Android Studio 可以将应用程序打包为 Android 安装包(APK)。开发人员可以生成签名的 APK 文件,以便在设备上进行部署和分发。
- SDK 管理器:Android Studio 包含一个 SDK(Software Development Kit)管理器,用于下载和管理 Android 平台的各种版本和附加组件。开发人员可以根据目标设备和最低支持版本来选择所需的 SDK 组件。

## 学习方法

单纯通过文档来学习 Android 开发是很痛苦的,因此我们强烈建议面向一些实际的代码,而不是文档中的样例代码,这样可以更容易了解 Android 的设计模式,而且在 IDE 中可以用你的 shift 键直接跳转到你需要浏览的地方,这比看文档看到不明白的地方再去百度/谷歌快太多了。本文末尾也会提供一个项目供大家参考。

## 后续拓展

本文档可以在以下课程帮助到你:

- 程序设计训练 Java 课堂
- 其他存在 Android 开发需求的课程

## 资源链接

- Android 开发者官方文档 <https://developer.android.google.cn/docs>
- GitHub 上的一个 Android 包含教程、一些开源项目和工具库的合集 <https://github.com/aritraroy/UltimateAndroidReference>
Loading

0 comments on commit bcf98d0

Please sign in to comment.