使用SearchView与RecyclerView时的数据跳转异常问题
Android全局搜索后跳转数据异常问题
我开发的Android应用包含ViewPager、TabLayout及3个内置RecyclerView的Fragment,需求是通过SearchView对所有Fragment内容做全局搜索,而非仅当前Fragment。但搜索后出现异常:点击第一个搜索结果可正常跳转并携带数据至Buy_Accessories页面,点击第二个结果跳转时无数据传入。注:所有Fragment代码、Adapter逻辑一致,仅Firebase数据库路径不同。
相关代码
ViewpagerActivity
public class Accessories_viewpager extends AppCompatActivity { ViewpagerMainBinding binding; ViewPagerAdapter viewPagerAdapter; ArrayList <Product_Model> allItems = new ArrayList <>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.viewpager_main); binding.tvTitle.setText("اكسسوارات"); getTab(); binding.btnArrowBack.setOnClickListener(v -> { startActivity(new Intent(Accessories_viewpager.this, Nav_Home.class)); finish(); }); binding.btnCart.setOnClickListener(v -> { Intent intent = new Intent(Accessories_viewpager.this, Nav_Home.class); intent.putExtra("cartId", 2); startActivity(intent); finish(); }); binding.btnSearch.setOnClickListener(v -> { }); } private void getTab() { new Handler().post(() -> { viewPagerAdapter = new ViewPagerAdapter(this); viewPagerAdapter.addFragment(BodyAcc_Fragment.getInstance(),"اكسسوارات جسم"); viewPagerAdapter.addFragment(HairAcce_Fragment.getInstance(),"اكسسوارات شعر"); viewPagerAdapter.addFragment(OtherAcce_Fragment.getInstance(),"اكسسوارات اخري"); binding.viewPagerId.setAdapter(viewPagerAdapter); String [] title = new String[]{"اكسسوارات جسم","اكسسوارات شعر","اكسسوارات اخري"}; new TabLayoutMediator(binding.tabLayoutId,binding.viewPagerId,(((tab, position) -> tab.setText(title[position])))).attach(); binding.viewPagerId.setOffscreenPageLimit(3); }); } @Override public void onBackPressed() { super.onBackPressed(); startActivity(new Intent(Accessories_viewpager.this, Nav_Home.class)); finish(); } }
Fragment(以BodyAcc_Fragment为例)
public class BodyAcc_Fragment extends Fragment implements RecyclerView_InterFace { ProductList2Binding binding; private DatabaseReference db; FirebaseAuth mAuth; private ArrayList <Product_Model> list; private Products2_Adapter adapter; public static BodyAcc_Fragment getInstance() { BodyAcc_Fragment mix_fragment = new BodyAcc_Fragment(); return mix_fragment; } @Override public void onAttach(@NonNull Context context) { super.onAttach(context); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = DataBindingUtil.inflate(inflater, R.layout.product_list2, container, false); View view = binding.getRoot(); mAuth = FirebaseAuth.getInstance(); db = FirebaseDatabase.getInstance().getReference(); binding.rvHFSD.setHasFixedSize(true); binding.rvHFSD.setLayoutManager(new GridLayoutManager(getActivity(),2)); binding.searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { adapter.getAllItems(((Accessories_viewpager) getActivity()).allItems); adapter.getFilter().filter(newText); return false; } }); binding.nameOfCategoryTv.setText("اكسسوارات الجسم"); binding.categoryImage.setImageResource(R.drawable.img_accessories1); getList(); return view; } private void getList() { binding.progressBar.setVisibility(View.VISIBLE); db.child("اكسسوارات").child("جسم").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { binding.progressBar.setVisibility(View.GONE); list = new ArrayList <>(); adapter = new Products2_Adapter(getActivity(), list, BodyAcc_Fragment.this); binding.rvHFSD.setAdapter(adapter); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Product_Model menuModel = dataSnapshot.getValue(Product_Model.class); list.add(menuModel); adapter.notifyItemInserted(list.size() - 1); adapter.getItemCount(); } ((Accessories_viewpager) getActivity()).allItems.addAll(list); } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_SHORT).show(); } }); } @Override public void onItemClick(int position) { Product_Model product_model = list.get(position); Intent intent = new Intent(getActivity(), Buy_Accessories.class); intent.putExtra("name", product_model.getOilName()); intent.putExtra("productId", product_model.getProductId()); intent.putExtra("image", product_model.getImageResource()); intent.putExtra("category", "جسم"); startActivity(intent); getActivity().finish(); } }
Products2_Adapter
public class Products2_Adapter extends RecyclerView.Adapter <Products2_Adapter.ViewHolder> implements Filterable { private Context context; public List <Product_Model> list; List<Product_Model> allItems; private List<Product_Model> menuModelListFilter; private RecyclerView_InterFace recyclerView_interFace; public Products2_Adapter(Context context, List <Product_Model> list, RecyclerView_InterFace recyclerView_interFace) { this.context = context; this.list = list; this.recyclerView_interFace = recyclerView_interFace; menuModelListFilter = new ArrayList <>(list); } @NonNull @Override public Products2_Adapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new Products2_Adapter.ViewHolder(LayoutInflater.from(parent.getContext()) .inflate(R.layout.card_menu, parent, false)); } @Override public void onBindViewHolder(@NonNull Products2_Adapter.ViewHolder holder, int position) { Product_Model menuModel = list.get(position); holder.imageName.setText(menuModel.getOilName()); Picasso.get().load(menuModel.getImageResource()).into(holder.image); } @Override public int getItemCount() { return list.size(); } public class ViewHolder extends RecyclerView.ViewHolder { ImageView image; TextView imageName; CardView parentLayout; public ViewHolder(@NonNull View itemView) { super(itemView); image = itemView.findViewById(R.id.image); imageName = itemView.findViewById(R.id.image_name); parentLayout = itemView.findViewById(R.id.RelativeLayout); itemView.setOnClickListener(view -> recyclerView_interFace.onItemClick(getBindingAdapterPosition())); } } @Override public Filter getFilter() { return filter; } private Filter filter = new Filter() { //run on background thread @Override protected FilterResults performFiltering(CharSequence charSequence) { if (menuModelListFilter.size() == 0) menuModelListFilter = new ArrayList<>(list); List<Product_Model> filterList = new ArrayList <>(); if (charSequence ==null || charSequence.length()==0){ // if search text is empty add all original list values to filter list filterList.addAll(menuModelListFilter); }else { String filterPattern = charSequence.toString().toLowerCase().trim(); // Perform search on whole original list for (Product_Model menu : allItems){ if (menu.getOilName().toLowerCase().contains(filterPattern)){ filterList.add(menu); } } } FilterResults results = new FilterResults(); results.values = filterList; return results; } //run on UI thread @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { list.clear(); list.addAll((List) filterResults.values); notifyDataSetChanged(); } }; public void getAllItems(List<Product_Model> allData){ allItems = allData; } }
问题根源与修复方案
核心问题
- 点击数据来源不匹配:Fragment的
onItemClick方法从本地原始列表list中取数据,但搜索过滤后,RecyclerView显示的是Adapter中经过过滤的list集合,两者内容不一致。当搜索结果包含其他Fragment的数据时,当前Fragment的原始列表中没有对应数据,就会出现跳转无数据的情况。 - 搜索逻辑冗余:每次搜索文本变化时都重复给Adapter赋值全量数据,可能导致数据混乱;同时Filter逻辑中
menuModelListFilter的初始化逻辑存在漏洞,清空搜索时可能无法正确恢复原始数据。
修复步骤
1. 修改点击事件传递逻辑,直接传递数据对象
- 更新
RecyclerView_InterFace接口,改为接收Product_Model对象:
public interface RecyclerView_InterFace { void onItemClick(Product_Model product); }
- 修改Adapter的ViewHolder点击事件,直接传递当前过滤后的列表项:
itemView.setOnClickListener(view -> recyclerView_interFace.onItemClick(list.get(getBindingAdapterPosition())));
2. 更新Fragment的点击处理逻辑
在Fragment的onItemClick方法中,直接使用传递过来的对象,无需再从本地列表获取:
@Override public void onItemClick(Product_Model product_model) { Intent intent = new Intent(getActivity(), Buy_Accessories.class); intent.putExtra("name", product_model.getOilName()); intent.putExtra("productId", product_model.getProductId()); intent.putExtra("image", product_model.getImageResource()); // 注意:需在Product_Model中添加category字段,从Firebase获取数据时同步赋值,避免硬编码分类 intent.putExtra("category", product_model.getCategory()); startActivity(intent); getActivity().finish(); }
3. 优化搜索过滤逻辑
- 修正Fragment中搜索文本变化的处理,避免重复赋值全量数据:
@Override public boolean onQueryTextChange(String newText) { // 仅在全量数据未初始化时赋值 if (adapter.allItems == null) { adapter.getAllItems(((Accessories_viewpager) getActivity()).allItems); } adapter.getFilter().filter(newText); return false; }
- 修复Adapter的Filter逻辑,确保清空搜索时恢复当前Fragment的原始数据:
private Filter filter = new Filter() { @Override protected FilterResults performFiltering(CharSequence charSequence) { List<Product_Model> filterList = new ArrayList<>(); if (charSequence == null || charSequence.length() == 0) { // 清空搜索时恢复当前Fragment的原始数据 filterList.addAll(menuModelListFilter); } else { String filterPattern = charSequence.toString().toLowerCase().trim(); // 全局搜索遍历全量数据 if (allItems != null) { for (Product_Model menu : allItems) { if (menu.getOilName().toLowerCase().contains(filterPattern)) { filterList.add(menu); } } } } FilterResults results = new FilterResults(); results.values = filterList; return results; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { list.clear(); list.addAll((List) filterResults.values); notifyDataSetChanged(); } };
4. 完善Product_Model数据结构
在Product_Model中添加category字段及对应的getter/setter方法,同时在Firebase数据库中为每个产品添加分类属性,确保跳转时能正确传递分类信息,避免硬编码。
内容的提问来源于stack exchange,提问作者Taha Younis
相关产品推荐
相关产品推荐

