searchview viewmodel

74 阅读4分钟

解决方案(最小修改):

1. 给CountryAdapter添加一个updateData方法:

public class ClientListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
    // 添加操作监听接口
    public interface OnDeviceActionListener {
        void onBlockDevice(String deviceName, int position);
        void onDeleteDevice(String deviceName, int position);
    }
    
    private OnDeviceActionListener listener;
    
    // 设置监听器
    public void setOnDeviceActionListener(OnDeviceActionListener listener) {
        this.listener = listener;
    }
}

// 在onBindViewHolder中修改长按事件
switch (status) {
    case 0: // 在线设备
        lineItem.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                TPPopupMenu popupMenu = new TPPopupMenu(lineItem.getContext(), v, R.menu.menu_block)
                    .setIconEnable(true);
                
                // 添加菜单项点击监听器
                popupMenu.setOnMenuItemClickListener(item -> {
                    if (item.getItemId() == R.id.menu_block) {
                        showBlockConfirmDialog(deviceName, position);
                        return true;
                    }
                    return false;
                });
                
                popupMenu.show();
                return true;
            }
        });
        break;
        
    case 1: // 离线设备
        lineItem.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                TPPopupMenu popupMenu = new TPPopupMenu(lineItem.getContext(), v, R.menu.menu_delete)
                    .setIconEnable(true);
                
                popupMenu.setOnMenuItemClickListener(item -> {
                    if (item.getItemId() == R.id.menu_block) {
                        showBlockConfirmDialog(deviceName, position);
                        return true;
                    } else if (item.getItemId() == R.id.menu_delete) {
                        showDeleteConfirmDialog(deviceName, position);
                        return true;
                    }
                    return false;
                });
                
                popupMenu.show();
                return true;
            }
        });
        break;
}

// 显示拉黑确认对话框
private void showBlockConfirmDialog(String deviceName, int position) {
    new androidx.appcompat.app.AlertDialog.Builder(context)
        .setTitle("拉黑设备")
        .setMessage("确定要将 \"" + deviceName + "\" 加入黑名单吗?")
        .setPositiveButton("确定", (dialog, which) -> {
            if (listener != null) {
                listener.onBlockDevice(deviceName, position);
            }
        })
        .setNegativeButton("取消", null)
        .show();
}

// 显示删除确认对话框
private void showDeleteConfirmDialog(String deviceName, int position) {
    new androidx.appcompat.app.AlertDialog.Builder(context)
        .setTitle("删除设备")
        .setMessage("确定要删除 \"" + deviceName + "\" 吗?")
        .setPositiveButton("确定", (dialog, which) -> {
            if (listener != null) {
                listener.onDeleteDevice(deviceName, position);
            }
        })
        .setNegativeButton("取消", null)
        .show();
}// ClientMainFragment 和 ClientGuestFragment 都需要实现
public class ClientMainFragment extends BaseMvvmFragment<FragmentClientsMainBinding> 
    implements ClientListAdapter.OnDeviceActionListener {
    
    private void initView() {
        onlineAdapter = new ClientListAdapter(
            viewModel.getCurrentMainOnlineCount(), 
            requireActivity().getBaseContext(), 
            true
        );
        onlineAdapter.setOnDeviceActionListener(this); // 设置监听器
        viewBinding.rvOnline.setAdapter(onlineAdapter);
        
        // 离线adapter也要设置
        offlineAdapter.setOnDeviceActionListener(this);
    }
    
    @Override
    public void onBlockDevice(String deviceName, int position) {
        viewModel.blockDevice(deviceName);
        Toast.makeText(getContext(), deviceName + " 已加入黑名单", Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onDeleteDevice(String deviceName, int position) {
        Toast.makeText(getContext(), deviceName + " 已删除", Toast.LENGTH_SHORT).show();
    }
}

111
public class ClientMainFragment extends BaseMvvmFragment<FragmentClientsMainBinding> 
    implements ClientListAdapter.OnDeviceActionListener {
    
    // ... 现有代码保持不变 ...
    
    @Override
    protected void subscribeViewModel(@Nullable Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireActivity()).get(ClientViewModel.class);
        initView();
        observeViewModel(); // 添加观察方法
    }
    
    private void observeViewModel() {
        viewModel.getMainOnlineCount().observe(this, onlineCount -> {
            if (onlineCount != null && onlineAdapter != null) {
                // 如果数量变化,重新创建adapter
                onlineAdapter = new ClientListAdapter(onlineCount, requireActivity(), true);
                onlineAdapter.setOnDeviceActionListener(this);
                viewBinding.rvOnline.setAdapter(onlineAdapter);
            }
            checkAndShowEmptyState();
        });
        
        viewModel.getMainOfflineCount().observe(this, offlineCount -> {
            if (offlineCount != null && offlineAdapter != null) {
                offlineAdapter = new ClientListAdapter(offlineCount, requireActivity(), false);
                offlineAdapter.setOnDeviceActionListener(this);
                viewBinding.rvOffline.setAdapter(offlineAdapter);
            }
            checkAndShowEmptyState();
        });
    }
    
    private void checkAndShowEmptyState() {
        Integer onlineCount = viewModel.getMainOnlineCount().getValue();
        Integer offlineCount = viewModel.getMainOfflineCount().getValue();
        
        boolean isEmpty = (onlineCount == null || onlineCount == 0) && 
                         (offlineCount == null || offlineCount == 0);
        
        if (isEmpty) {
            showEmptyView();
        } else {
            showDeviceList();
        }
    }
    
    private void showEmptyView() {
        viewBinding.rvOnline.setVisibility(View.GONE);
        viewBinding.rvOffline.setVisibility(View.GONE);
        viewBinding.emptyLayout.setVisibility(View.VISIBLE);
        viewBinding.emptyImage.setImageResource(R.drawable.empty_device);
        viewBinding.emptyText.setText("没有设备记录");
    }
    
    private void showDeviceList() {
        viewBinding.rvOnline.setVisibility(View.VISIBLE);
        viewBinding.rvOffline.setVisibility(View.VISIBLE);
        viewBinding.emptyLayout.setVisibility(View.GONE);
    }
    
    @Override
    public void onBlockDevice(String deviceName, int position, boolean isOnlineAdapter) {
        viewModel.blockDevice(deviceName);
        
        // 更新对应的设备数量
        if (isOnlineAdapter) {
            viewModel.updateMainOnlineCount(viewModel.getCurrentMainOnlineCount() - 1);
        } else {
            viewModel.updateMainOfflineCount(viewModel.getCurrentMainOfflineCount() - 1);
        }
        
        Toast.makeText(getContext(), deviceName + " 已加入黑名单", Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onDeleteDevice(String deviceName, int position, boolean isOnlineAdapter) {
        if (isOnlineAdapter) {
            viewModel.updateMainOnlineCount(viewModel.getCurrentMainOnlineCount() - 1);
        } else {
            viewModel.updateMainOfflineCount(viewModel.getCurrentMainOfflineCount() - 1);
        }
        
        Toast.makeText(getContext(), deviceName + " 已删除", Toast.LENGTH_SHORT).show();
    }
}

public class ClientGuestFragment extends BaseMvvmFragment<FragmentClientsGuestBinding> 
    implements ClientListAdapter.OnDeviceActionListener {
    
    @Override
    protected void subscribeViewModel(@Nullable Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireActivity()).get(ClientViewModel.class);
        initView();
        observeViewModel();
    }
    
    private void observeViewModel() {
        viewModel.getGuestCount().observe(this, guestCount -> {
            if (guestCount != null && adapter != null) {
                adapter = new ClientListAdapter(guestCount, requireActivity(), true);
                adapter.setOnDeviceActionListener(this);
                viewBinding.rvOnline.setAdapter(adapter);
            }
            
            if (guestCount == null || guestCount == 0) {
                showEmptyView();
            } else {
                showDeviceList();
            }
        });
    }
    
    private void showEmptyView() {
        viewBinding.rvOnline.setVisibility(View.GONE);
        viewBinding.emptyLayout.setVisibility(View.VISIBLE);
        viewBinding.emptyImage.setImageResource(R.drawable.empty_device);
        viewBinding.emptyText.setText("没有设备记录");
    }
    
    private void showDeviceList() {
        viewBinding.rvOnline.setVisibility(View.VISIBLE);
        viewBinding.emptyLayout.setVisibility(View.GONE);
    }
    
    @Override
    public void onBlockDevice(String deviceName, int position, boolean isOnlineAdapter) {
        viewModel.blockDevice(deviceName);
        viewModel.updateGuestCount(viewModel.getCurrentGuestCount() - 1);
        Toast.makeText(getContext(), deviceName + " 已加入黑名单", Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onDeleteDevice(String deviceName, int position, boolean isOnlineAdapter) {
        viewModel.updateGuestCount(viewModel.getCurrentGuestCount() - 1);
        Toast.makeText(getContext(), deviceName + " 已删除", Toast.LENGTH_SHORT).show();
    }
}
// 在CountryAdapter类中添加这个方法
public void updateData(List<String> newCountryList) {
    this.countryList.clear();
    this.countryList.addAll(newCountryList);
    
    // 重新计算选中位置,基于国家名称
    selectedPosition = -1;
    for (int i = 0; i < countryList.size(); i++) {
        if (countryList.get(i).equals(selectedCountryName)) {
            selectedPosition = i;
            break;
        }
    }
    
    notifyDataSetChanged();
}

2. 修改Activity中的setupCountrySelectionUI方法:

java

private void setupCountrySelectionUI(View contentView, TPModalBottomSheet modal) {
    RecyclerView recyclerView = contentView.findViewById(R.id.recyclerView_countries);
    CountryAdapter countryAdapter = new CountryAdapter(countryViewModel.getAllCountries());
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(countryAdapter);

    // 设置当前选中的国家
    String currentCountry = countryViewModel.getCurrentSelectedCountry();
    if (currentCountry != null && !currentCountry.trim().isEmpty()) {
        countryAdapter.setSelectedCountry(currentCountry);
    }

    // 设置国家点击监听
    countryAdapter.setOnCountryClickListener(new CountryAdapter.OnCountryClickListener() {
        @Override
        public void onCountryClick(String countryName, int position) {
            countryViewModel.selectCountry(countryName);
            modal.dismiss();
        }
    });

    // ★ 关键修改:取消注释并启用这个观察者
    countryViewModel.filteredCountries.observe(this, countries -> {
        countryAdapter.updateData(countries); // 使用新添加的updateData方法
    });

    // 设置搜索功能
    setupSearchView(contentView, countryAdapter, modal);
}

3. 简化setupSearchView方法:

java

private void setupSearchView(View contentView, CountryAdapter countryAdapter, TPModalBottomSheet modal) {
    TPSearchBar tpSearchBar = contentView.findViewById(R.id.tp_search);

    // 创建搜索建议数据
    ArrayList<String> searchData = new ArrayList<>(countryViewModel.getAllCountries());

    // 创建搜索适配器
    SearchViewBaseAdapter<String> searchAdapter = new SearchViewBaseAdapter<String>(
            searchData,
            new SearchViewBaseAdapter.OnBindDataListener<String>() {
                @Override
                public void onBindViewHolder(String model, SearchViewBaseViewHolder viewHolder, int type, int position) {
                    viewHolder.setText(R.id.tv_country_name, model);
                }

                @Override
                public int getLayoutId(int type) {
                    return R.layout.country_item;
                }
            }
    );

    // 设置搜索结果点击事件
    searchAdapter.setItemClickListener(new SearchViewBaseAdapter.OnItemClickListener<String>() {
        @Override
        public void onClick(View view, String data) {
            countryViewModel.selectCountry(data);
            tpSearchBar.dismissSearchView();
            countryViewModel.clearSearch(); // 这会触发filteredCountries更新,从而更新RecyclerView
            modal.dismiss();
        }
    });

    // ★ 关键修改:移除countryAdapter.filterCountries调用
    tpSearchBar.setTextChangeListener(new TPMaterialSearchView.TextChangeListener() {
        @Override
        public void onTextChange(String searchText) {
            // 只通过ViewModel处理搜索过滤
            countryViewModel.filterCountries(searchText);
            
            // 删除这一行:countryAdapter.filterCountries(searchText);

            // 过滤搜索建议
            if (searchText == null || searchText.trim().isEmpty()) {
                searchAdapter.updateData(new ArrayList<>(countryViewModel.getAllCountries()));
            } else {
                ArrayList<String> filteredList = getFilteredCountries(searchText);
                searchAdapter.updateData(filteredList);
            }
        }
    });

    tpSearchBar.setManager(getSupportFragmentManager());
    tpSearchBar.setSearchViewAdapter(searchAdapter);

    // 其余代码保持不变...
}

工作流程:

  1. 用户在搜索框输入文字
  2. countryViewModel.filterCountries(searchText) 被调用
  3. ViewModel更新filteredCountries
  4. 观察者countryViewModel.filteredCountries.observe被触发
  5. 调用countryAdapter.updateData(countries)更新RecyclerView
  6. 用户在搜索结果中选择国家,点击搜索建议
  7. 调用countryViewModel.clearSearch()清除搜索
  8. 再次触发观察者,RecyclerView回到完整列表,且保持选中状态

总结:

你的CountryAdapter写得很好,只需要加一个updateData方法。主要问题是Activity中没有正确连接ViewModel和Adapter,导致ViewModel的状态变化没有传递给UI。通过添加观察者和updateData方法,就能实现你想要的功能:搜索后选择国家,能够正确回到主列表并显示选中状态。