当前位置: 首页 > news >正文

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"><TextViewandroid: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;}// 创建新视图(由布局管理器调用)@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {// 创建一个新视图View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);return new MyViewHolder(itemView);}// 替换视图的内容(由布局管理器调用)@Overridepublic void onBindViewHolder(MyViewHolder holder, int position) {// 获取元素数据集在这个位置的数据,并替换视图的内容holder.textView.setText(mDataset[position]);}// 返回数据集的大小(由布局管理器调用)@Overridepublic 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中设置RecyclerViewAdapter

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private RecyclerView.Adapter adapter;private RecyclerView.LayoutManager layoutManager;@Overrideprotected 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);// 指定adapteradapter = new MyAdapter(new String[]{"Data 1", "Data 2", "Data 3"});recyclerView.setAdapter(adapter);}
}

这段代码设置了一个简单的RecyclerView,它使用线性布局显示一个字符串数组中的数据。

总结

RecyclerView是展示集合数据的强大工具,它的灵活性和扩展性使得创建复杂的列表和网格布局变得容易。通过自定义AdapterViewHolder,你可以高度定制每个列表项的展示方式,包括布局和动画效果。此外,RecyclerView还支持添加分隔符、处理点击事件等高级功能。

Fragment的使用和与Activity的交互

Fragment 的基本使用

Fragment是一种可以在Activity内部使用的可复用组件,它有自己的生命周期、接收输入事件,并且可以添加到Activity布局中。Fragment使得在一个Activity中组合多个组件以及在多个Activity之间复用同一个组件成为可能。

创建 Fragment
  1. 定义 Fragment 类:扩展Fragment类,并重写关键的生命周期方法,如onCreateView(),在其中通过布局填充器加载Fragment的布局。

public class ExampleFragment extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn 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 的交互

FragmentActivity的交互通常通过以下几种方式实现:

  1. 通过 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;@Overridepublic 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 {@Overridepublic 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;}
}

 然后ActivityFragment都可以观察这个ViewModel中的数据变化。

总结

Fragment为创建动态和可复用的UI组件提供了极大的灵活性。通过合理使用Fragment,你可以使你的应用更加模块化,更容易适应不同的屏幕尺寸和方向。与Activity的交互使得组件之间的数据传递和事件处理成为可能,ViewModel的使用进一步简化了这种交互,使数据管理变得更加高效和简单。

网络编程

学习如何使用HttpURLConnection或OkHttp进行网络请求

在Android开发中,进行网络请求是一项常见需求。HttpURLConnectionOkHttp是两种流行的方式来执行这些请求。下面分别介绍如何使用这两种方法。

使用 HttpURLConnection

HttpURLConnection是Java标准库的一部分,可以直接在Android项目中使用。它支持基本的GET、POST等HTTP方法。

示例:使用HttpURLConnection发起GET请求
new Thread(new Runnable() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic 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)的形式存储数据,支持基本的数据类型,如booleanfloatintlongString及它们的集合。

使用SharedPreferences存储数据

要使用SharedPreferences存储数据,可以通过ContextgetSharedPreferences方法获取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方法,如getStringgetInt等。如果键不存在,可以为这些方法提供一个默认值。

示例:读取数据
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);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE contacts (" +"_id INTEGER PRIMARY KEY," +"name TEXT," +"email TEXT)");}@Overridepublic 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);

注意事项

  • 执行数据库操作(尤其是写入和更新操作)时,建议使用事务来保证数据的一致性。
  • 执行完数据库操作后,记得关闭CursorSQLiteDatabase对象,以释放资源。
  • 对于复杂的数据库操作和管理,可以考虑使用Room Persistence Library,它是一个在SQLite之上的抽象层,提供了更简洁的API和编译时的SQL检查。

使用SQLite数据库,可以在Android应用中有效地进行数据持久化操作,对于需要存储大量结构化数据的应用尤其有用。

Android的异步处理

理解并使用AsyncTask和Handler进行异步任务处理

在Android开发中,异步任务处理是常见需求,主要用于执行耗时操作(如网络请求、数据库操作等),而不阻塞主线程(UI线程)。AsyncTaskHandler是实现这一目标的两种常用方法。

AsyncTask

AsyncTask是一个抽象的泛型类,它允许你在后台线程上执行长时间运行的操作,并在完成后将结果发布到UI线程。不过,从Android 11(API级别30)开始,AsyncTask已被标记为过时(deprecated),建议使用其他现代化的方式,如java.util.concurrent或Kotlin 协程。

使用AsyncTask的基本步骤
  1. 定义一个继承AsyncTask的类:指定输入参数、进度和结果的类型。
  2. 实现doInBackground方法:在这里执行后台任务。
  3. (可选)实现onPreExecuteonPostExecuteonProgressUpdate方法:在UI线程上执行操作,如初始化、更新进度和处理结果。
private static class ExampleAsyncTask extends AsyncTask<Void, Void, String> {@Overrideprotected void onPreExecute() {super.onPreExecute();// 在UI线程执行初始化操作}@Overrideprotected String doInBackground(Void... voids) {// 执行耗时后台任务return "Result";}@Overrideprotected void onPostExecute(String result) {super.onPostExecute(result);// 使用后台任务的结果在UI线程上执行操作}
}

执行AsyncTask

new ExampleAsyncTask().execute();

Handler

Handler是Android中处理线程间通信的另一种方式,它允许你发送和处理MessageRunnable对象与一个MessageQueue关联的线程。Handler常用于在工作线程完成任务后更新UI。

使用Handler的基本步骤
  1. 创建Handler实例:在主线程(通常是在ActivityFragment中)创建Handler实例来处理消息或运行代码。
  2. 在工作线程中使用Handler发送消息或执行Runnable
// 在主线程创建Handler
Handler handler = new Handler(Looper.getMainLooper());// 工作线程执行任务
new Thread(new Runnable() {@Overridepublic void run() {// 执行耗时任务// 任务完成,通知UI线程更新handler.post(new Runnable() {@Overridepublic void run() {// 在UI线程执行操作}});}
}).start();

总结

  • AsyncTask:适用于简单的异步任务,尤其是那些直接与UI相关的。然而,因为AsyncTask已被标记为过时,建议使用更现代的并发解决方案。
  • Handler:适用于复杂的线程间通信和对于UI的定时更新或处理。Handler是处理线程间通信的强大工具,但使用时需要更多的注意事项,特别是与线程和消息循环相关的。

对于现代Android开发,推荐使用java.util.concurrent中的类(如ExecutorThreadPoolExecutor)和Kotlin协程,这些方法提供了更强大、更灵活和更简洁的并发和异步处理能力。

相关文章:

  • Mapper.xml映射文件
  • 【笔记】Python学习记录
  • Windows 11 安装 Scoop
  • Mysql数据库:索引管理
  • 【算法与数据结构】二叉树(前中后)序遍历
  • 自营、入驻商城小程序开发
  • Charles 工具如何做断点测试?
  • 流畅的 Python 第二版(GPT 重译)(二)
  • Elastic-Job 分布式任务调度
  • 外包干了14天,技术退步明显。。。
  • 【数据库】SQL Server 2008 R2 安装过程
  • 数据结构之排序一
  • 第六章 Java 正则表达式
  • 34-Java传输对象模式 ( Transfer Object Pattern )
  • CTF题型 md5考法例题汇总
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 77. Combinations
  • Consul Config 使用Git做版本控制的实现
  • Java程序员幽默爆笑锦集
  • Java多线程(4):使用线程池执行定时任务
  • js数组之filter
  • vue2.0项目引入element-ui
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 前端路由实现-history
  • 数据结构java版之冒泡排序及优化
  • 突破自己的技术思维
  • 为什么要用IPython/Jupyter?
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • MPAndroidChart 教程:Y轴 YAxis
  • mysql面试题分组并合并列
  • 昨天1024程序员节,我故意写了个死循环~
  • #{} 和 ${}区别
  • $(selector).each()和$.each()的区别
  • (06)Hive——正则表达式
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (ZT)出版业改革:该死的死,该生的生
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (转)EOS中账户、钱包和密钥的关系
  • (转载)Linux 多线程条件变量同步
  • .Net CF下精确的计时器
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • @reference注解_Dubbo配置参考手册之dubbo:reference
  • [bzoj1038][ZJOI2008]瞭望塔
  • [bzoj1901]: Zju2112 Dynamic Rankings
  • [C# 网络编程系列]专题六:UDP编程
  • [EMWIN]FRAMEWIN 与 WINDOW 的使用注意
  • [ffmpeg] 定制滤波器
  • [Flutter] extends、implements、mixin和 abstract、extension的使用介绍说明
  • [Flutter]打包IPA
  • [FxCop.设计规则]8. 也许参数类型应该是基类型