安卓编写---在适配器中采用监听器
根据以下的例子,来学习怎么在适配器中怎么使用监听器。
1.在适配器中使用监听器的目的是什么?
答: 实现组件间的解耦通信,Adapter 只应负责数据绑定和视图创建,不应包含业务逻辑,通过监听器将点击事件"转发"给业务层处理。保障线程安全与生命周期安全,自动在主线程回调(避免UI更新崩溃)。
2.在适配器中使用监听器的顺序是什么?
参考以下案例的步骤
在Adapter里面
①定义监听接口
public interface OnItemClickListener { void onItemClick(int position, KeySetting item); }
②设置监听器:将外部传入的监听器对象保存到适配器成员变量中
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
③触发监听
holder.itemView.setOnClickListener(v -> {//为整个列表项设置点击监听器d
int adapterPosition = holder.getAdapterPosition();//获取当前点击项在Adapter中的位置
//检查位置是否有效:adapterPosition != RecyclerView.NO_POSITION
//检查监听器是否已经设置:onItemClickListener != null
if (adapterPosition != RecyclerView.NO_POSITION && onItemClickListener != null) {
//触发回调,传递两个关键数据,点击的位置索引,点击项的数据对象
onItemClickListener.onItemClick(adapterPosition, keyListInAdapter.get(adapterPosition));
}
});
在Activity中
//【外部使用监听器】实现接口
adapter.setOnItemClickListener((position, item) -> {
// 写入缓存,只写当前耳朵
Caches.getInstance().saveButtonTitle(buttonIndex, item.getName(), isLeftSelected);
Caches.getInstance().saveButtonAction(buttonIndex, item.getName(), isLeftSelected);
// 更新UI
updateButtonDisplay(buttonIndex, item.getName());
adapter.setSelectedPosition(position);//用户点击时,更新选中状态
tvTitle.setText(item.getName());
});
3.具体的数据流是怎么样流动的?
①当用户点击列表项,直接触发适配器中的holder.itemView.setOnClickListener,立刻向Adapter传递数据onItemClickListener.onItemClick(adapterPosition,keyListInAdapter.get(adapterPosition));。
②适配器自动检查监听器有效性,然后转发数据,就是回调listener.onItemClick()。
③Activity收到数据,根据收到的数据处理UI。
标准流程图


核心组件关系图

MainActivity.java
package com.example.myapplication;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
//UI
private TextView leftEarphone, rightEarphone; //左耳,右耳
private ImageView oneClickForward, doubleClickForward, threeClickForward, longPressForward; //单击,双击,三击,长按
private TextView oneClickWoldDispaly, doubleClickWoldDispaly, threeClickWoldDispaly, longPressWoldDispaly; //显示的功能值
private int oneclick_1 = 1, doubleclick_2 = 2, threeclick_3 = 3, longpress_4 = 4; //用于区分按钮
private boolean isLeftSelected = true; // 当前选中的耳朵,默认左耳(true 左耳,false 右耳)
private BottomSheetDialog dialog; // 当前显示的对话框
List keyList = new ArrayList<>();//数据列表
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化
initView();
initClick();
initListDate();
// 初始化加载配置
loadButtonConfigs();
selectEarUI(); // 触发首次UI更新
}
@Override
protected void onPause() {
super.onPause();
dismissCurrentDialog();
}
// 初始化列表数据
private void initListDate() {
keyList.add(new KeySetting("无作用"));
keyList.add(new KeySetting("语音助手"));
keyList.add(new KeySetting("上一首"));
keyList.add(new KeySetting("下一首"));
keyList.add(new KeySetting("音量加"));
keyList.add(new KeySetting("音量减"));
keyList.add(new KeySetting("播放/暂停"));
keyList.add(new KeySetting("ANC切换"));
}
//1.获取视图
private void initView() {
leftEarphone = findViewById(R.id.tv_left_earphone);
rightEarphone = findViewById(R.id.tv_right_earphone);
oneClickForward = findViewById(R.id.iv_one_forward);
doubleClickForward = findViewById(R.id.iv_double_forward);
threeClickForward = findViewById(R.id.iv_three_forward);
longPressForward = findViewById(R.id.iv_long_forward);
oneClickWoldDispaly = findViewById(R.id.tv_one_click_display);
doubleClickWoldDispaly = findViewById(R.id.tv_double_click_display);
threeClickWoldDispaly = findViewById(R.id.tv_three_click_display);
longPressWoldDispaly = findViewById(R.id.tv_long_press_display);
}
//2.点击事件
private void initClick() {
// 按钮点击事件
oneClickForward.setOnClickListener(v -> showSelectDialog(oneclick_1));
doubleClickForward.setOnClickListener(v -> showSelectDialog(doubleclick_2));
threeClickForward.setOnClickListener(v -> showSelectDialog(threeclick_3));
longPressForward.setOnClickListener(v -> showSelectDialog(longpress_4));
// 左耳点击事件
leftEarphone.setOnClickListener(view -> {
isLeftSelected = true;
selectEarUI();//更新UI
});
// 右耳点击事件
rightEarphone.setOnClickListener(view -> {
isLeftSelected = false;
selectEarUI();//更新UI
});
}
// 3.初始化 UI, 从Caches读取4个按钮的功能值,默认显示左耳
private void loadButtonConfigs() {
// 加载当前选中耳朵的配置
updateButtonDisplays(isLeftSelected);
}
//读存储,更新用来显示的功能值
private void updateButtonDisplays(boolean isLeft) {
oneClickWoldDispaly.setText(
Caches.getInstance().getButtonAction(oneclick_1, isLeft)
);
doubleClickWoldDispaly.setText(
Caches.getInstance().getButtonAction(doubleclick_2, isLeft)
);
threeClickWoldDispaly.setText(
Caches.getInstance().getButtonAction(threeclick_3, isLeft)
);
longPressWoldDispaly.setText(
Caches.getInstance().getButtonAction(longpress_4, isLeft)
);
}
// 4.修改按键的UI
private void selectEarUI() {
Log.d(TAG, "选中耳机: " + (isLeftSelected ? "左耳" : "右耳"));
leftEarphone.setBackgroundColor(ContextCompat.getColor(this,
isLeftSelected ? R.color.blue : R.color.gray));
leftEarphone.setTextColor(ContextCompat.getColor(this,
isLeftSelected ? R.color.white : R.color.black));
rightEarphone.setBackgroundColor(ContextCompat.getColor(this,
!isLeftSelected ? R.color.blue : R.color.gray));
rightEarphone.setTextColor(ContextCompat.getColor(this,
!isLeftSelected ? R.color.white : R.color.black));
// 切换按钮时,重新从caches里面更新按钮的功能值
updateButtonDisplays(isLeftSelected);
}
// 5.弹窗展示
private void showSelectDialog(int buttonIndex) {
// 先销毁已有对话框,确保同一时间只存在一个对话框实例
dismissCurrentDialog();
//创建新对话框
dialog = new BottomSheetDialog(this);
//加载对话框布局
View view = LayoutInflater.from(this).inflate(R.layout.dialog_key_setting_select, null);
dialog.setContentView(view);
//获取子视图
TextView tvTitle = view.findViewById(R.id.tv_title);//子项的标题
RecyclerView rv = view.findViewById(R.id.rv_actions);//子项的列表
// 同步标题文字:从Caches读取当前按键的标题
String savedTitle = Caches.getInstance().getButtonTitle(buttonIndex, isLeftSelected);
//同步获取当前对话框设置的功能值
String currentAction = Caches.getInstance().getButtonAction(buttonIndex, isLeftSelected);
tvTitle.setText(savedTitle != null ? savedTitle : "请选择");
//配置适配器
rv.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));//设置分割线
rv.setLayoutManager(new LinearLayoutManager(this));
// 设置适配器,将列表数据绑定到RecyclerView
KeySettingAdapter adapter = new KeySettingAdapter(keyList);
rv.setAdapter(adapter);
//初始化选中项,遍历列表找到与缓存匹配的项
for (int i = 0; i < keyList.size(); i++) {
if (keyList.get(i).getName().equals(currentAction)) {
adapter.setSelectedPosition(i);//把选中的位置传给适配器
break;
}
}
//设置列表项点击监听器
//【外部使用监听器】实现接口
adapter.setOnItemClickListener((position, item) -> {
// 写入缓存,只写当前耳朵
Caches.getInstance().saveButtonTitle(buttonIndex, item.getName(), isLeftSelected);
Caches.getInstance().saveButtonAction(buttonIndex, item.getName(), isLeftSelected);
// 更新UI
updateButtonDisplay(buttonIndex, item.getName());
adapter.setSelectedPosition(position);//用户点击时,更新选中状态
tvTitle.setText(item.getName());
});
dialog.setOnDismissListener(dialog -> {
dialog = null; // 清除引用
});
dialog.show();
}
//销毁对话框
private void dismissCurrentDialog() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
dialog = null;
}
}
// 6.更新按钮的显示
private void updateButtonDisplay(int buttonIndex, String newTitle) {
switch (buttonIndex) {
case 1:
oneClickWoldDispaly.setText(newTitle);
break;
case 2:
doubleClickWoldDispaly.setText(newTitle);
break;
case 3:
threeClickWoldDispaly.setText(newTitle);
break;
case 4:
longPressWoldDispaly.setText(newTitle);
break;
}
}
}
KeySettingAdapter.java
package com.example.myapplication;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class KeySettingAdapter extends RecyclerView.Adapter {
//定义数据成员(KeySetting)类型,用于接收Activitiy调用适配器是传递数据,实现数据共享
private List keyListInAdapter;//数据源:选项列表
private int selectedPosition = -1;//当前选中项的位置,-1表示未选中
//构造函数
public KeySettingAdapter(List keyListInAdapter) {
this.keyListInAdapter = keyListInAdapter;
}
// 1.【定义监听接口】添加点击监听器接口
public interface OnItemClickListener {
void onItemClick(int position, KeySetting item);
}
//2.【设置监听器】将外部传入的监听器对象保存到适配器成员变量中
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
//返回列表项总数
@Override
public int getItemCount() {
return keyListInAdapter.size();
}
//布局:通过inflate方法将列表项item布局编译为view对象,返回以这个对象为参数的ViewHolder对象(就是创建viewHolder)
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.key_setting_item, parent, false);
return new ViewHolder(view);
}
//将数据渲染到列表项的ViewHolder的view控件中(就是绑定数据)
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
//给第position条目 绑定数据(从0开始计数) 对应的数据是keyListInAdapter的position个元素
KeySetting item = keyListInAdapter.get(position);
holder.tvAction.setText(item.getName());
//控制选中图标显示还是隐藏,选中就显示,未选择就隐藏
holder.ivSelected.setVisibility(position == selectedPosition ? View.VISIBLE : View.GONE);
//设置点击事件
//当用户点击某个列表项时,触发回调通知外部
//【触发监听】当列表项的任意区域被点击时
holder.itemView.setOnClickListener(v -> {//为整个列表项设置点击监听器d
int adapterPosition = holder.getAdapterPosition();//获取当前点击项在Adapter中的位置
//检查位置是否有效:adapterPosition != RecyclerView.NO_POSITION
//检查监听器是否已经设置:onItemClickListener != null
if (adapterPosition != RecyclerView.NO_POSITION && onItemClickListener != null) {
//触发回调,传递两个关键数据,点击的位置索引,点击项的数据对象
onItemClickListener.onItemClick(adapterPosition, keyListInAdapter.get(adapterPosition));
}
});
}
// 选中状态管理(自动取消旧项选中状态,严格保证唯一选中项)
public void setSelectedPosition(int position) {
if (selectedPosition != position) {//检查新旧位置是否不同
int oldPosition = selectedPosition;//保存旧位置
selectedPosition = position;//更新当前选中位置
if (oldPosition != -1) {//如果旧位置有效
notifyItemChanged(oldPosition);//就刷新旧位置
}
notifyItemChanged(selectedPosition);//刷新新项UI
}
}
// 获取当前选中项
public int getSelectedPosition() {
return selectedPosition;
}
//新建viewHolder
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvAction;
ImageView ivSelected;
public ViewHolder(@NonNull View itemView) {
super(itemView);
tvAction = itemView.findViewById(R.id.tv_action);
ivSelected = itemView.findViewById(R.id.iv_selected);
}
}
}
浙公网安备 33010602011771号