Android学习进阶
UI组件进阶
使用RecyclerView和Adapter显示列表数据
RecyclerView
是Android开发中用于显示列表数据的一个灵活且高效的组件。与其前身ListView
相比,RecyclerView
引入了更加复杂的布局排列和动画支持,使得创建高度定制化的列表和网格布局变得更加简单。使用RecyclerView
需要配合Adapter
来显示数据。以下是实现RecyclerView
显示列表数据的基本步骤:
1. 添加RecyclerView依赖
首先,确保你的项目中包含了RecyclerView
库。打开你的build.gradle
(Module: app)文件,并添加以下依赖:
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.1'
}
请检查是否有最新版本的RecyclerView
依赖可用,并使用最新版本。
2. 添加RecyclerView到布局文件
在你的布局文件中添加RecyclerView
组件。例如,在activity_main.xml
中:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
3. 创建列表项布局
为RecyclerView
中每个项(item)创建一个布局文件。例如,创建一个item_view.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="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<TextView
android:id="@+id/item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"/>
</LinearLayout>
4. 创建Adapter
创建一个Adapter
类继承自RecyclerView.Adapter
,并实现必要的方法:onCreateViewHolder()
, onBindViewHolder()
, getItemCount()
。这个Adapter
负责将数据绑定到每个ViewHolder
中。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] mDataset;
// 提供合适的构造器(取决于数据集的类型)
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
// 创建新视图(由布局管理器调用)
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 创建一个新视图
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_view, parent, false);
return new MyViewHolder(itemView);
}
// 替换视图的内容(由布局管理器调用)
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// 获取元素数据集在这个位置的数据,并替换视图的内容
holder.textView.setText(mDataset[position]);
}
// 返回数据集的大小(由布局管理器调用)
@Override
public int getItemCount() {
return mDataset.length;
}
// 提供对视图的引用
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.item_text);
}
}
}
5. 在Activity中使用RecyclerView
在你的Activity
中设置RecyclerView
和Adapter
:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.my_recycler_view);
// 使用这个设置来提高性能,如果你知道内容不会改变布局大小
recyclerView.setHasFixedSize(true);
// 使用线性布局管理器
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
// 指定adapter
adapter = new MyAdapter(new String[]{"Data 1", "Data 2", "Data 3"});
recyclerView.setAdapter(adapter);
}
}
这段代码设置了一个简单的RecyclerView
,它使用线性布局显示一个字符串数组中的数据。
总结
RecyclerView
是展示集合数据的强大工具,它的灵活性和扩展性使得创建复杂的列表和网格布局变得容易。通过自定义Adapter
和ViewHolder
,你可以高度定制每个列表项的展示方式,包括布局和动画效果。此外,RecyclerView
还支持添加分隔符、处理点击事件等高级功能。
Fragment的使用和与Activity的交互
Fragment 的基本使用
Fragment
是一种可以在Activity
内部使用的可复用组件,它有自己的生命周期、接收输入事件,并且可以添加到Activity
布局中。Fragment
使得在一个Activity
中组合多个组件以及在多个Activity
之间复用同一个组件成为可能。
创建 Fragment
-
定义 Fragment 类:扩展
Fragment
类,并重写关键的生命周期方法,如onCreateView()
,在其中通过布局填充器加载Fragment
的布局。
public class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_example, container, false);
}
}
Fragment 布局:为Fragment
创建一个XML布局文件,例如fragment_example.xml
。
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello from the Fragment"/>
在 Activity 布局中声明:在Activity
的布局文件中,使用<fragment>
标签声明Fragment
,或者在Activity
的代码中动态添加。
静态添加:
<fragment android:name="com.example.ExampleFragment"
android:id="@+id/example_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
动态添加:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
与 Activity 的交互
Fragment
与Activity
的交互通常通过以下几种方式实现:
-
通过 Activity 传递数据给 Fragment:
- 可以在创建
Fragment
的实例时通过Bundle
设置参数。
- 可以在创建
ExampleFragment fragment = new ExampleFragment();
Bundle args = new Bundle();
args.putInt("someInt", 123);
fragment.setArguments(args);
Fragment 回调到 Activity:
- 定义一个回调接口,并让
Activity
实现它。然后在Fragment
内调用Activity
的回调方法。
public class ExampleFragment extends Fragment {
CallbackInterface callback;
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
callback = (CallbackInterface) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement CallbackInterface");
}
}
public interface CallbackInterface {
void onSomeEvent();
}
}
然后在Activity
中实现这个接口:
public class MainActivity extends AppCompatActivity implements ExampleFragment.CallbackInterface {
@Override
public void onSomeEvent() {
// 处理回调事件
}
}
使用 ViewModel:
ViewModel
可以用于Activity
和所有的Fragment
之间共享数据。它遵循观察者模式,当数据变化时通知UI进行更新。
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Integer> selected = new MutableLiveData<Integer>();
public void select(int item) {
selected.setValue(item);
}
public LiveData<Integer> getSelected() {
return selected;
}
}
然后Activity
和Fragment
都可以观察这个ViewModel
中的数据变化。
总结
Fragment
为创建动态和可复用的UI组件提供了极大的灵活性。通过合理使用Fragment
,你可以使你的应用更加模块化,更容易适应不同的屏幕尺寸和方向。与Activity
的交互使得组件之间的数据传递和事件处理成为可能,ViewModel
的使用进一步简化了这种交互,使数据管理变得更加高效和简单。
网络编程
学习如何使用HttpURLConnection或OkHttp进行网络请求
在Android开发中,进行网络请求是一项常见需求。HttpURLConnection
和OkHttp
是两种流行的方式来执行这些请求。下面分别介绍如何使用这两种方法。
使用 HttpURLConnection
HttpURLConnection
是Java标准库的一部分,可以直接在Android项目中使用。它支持基本的GET、POST等HTTP方法。
示例:使用HttpURLConnection发起GET请求
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL("http://www.example.com");
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
String response = readStream(in);
// 处理响应...
} catch (Exception e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
}).start();
这个例子展示了如何在一个新线程中发起一个简单的GET请求,并读取响应。注意网络请求必须在非UI线程中执行。
使用 OkHttp
OkHttp
是一个第三方库,由Square开发,相比HttpURLConnection
,它提供了更简洁的API、更快的速度和更丰富的功能,如连接池、GZIP压缩和请求缓存等。
首先,添加OkHttp的依赖到你的build.gradle
文件:
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
}
示例:使用OkHttp发起GET请求
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
new Thread(new Runnable() {
@Override
public void run() {
try {
String response = run("http://www.example.com");
// 处理响应...
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
这个例子展示了如何使用OkHttp发起一个GET请求,并同样需要在非UI线程中执行。
异步请求
对于OkHttp,进行异步请求和处理响应更为方便,只需要使用enqueue
方法而不是execute
,并提供一个Callback
:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.example.com")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
// 处理响应...
}
});
这样就不需要手动创建新线程,OkHttp会自动异步执行请求,并在回调中返回结果。
总结
HttpURLConnection
是一个基本的网络请求工具,直接内置于Java中,适合简单的网络操作和对第三方库依赖要求较低的场景。而OkHttp
提供了更强大的功能和更好的性能,适合需要复杂网络操作、性能优化和更好体验的应用。选择哪一个,取决于你的具体需求和偏好。在实际开发中,由于OkHttp
的强大和易用性,它通常是更受欢迎的选择。
理解JSON数据格式,使用Gson或Jackson解析JSON数据
理解 JSON 数据格式
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript的对象字面量规范,但独立于语言,因此许多编程语言都有JSON数据格式的解析和生成支持。JSON格式在网络传输数据时特别有用,如API请求的响应通常就是JSON格式。
一个简单的JSON对象例子:
{
"name": "John Doe",
"age": 30,
"isStudent": false,
"courses": ["Math", "Science"],
"address": {
"street": "123 Main St",
"city": "Anytown"
}
}
使用 Gson 解析 JSON
Gson是Google提供的用于Java对象和JSON数据之间转换的一个库。它能够将一个JSON字符串转换成等价的Java对象,或者将Java对象转换成其JSON表示形式。
添加 Gson 依赖
首先,在build.gradle
文件中添加Gson的依赖:
dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
}
解析 JSON 示例
假设有一个JSON表示的用户信息,我们想要将其解析为一个Java对象。
定义一个Java类来表示用户:
public class User {
private String name;
private int age;
private boolean isStudent;
private List<String> courses;
private Address address;
// Address类
public static class Address {
private String street;
private String city;
// getters 和 setters
}
// getters 和 setters
}
使用Gson来解析JSON字符串:
String json = "{...}"; // JSON数据
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
使用 Jackson 解析 JSON
Jackson是另一个流行的Java库,用于处理JSON。与Gson类似,它也可以轻松地将JSON字符串转换为Java对象,或将Java对象序列化为JSON字符串。
添加 Jackson 依赖
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.3'
}
解析 JSON 示例
使用Jackson解析同样的JSON数据:
String json = "{...}"; // JSON数据
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(json, User.class);
JSON 解析库选择
Gson和Jackson都是处理JSON数据的优秀库,它们提供了丰富的API和灵活的配置。选择哪个主要取决于个人偏好以及特定项目的需求。Gson通常使用起来更简单一些,而Jackson在处理复杂的JSON结构时提供了更多的功能和更高的性能。无论选择哪个,它们都大大简化了JSON数据的处理过程。
数据存储
学习SharedPreferences的使用,用于保存轻量级的本地数据
SharedPreferences
是Android平台上一个轻量级的存储方案,主要用于保存应用的配置数据或者是少量的数据需要持久化,比如用户设置、应用内的简单状态等。SharedPreferences
以键值对(Key-Value)的形式存储数据,支持基本的数据类型,如boolean
、float
、int
、long
、String
及它们的集合。
使用SharedPreferences存储数据
要使用SharedPreferences
存储数据,可以通过Context
的getSharedPreferences
方法获取SharedPreferences
的实例。这个方法需要两个参数:首先是文件名(如果文件不存在,Android会创建一个),其次是操作模式(通常是MODE_PRIVATE
,表示只有当前的应用可以访问这个文件)。
示例:保存数据
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key_name", "John Doe");
editor.putInt("key_age", 30);
editor.putBoolean("key_is_student", false);
editor.apply(); // 或者 editor.commit();
getSharedPreferences
方法用于获取一个SharedPreferences
实例。- 使用
edit()
方法获得SharedPreferences.Editor
对象,然后通过这个对象添加或修改键值对。 - 调用
apply()
或commit()
提交更改。apply()
是异步的,没有返回值,而commit()
是同步的,会返回一个布尔值表示操作是否成功。
使用SharedPreferences读取数据
要从SharedPreferences
读取数据,可以使用相应的get
方法,如getString
、getInt
等。如果键不存在,可以为这些方法提供一个默认值。
示例:读取数据
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", MODE_PRIVATE);
String name = sharedPreferences.getString("key_name", "No Name");
int age = sharedPreferences.getInt("key_age", 0);
boolean isStudent = sharedPreferences.getBoolean("key_is_student", true);
在这个示例中,如果SharedPreferences
中不存在对应的键,getString
方法将返回"No Name",getInt
将返回0,getBoolean
将返回true
。
注意事项
SharedPreferences
适合存储轻量级的数据,但不适合存储大量数据或复杂的数据结构。- 存储和读取操作都是同步进行的,可能会阻塞主线程。因此,如果有大量数据需要读写,建议在子线程中进行操作,尽管
apply()
方法已经是异步执行的。 - 使用
SharedPreferences
时,数据是以明文形式保存的,因此不应该用来存储敏感信息,如用户密码。
SharedPreferences
是实现数据持久化的一种简单而有效的方法,特别适合于保存少量的配置信息或状态数据。
学习SQLite数据库的使用,进行数据的增、删、改、查操作
SQLite是一个轻量级的数据库,它存储在单个磁盘文件上。在Android中,SQLite被广泛用于数据持久化需求,支持标准的SQL语法,并提供了一套Java API,使得在Android应用中进行数据库操作变得简单。
创建 SQLite 数据库
在Android中,通常通过扩展SQLiteOpenHelper
类来创建数据库和表。这个类提供了onCreate()
和onUpgrade()
两个回调函数,用于数据库的创建和版本管理。
public class DBHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "example.db";
private static final int DATABASE_VERSION = 1;
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(
"CREATE TABLE contacts (" +
"_id INTEGER PRIMARY KEY," +
"name TEXT," +
"email TEXT)"
);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS contacts");
onCreate(db);
}
}
插入数据
使用getWritableDatabase()
获取SQLiteDatabase
对象,然后通过insert()
方法或执行SQL
语句插入数据。
DBHelper dbHelper = new DBHelper(context);
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "John Doe");
values.put("email", "john@example.com");
long newRowId = db.insert("contacts", null, values);
查询数据
查询数据通常使用query()
方法或直接执行SQL
查询语句,并通过Cursor
遍历查询结果。
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query(
"contacts", // 表名称
new String[] { "_id", "name", "email" }, // 要查询的列
null, // WHERE子句
null, // WHERE子句的参数
null, // GROUP BY子句
null, // HAVING子句
null // ORDER BY子句
);
List<String> names = new ArrayList<>();
while(cursor.moveToNext()) {
String name = cursor.getString(
cursor.getColumnIndexOrThrow("name"));
names.add(name);
}
cursor.close();
更新数据
更新数据可以使用update()
方法或直接执行SQL
语句。
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("email", "johndoe@example.com");
String selection = "name LIKE ?";
String[] selectionArgs = { "John Doe" };
int count = db.update(
"contacts",
values,
selection,
selectionArgs);
删除数据
删除数据可以使用delete()
方法或直接执行SQL
语句。
SQLiteDatabase db = dbHelper.getWritableDatabase();
String selection = "name LIKE ?";
String[] selectionArgs = { "John Doe" };
int deletedRows = db.delete("contacts", selection, selectionArgs);
注意事项
- 执行数据库操作(尤其是写入和更新操作)时,建议使用事务来保证数据的一致性。
- 执行完数据库操作后,记得关闭
Cursor
和SQLiteDatabase
对象,以释放资源。 - 对于复杂的数据库操作和管理,可以考虑使用Room Persistence Library,它是一个在SQLite之上的抽象层,提供了更简洁的API和编译时的SQL检查。
使用SQLite数据库,可以在Android应用中有效地进行数据持久化操作,对于需要存储大量结构化数据的应用尤其有用。
Android的异步处理
理解并使用AsyncTask和Handler进行异步任务处理
在Android开发中,异步任务处理是常见需求,主要用于执行耗时操作(如网络请求、数据库操作等),而不阻塞主线程(UI线程)。AsyncTask
和Handler
是实现这一目标的两种常用方法。
AsyncTask
AsyncTask
是一个抽象的泛型类,它允许你在后台线程上执行长时间运行的操作,并在完成后将结果发布到UI线程。不过,从Android 11(API级别30)开始,AsyncTask
已被标记为过时(deprecated),建议使用其他现代化的方式,如java.util.concurrent
或Kotlin 协程。
使用AsyncTask的基本步骤
- 定义一个继承
AsyncTask
的类:指定输入参数、进度和结果的类型。 - 实现
doInBackground
方法:在这里执行后台任务。 - (可选)实现
onPreExecute
、onPostExecute
和onProgressUpdate
方法:在UI线程上执行操作,如初始化、更新进度和处理结果。
private static class ExampleAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// 在UI线程执行初始化操作
}
@Override
protected String doInBackground(Void... voids) {
// 执行耗时后台任务
return "Result";
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
// 使用后台任务的结果在UI线程上执行操作
}
}
执行AsyncTask
:
new ExampleAsyncTask().execute();
Handler
Handler
是Android中处理线程间通信的另一种方式,它允许你发送和处理Message
和Runnable
对象与一个MessageQueue
关联的线程。Handler
常用于在工作线程完成任务后更新UI。
使用Handler的基本步骤
- 创建
Handler
实例:在主线程(通常是在Activity
或Fragment
中)创建Handler
实例来处理消息或运行代码。 - 在工作线程中使用
Handler
发送消息或执行Runnable。
// 在主线程创建Handler
Handler handler = new Handler(Looper.getMainLooper());
// 工作线程执行任务
new Thread(new Runnable() {
@Override
public void run() {
// 执行耗时任务
// 任务完成,通知UI线程更新
handler.post(new Runnable() {
@Override
public void run() {
// 在UI线程执行操作
}
});
}
}).start();
总结
- AsyncTask:适用于简单的异步任务,尤其是那些直接与UI相关的。然而,因为
AsyncTask
已被标记为过时,建议使用更现代的并发解决方案。 - Handler:适用于复杂的线程间通信和对于UI的定时更新或处理。
Handler
是处理线程间通信的强大工具,但使用时需要更多的注意事项,特别是与线程和消息循环相关的。
对于现代Android开发,推荐使用java.util.concurrent
中的类(如Executor
、ThreadPoolExecutor
)和Kotlin协程,这些方法提供了更强大、更灵活和更简洁的并发和异步处理能力。