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中设置RecyclerViewAdapter

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

Fragment的使用和与Activity的交互

Fragment 的基本使用

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

创建 Fragment
  1. 定义 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 的交互

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;

    @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;
    }
}

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

总结

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

网络编程

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

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

使用 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)的形式存储数据,支持基本的数据类型,如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);
    }
    
    @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);

注意事项

  • 执行数据库操作(尤其是写入和更新操作)时,建议使用事务来保证数据的一致性。
  • 执行完数据库操作后,记得关闭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> {
    @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中处理线程间通信的另一种方式,它允许你发送和处理MessageRunnable对象与一个MessageQueue关联的线程。Handler常用于在工作线程完成任务后更新UI。

使用Handler的基本步骤
  1. 创建Handler实例:在主线程(通常是在ActivityFragment中)创建Handler实例来处理消息或运行代码。
  2. 在工作线程中使用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中的类(如ExecutorThreadPoolExecutor)和Kotlin协程,这些方法提供了更强大、更灵活和更简洁的并发和异步处理能力。

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值