轻松上手:<Android Studio笔记应用开发>(三)笔记可再编辑

I. 引言

点击查看该项目GitHub地址

本文将主要实现笔记的点击编辑功能: 用户可以点击笔记列表中的项,进入编辑页面,编辑该笔记的内容。

II. 前情回顾

轻松上手:<Android Studio笔记应用开发>(一)入门与笔记应用浅开发

轻松上手:<Android Studio笔记应用开发>(二)笔记可显示Part1:实现逻辑与textView

轻松上手:<Android Studio笔记应用开发>(二)笔记可显示Part2:定义笔记的数据结构类型

轻松上手:<Android Studio笔记应用开发>(二)笔记可显示Part3:适配器

轻松上手:<Android Studio笔记应用开发>(二)大功告成!添加新笔记!

为了创建一个简单的Android笔记应用,前文已经成功实现了以下主要功能:

  1. 笔记的展示: 主活动(MainActivity)中通过一个列表视图(ListView)展示了所有笔记的内容和创建时间。

  2. 笔记的添加: 用户通过悬浮按钮(FloatingActionButton)可以添加新的笔记,进入编辑页面(EditActivity),并在该页面输入笔记内容后保存。

  3. 笔记的保存和显示: 新添加的笔记会保存在 SQLite 数据库中,主活动在每次启动时从数据库读取笔记列表,并通过适配器(NoteAdapter)将笔记显示在列表视图中。

III. 编辑活动类 (EditActivity) 的更新

1. 编辑活动的设计和布局

  • 布局设计: 采用垂直方向的线性布局,包含一个 EditText 用于用户输入笔记内容。
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="top">

    </EditText>

</LinearLayout>
  • 启动 EditActivity:
    • 通过点击悬浮按钮 (FloatingActionButton) 打开并编辑新笔记;
    • 通过点击列表中的笔记项更新旧笔记。

2. 加载现有笔记数据的方法 loadNoteData

编辑功能需要加载现有笔记数据以便进行编辑。 点击列表中的现有笔记时,跳转到编辑活动界面,并展示该笔记的原有数据。

  • 加载逻辑:
    • 通过传递的 note_id 判断是新建笔记还是编辑现有笔记。
  • note_id
    • note_id是干啥用的? note_id 用于在编辑活动 (EditActivity) 中标识要编辑的笔记。通过传递 note_id,我们可以从数据库中获取相应笔记的数据,并在编辑界面中显示原有内容。
    • note_id如何标识要编辑的笔记(如有)? note_id 是通过在意图(Intent)中附加额外信息的方式传递的。在启动编辑活动时,通过点击悬浮按钮或点击列表项触发的事件,我们会创建一个意图 (Intent) 并附加 note_id 作为额外的信息。在编辑活动中,通过 getIntent().getLongExtra("note_id", -1) 获取传递的 note_id,默认值为 -1 表示没有传递有效的 note_id
java 复制代码
// 从 EditActivity 获取传递的 note_id 额外信息,默认为 -1
long noteId = getIntent().getLongExtra("note_id", -1);
if (noteId != -1) {
    // 加载现有笔记数据以便编辑
    loadNoteData(noteId);
}
  • 数据库操作: 通过数据库操作类 (CRUD) 获取指定 note_id 的笔记数据,并将其展示在编辑界面上。
java 复制代码
CRUD op = new CRUD(this);
op.open();
Note existingNote = op.getNoteById(noteId);
op.close();
et.setText(existingNote.getContent());
  • 注意: Note getNoteById(long noteId) 目前尚未实现。

3. 判断新建还是更新笔记

  • 通过 note_id 判断是新建还是更新笔记: 通过传递的 note_id 是否为默认值(-1)来判断是新建笔记还是编辑现有笔记。
java 复制代码
long noteId = getIntent().getLongExtra("note_id", -1);
if (noteId == -1L) {
    // 如果是新笔记,直接传递数据
    setResult(RESULT_OK, intent);
} else {
    // 如果是现有笔记,传递 ID
    intent.putExtra("note_id", noteId);
    setResult(RESULT_OK, intent);
}

通过以上步骤,编辑活动类得以引入,并且能够根据传递的 note_id 区分新建笔记和编辑现有笔记,为后续编辑功能的实现奠定了基础。

4. 编辑活动类 (EditActivity) 更新后的代码

Java 复制代码
package com.example.my_notes_record;

import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.EditText;

import androidx.appcompat.app.AppCompatActivity;

import java.text.SimpleDateFormat;
import java.util.Date;

public class EditActivity extends AppCompatActivity{

    private String content; // 声明用于存储笔记内容的变量
    private String time; // 声明用于存储笔记时间的变量

    EditText et; // 声明文本编辑框

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.edit_layout); // 设置视图为 "edit_layout"
        et = findViewById(R.id.et); // 从布局文件中获取文本编辑框
        // 检查是否存在note_id额外信息
        long noteId = getIntent().getLongExtra("note_id", -1);
        if (noteId != -1) {
            // 加载现有笔记数据以便编辑
            loadNoteData(noteId);
        }
    }

    // 方法用于加载现有笔记数据
    private void loadNoteData(long noteId) {
        // 使用CRUD或其他方法根据noteId检索现有笔记数据
        CRUD op = new CRUD(this);
        op.open();
        Note existingNote = op.getNoteById(noteId);
        op.close();

        // 用现有笔记内容填充EditText
        et.setText(existingNote.getContent());
    }

    // 处理按键事件
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_HOME) {
            return true; // 如果按下 HOME 键,返回 true,表示事件已处理
        } else if (keyCode == KeyEvent.KEYCODE_BACK) {
            Intent intent = new Intent(); // 创建一个新意图
            intent.putExtra("content", et.getText().toString()); // 将编辑框中的文本内容放入意图中
            intent.putExtra("time", dateToString()); // 将当前时间放入意图中

            // 获取传递的note_id额外信息,默认为-1
            long noteId = getIntent().getLongExtra("note_id", -1L);

            if (noteId == -1L) {
                // 对于新笔记,直接传递数据
                setResult(RESULT_OK, intent);
            } else {
                // 对于现有笔记,传递ID
                intent.putExtra("note_id", noteId);
                setResult(RESULT_OK, intent);
            }
            finish(); // 结束当前活动
            return true; // 返回 true,表示事件已处理
        }
        return super.onKeyDown(keyCode, event); // 如果按键不是 HOME 或 BACK,使用默认处理
    }

    // 将当前时间格式化为字符串
    public String dateToString(){
        Date date = new Date(); // 获取当前日期和时间
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 创建日期格式化器
        return simpleDateFormat.format(date); // 格式化日期并返回字符串
    }
}

IV. 适配器的改进

1. 引入笔记项点击的回调接口 OnNoteItemClickListener

在原来的代码里,适配器(NoteAdapter)主要负责将数据与列表项的视图绑定,但没有处理列表项的点击事件。为了支持编辑功能,我们需要在适配器中引入一个回调接口,用于处理笔记项的点击事件。

2. 添加 OnNoteItemClickListener 接口

  • NoteAdapter 类中添加一个接口 OnNoteItemClickListener,定义笔记项点击的回调方法。
java 复制代码
public interface OnNoteItemClickListener {
    void onNoteItemClick(long noteId);
}

3. 注册点击事件监听器

  • NoteAdapter 中添加一个成员变量用于存储回调接口的实例,并在 getView 方法中注册点击事件监听器。
java 复制代码
private OnNoteItemClickListener onNoteItemClickListener;

public NoteAdapter(Context context, List<Note> noteList, OnNoteItemClickListener onNoteItemClickListener) {
    this.context = context;
    this.noteList = noteList;
    this.onNoteItemClickListener = onNoteItemClickListener;
}

4. 在 getView 中触发回调

  • getView 方法中,当笔记项被点击时,触发回调接口的方法,将点击事件传递给主活动类处理。
java 复制代码
view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // 触发笔记项点击事件
        if (onNoteItemClickListener != null) {
            onNoteItemClickListener.onNoteItemClick(noteList.get(position).getId());
        }
    }
});

5. 适配器改进后的代码

JAVA 复制代码
package com.example.my_notes_record;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;

public class NoteAdapter extends BaseAdapter {
    public interface OnNoteItemClickListener {
        void onNoteItemClick(long noteId);
    }
    private Context context;
    private List<Note> noteList;
    private OnNoteItemClickListener onNoteItemClickListener;

    // 默认构造函数
    public NoteAdapter(){
    }

    // 带参数的构造函数,接受上下文和笔记列表
    public NoteAdapter(Context Context,List<Note> noteList){
        this.context=Context;
        this.noteList=noteList;
    }

    public NoteAdapter(Context context, List<Note> noteList, OnNoteItemClickListener onNoteItemClickListener) {
        this.context = context;
        this.noteList = noteList;
        this.onNoteItemClickListener = onNoteItemClickListener;
    }

    // 获取列表项数量
    @Override
    public int getCount() {
        return noteList.size();
    }

    // 获取指定位置的笔记对象
    @Override
    public Object getItem(int position){
        return noteList.get(position);
    }

    // 获取指定位置的笔记ID
    @Override
    public long getItemId(int position){
        return position;
    }

    // 创建并返回每个列表项的视图
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 从XML布局文件实例化视图
        View view = View.inflate(context, R.layout.note_list_item, null);
        // 获取布局中的TextView控件
        TextView tv_content = (TextView) view.findViewById(R.id.tv_content);
        TextView tv_time = (TextView) view.findViewById(R.id.tv_time);

        // 从笔记对象中获取内容和时间信息
        String allText = noteList.get(position).getContent();

        // 设置TextView的文本内容
        tv_content.setText(allText.split("\n")[0]);
        tv_time.setText(noteList.get(position).getTime());

        // 将笔记ID作为视图的标签
        view.setTag(noteList.get(position).getId());

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 触发笔记项点击事件
                if (onNoteItemClickListener != null) {
                    onNoteItemClickListener.onNoteItemClick(noteList.get(position).getId());
                }
            }
        });

        return view;
    }
}

V. 主活动的更新

1. 实现接口 NoteAdapter.OnNoteItemClickListener

在主活动类中实现 NoteAdapter.OnNoteItemClickListener 接口,以处理笔记项的点击事件。

java 复制代码
public class MainActivity extends AppCompatActivity implements NoteAdapter.OnNoteItemClickListener {

2. 适配器初始化

在主活动的 onCreate 方法中初始化适配器时,将当前主活动实例传递给适配器。

java 复制代码
adapter = new NoteAdapter(getApplicationContext(), noteList , this);

3. 实现接口方法

实现接口中的方法 onNoteItemClick,该方法在用户点击笔记项时被调用,负责处理点击事件。

java 复制代码
@Override
public void onNoteItemClick(long noteId) {
    // 处理项点击,启动 EditActivity 并传递选定笔记以进行编辑
    Intent intent = new Intent(MainActivity.this, EditActivity.class);
    intent.putExtra("note_id", noteId);

    someActivityResultLauncher.launch(intent);
}

4. 处理编辑活动返回的数据

在主活动的 someActivityResultLauncher 中,获取从编辑活动返回的数据,包括编辑框中的文本内容、时间以及笔记的ID。

java 复制代码
long noteId = data.getLongExtra("note_id", -1);

// 检查是否是新笔记还是更新现有笔记
if (noteId == -1L) {
    // 如果是新笔记,调用添加新笔记的方法
    if (!content.isEmpty()) addNewNote(content, time);
} else {
    // 如果是现有笔记,调用更新现有笔记的方法
    updateExistingNote(noteId, content, time);
}

5. 添加新笔记和更新现有笔记的方法

实现添加新笔记和更新现有笔记的方法,通过数据库操作类(CRUD)进行相应的操作。

java 复制代码
// 添加新笔记
private void addNewNote(String content, String time) {
    CRUD op = new CRUD(this);
    op.open();
    Note newNote = new Note(content, time);
    op.addNote(newNote);
    op.close();
}

// 更新现有笔记
private void updateExistingNote(long noteId, String content, String time) {
    CRUD op = new CRUD(this);
    op.open();
    Note updatedNote = new Note(content, time);
    updatedNote.setId(noteId);
    op.updateNote(updatedNote);
    op.close();
}

6. 主活动类 (MainActivity)更新后的代码

java 复制代码
package com.example.my_notes_record;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.util.ArrayList;
import java.util.List;


// 创建名为 "MainActivity" 的主活动类
public class MainActivity extends AppCompatActivity implements NoteAdapter.OnNoteItemClickListener {

    private Context context = this; // 上下文对象,用于数据库操作
    private NoteDatabase dbHelper; // 数据库帮助类
    private NoteAdapter adapter; // 笔记适配器
    private List<Note> noteList = new ArrayList<>(); // 笔记列表
    private FloatingActionButton btn; // 悬浮按钮
    private ListView lv; // 列表视图

    // 定义一个 ActivityResultLauncher,用于处理其他活动的结果
    private ActivityResultLauncher<Intent> someActivityResultLauncher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        //调用父类的 onCreate 方法,用于执行一些初始化操作
        //savedInstanceState 参数用于恢复之前的状态

        setContentView(R.layout.activity_main);//设置当前 Activity 的布局

        btn = findViewById(R.id.floatingActionButton); // 悬浮按钮

        lv = findViewById(R.id.lv); // 列表视图,用于显示数据列表

        adapter = new NoteAdapter(getApplicationContext(), noteList , this);//初始化一个笔记适配器,并将应用的上下文对象和笔记列表传递给适配器

        refreshListView(); // 刷新笔记列表

        lv.setAdapter(adapter); // 将适配器与列表视图关联,从而显示笔记列表中的数据在界面上

        // 初始化 ActivityResultLauncher,用于处理启动其他活动的结果
        someActivityResultLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (result.getResultCode() == RESULT_OK) {
                        Intent data = result.getData();
                        if (data != null) {
                            // 从 EditActivity 返回的内容和时间
                            String content = data.getStringExtra("content");
                            String time = data.getStringExtra("time");
                            long noteId = data.getLongExtra("note_id", -1);

                            // 检查是否是新笔记还是更新现有笔记
                            if (noteId == -1L) {
                                // 如果是新笔记,调用添加新笔记的方法
                                if(!content.isEmpty()) addNewNote(content, time);
                            } else {
                                // 如果是现有笔记,调用更新现有笔记的方法
                                updateExistingNote(noteId, content, time);
                            }

                            refreshListView(); // 刷新笔记列表
                        }
                    }
                }
        );

        // 设置悬浮按钮的点击事件监听器
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 启动 EditActivity 并等待结果
                Intent intent = new Intent(MainActivity.this, EditActivity.class);
                someActivityResultLauncher.launch(intent);
            }
        });
    }

    // 刷新笔记列表
    public void refreshListView() {
        // 创建数据库操作对象,打开数据库连接
        CRUD op = new CRUD(context);
        op.open();

        if (noteList.size() > 0) noteList.clear(); // 清空笔记列表
        noteList.addAll(op.getAllNotes()); // 获取数据库中所有笔记
        op.close(); // 关闭数据库连接

        adapter.notifyDataSetChanged(); // 通知适配器数据已更改,刷新列表视图
    }

    @Override
    public void onNoteItemClick(long noteId) {
        // 处理项点击,启动 EditActivity 并传递选定笔记以进行编辑
        Intent intent = new Intent(MainActivity.this, EditActivity.class);
        intent.putExtra("note_id", noteId);

        someActivityResultLauncher.launch(intent);
    }



    // 添加新笔记
    private void addNewNote(String content, String time) {
        CRUD op = new CRUD(this);
        op.open();
        Note newNote = new Note(content, time);
        op.addNote(newNote);
        op.close();
    }

    // 更新现有笔记
    private void updateExistingNote(long noteId, String content, String time) {
        CRUD op = new CRUD(this);
        op.open();
        Note updatedNote = new Note(content, time);
        updatedNote.setId(noteId);
        op.updateNote(updatedNote);
        op.close();
    }
}

VI. 数据库操作的扩展

1. 实现根据 ID 获取笔记的方法

为了支持编辑功能,我们需要在数据库操作类(CRUD)中添加一个方法,根据笔记的ID获取笔记的详细信息。

java 复制代码
// 根据笔记ID获取笔记详细信息
public Note getNoteById(long noteId) {
    String[] columns = {NoteDatabase.ID, NoteDatabase.CONTENT, NoteDatabase.TIME};
    Cursor cursor = database.query(
            NoteDatabase.TABLE_NAME,
            columns,
            NoteDatabase.ID + " = ?",
            new String[]{String.valueOf(noteId)},
            null,
            null,
            null
    );

    if (cursor != null) {
        cursor.moveToFirst();
        Note note = cursorToNote(cursor);
        cursor.close();
        return note;
    } else {
        return null;
    }
}

2. 添加更新笔记的方法

为了在编辑活动中更新现有笔记,我们需要在数据库操作类中添加一个更新笔记的方法。

java 复制代码
// 更新现有笔记
public void updateNote(Note note) {
    ContentValues values = new ContentValues();
    values.put(NoteDatabase.CONTENT, note.getContent());
    values.put(NoteDatabase.TIME, note.getTime());

    return database.update(
            NoteDatabase.TABLE_NAME,
            values,
            NoteDatabase.ID + " = ?",
            new String[]{String.valueOf(note.getId())}
    );
}

3. 数据库操作类 (CRUD) 扩展后的代码

Java 复制代码
package com.example.my_notes_record;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import java.util.ArrayList;
import java.util.List;

public class CRUD {
    SQLiteOpenHelper dbHandler; // SQLiteOpenHelper 实例用于处理数据库连接
    SQLiteDatabase db; // SQLiteDatabase 实例用于执行数据库操作

    // 定义数据库表的列名
    private static final String[] columns = {
            NoteDatabase.ID,
            NoteDatabase.CONTENT,
            NoteDatabase.TIME
    };

    // 构造方法,接受上下文参数
    public CRUD(Context context) {
        dbHandler = new NoteDatabase(context); // 初始化数据库处理器
    }

    // 打开数据库连接
    public void open() {
        db = dbHandler.getWritableDatabase(); // 获取可写的数据库连接
    }

    // 关闭数据库连接
    public void close() {
        dbHandler.close(); // 关闭数据库处理器
    }

    // 添加一条笔记记录
    public Note addNote(Note note) {
        ContentValues contentValues = new ContentValues(); // 创建一个用于存储数据的 ContentValues 对象
        contentValues.put(NoteDatabase.CONTENT, note.getContent()); // 添加内容
        contentValues.put(NoteDatabase.TIME, note.getTime()); // 添加时间
        long insertId = db.insert(NoteDatabase.TABLE_NAME, null, contentValues); // 将数据插入数据库
        note.setId(insertId); // 将插入后的 ID 设置到笔记对象中
        return note; // 返回包含新数据的笔记对象
    }

    public List<Note> getAllNotes() {

        Cursor cursor = db.query(
                NoteDatabase.TABLE_NAME,  // 表名
                columns,                // 要查询的列(在这里是ID、内容、时间)
                null,                   // 查询条件(null表示无特殊条件)
                null,                   // 查询条件参数(null表示无特殊条件)
                null,                   // 分组方式(null表示不分组)
                null,                   // 过滤方式(null表示不过滤)
                null                    // 排序方式(null表示不排序)
        );

        List<Note> notes = new ArrayList<>(); // 创建一个笔记列表用于存储查询结果
        if (cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                Note note = new Note(); // 创建笔记对象
                note.setId(cursor.getLong(cursor.getColumnIndex(NoteDatabase.ID))); // 设置 ID
                note.setContent(cursor.getString(cursor.getColumnIndex(NoteDatabase.CONTENT))); // 设置内容
                note.setTime(cursor.getString(cursor.getColumnIndex(NoteDatabase.TIME))); // 设置时间
                notes.add(note); // 将笔记对象添加到列表中
            }
        }
        cursor.close(); // 关闭游标
        return notes; // 返回包含所有笔记记录的列表
    }
    // 根据 ID 获取笔记
    public Note getNoteById(long noteId) {
        // 查询数据库,获取指定 ID 的笔记记录
        Cursor cursor = db.query(
                NoteDatabase.TABLE_NAME,   // 表名
                columns,                   // 要查询的列(在这里是ID、内容、时间)
                NoteDatabase.ID + "=?",    // 查询条件(通过 ID 进行查询)
                new String[]{String.valueOf(noteId)},  // 查询条件参数(指定要查询的 ID 值)
                null,                      // 分组方式(null表示不分组)
                null,                      // 过滤方式(null表示不过滤)
                null                       // 排序方式(null表示不排序)
        );

        Note note = null;
        if (cursor.moveToFirst()) {
            // 如果查询到结果,则创建新的笔记对象并设置其属性
            note = new Note();
            note.setId(cursor.getLong(cursor.getColumnIndex(NoteDatabase.ID))); // 设置 ID
            note.setContent(cursor.getString(cursor.getColumnIndex(NoteDatabase.CONTENT))); // 设置内容
            note.setTime(cursor.getString(cursor.getColumnIndex(NoteDatabase.TIME))); // 设置时间
        }

        cursor.close(); // 关闭游标,释放资源
        return note; // 返回获取到的笔记对象,如果未找到则返回 null
    }
    
    // 更新笔记
    public void updateNote(Note note) {
        // 创建一个 ContentValues 对象,用于存储要更新的数据
        ContentValues values = new ContentValues();
        values.put(NoteDatabase.CONTENT, note.getContent());
        values.put(NoteDatabase.TIME, note.getTime());

        // 执行数据库更新操作
        db.update(
                NoteDatabase.TABLE_NAME,        // 表名
                values,                         // 更新的内容值
                NoteDatabase.ID + "=?",        // 更新条件(通过 ID 进行更新)
                //`"=?"` 是一个占位符,它表示在 SQL 查询中使用参数。这是一种防止 SQL 注入攻击的方式。在这里,它表示将在这个位置上填入具体的数值。
                new String[]{String.valueOf(note.getId())}  // 更新条件参数(指定要更新的 ID 值)
                //创建一个字符串数组,数组中包含了要替代占位符 `"=?"` 的具体数值。在这里,它包含了笔记对象的 ID。
        );
    }
}

VII. 结语

在本文中,我们深入探讨了从初始代码到实现编辑功能的全过程。通过介绍编辑活动、适配器的改进、主活动的更新以及数据库操作的扩展,我们建立了一个功能进一步完善的Android笔记应用。通过不懈的努力和不断的学习,我们可以打造出更加强大、高效的移动应用。继续热爱编程,不断挑战自我,开发更出色的应用吧!🚀

持续爆肝更新中...🫡🫡🫡

求点赞👍求关注🩷求转发💕

相关推荐
阿巴斯甜15 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker16 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952717 小时前
Andorid Google 登录接入文档
android
黄林晴18 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android