安卓编写---在适配器中采用监听器

根据以下的例子,来学习怎么在适配器中怎么使用监听器。

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

posted on 2025-10-27 16:39  blfbuaa  阅读(3)  评论(0)    收藏  举报