软键盘开启时应用列表RecyclerView无法实时更新问题
问题描述
应用支持搜索已安装应用,软键盘关闭时搜索结果正常,但软键盘开启时RecyclerView无法实时更新;使用模拟器物理键盘则能正常刷新。预期软键盘输入时列表能实时更新,实际输入时列表空白,关闭键盘后才显示结果。
提供的代码
Fragment类代码
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); recyclerView = view.findViewById(R.id.justtype_view); layoutManager = new GridLayoutManager(getContext(), 4); recyclerView.setLayoutManager(layoutManager); jt = view.findViewById(R.id.jtInput); jt.requestFocus(); jt.isFocusableInTouchMode(); jt.setFocusable(true); if (jt.requestFocus()) { InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(jt, InputMethodManager.SHOW_IMPLICIT); } Window w = requireActivity().getWindow(); w.setStatusBarColor(ContextCompat.getColor(requireActivity(), R.color.status)); recyclerView.setVisibility(View.INVISIBLE); jtAdapter = new JTAdapter(requireContext(), jt.getText().toString()); recyclerView.setAdapter(jtAdapter); new Thread(this::contactsSearch).start(); jt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (jt.getText().toString().length()>0) { jtAdapter = new JTAdapter(requireContext(), s.toString()); recyclerView.setAdapter(jtAdapter); jtAdapter.notifyDataSetChanged(); recyclerView.setVisibility(View.VISIBLE); textChange(view); } else { recyclerView.setVisibility(View.INVISIBLE); } } }); }
JTAdapter类代码
public class JTAdapter extends RecyclerView.Adapter<JTAdapter.ViewHolder> { public static List<AppInfo> jtList; public JTAdapter(Context c, String q) { new Thread(() -> { PackageManager pm = c.getPackageManager(); jtList = new ArrayList<>(); Intent i = new Intent(Intent.ACTION_MAIN, null); i.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> allApps = pm.queryIntentActivities(i, 0); if(q.length() == 0) { for (ResolveInfo ri : allApps) { AppInfo app = new AppInfo(); app.label = ri.loadLabel(pm); app.packageName = ri.activityInfo.packageName; app.icon = ri.activityInfo.loadIcon(pm); jtList.add(app); } jtList.sort(Comparator.comparing(o -> o.label.toString())); } else { for (ResolveInfo ri : allApps) { AppInfo app = new AppInfo(); app.label = ri.loadLabel(pm); app.packageName = ri.activityInfo.packageName; app.icon = ri.activityInfo.loadIcon(pm); if(app.label.toString().toLowerCase().startsWith(q)) { jtList.add(app); } } jtList.sort(Comparator.comparing(o -> o.label.toString())); } }).start(); } @Override public int getItemCount() { return jtList.size(); } public void onBindViewHolder(@NonNull JTAdapter.ViewHolder viewHolder, int i) { try { String appLabel = jtList.get(i).label.toString(); Drawable appIcon = jtList.get(i).icon; TextView textView = viewHolder.textView; textView.setText(appLabel); ImageView imageView = viewHolder.img; imageView.setImageDrawable(appIcon); } catch (Exception e) { e.printStackTrace(); } } @NonNull public JTAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.jt_item_row_layout, parent, false); return new ViewHolder(view); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ImageView img; public ViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.jt_app_name); img = itemView.findViewById(R.id.jt_app_icon); itemView.setOnClickListener(v -> { int pos = getAdapterPosition(); Context context = v.getContext(); Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(jtList.get(pos).packageName.toString()); context.startActivity(launchIntent); }); } } }
修复方案
1. 重构JTAdapter,解决线程更新与静态列表问题
public class JTAdapter extends RecyclerView.Adapter<JTAdapter.ViewHolder> { private List<AppInfo> jtList; private Context context; private String query; public JTAdapter(Context c, String q) { context = c; query = q; jtList = new ArrayList<>(); loadAppData(); } private void loadAppData() { new Thread(() -> { PackageManager pm = context.getPackageManager(); Intent i = new Intent(Intent.ACTION_MAIN, null); i.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> allApps = pm.queryIntentActivities(i, 0); jtList.clear(); if(query.length() == 0) { for (ResolveInfo ri : allApps) { AppInfo app = new AppInfo(); app.label = ri.loadLabel(pm); app.packageName = ri.activityInfo.packageName; app.icon = ri.activityInfo.loadIcon(pm); jtList.add(app); } jtList.sort(Comparator.comparing(o -> o.label.toString())); } else { String lowerQuery = query.toLowerCase(); for (ResolveInfo ri : allApps) { AppInfo app = new AppInfo(); app.label = ri.loadLabel(pm); app.packageName = ri.activityInfo.packageName; app.icon = ri.activityInfo.loadIcon(pm); if(app.label.toString().toLowerCase().startsWith(lowerQuery)) { jtList.add(app); } } jtList.sort(Comparator.comparing(o -> o.label.toString())); } // 回到UI线程通知Adapter更新数据 ((Activity) context).runOnUiThread(this::notifyDataSetChanged); }).start(); } @Override public int getItemCount() { return jtList.size(); } @Override public void onBindViewHolder(@NonNull JTAdapter.ViewHolder viewHolder, int i) { try { AppInfo app = jtList.get(i); viewHolder.textView.setText(app.label.toString()); viewHolder.img.setImageDrawable(app.icon); } catch (Exception e) { e.printStackTrace(); } } @NonNull @Override public JTAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.jt_item_row_layout, parent, false); return new ViewHolder(view); } // 新增更新查询的方法,避免频繁创建Adapter实例 public void updateQuery(String newQuery) { query = newQuery; loadAppData(); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ImageView img; public ViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.jt_app_name); img = itemView.findViewById(R.id.jt_app_icon); itemView.setOnClickListener(v -> { int pos = getAdapterPosition(); if(pos != RecyclerView.NO_POSITION) { Context context = v.getContext(); Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(jtList.get(pos).packageName.toString()); if(launchIntent != null) { context.startActivity(launchIntent); } } }); } } }
2. 修改Fragment逻辑,复用Adapter实例
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); recyclerView = view.findViewById(R.id.justtype_view); layoutManager = new GridLayoutManager(getContext(), 4); recyclerView.setLayoutManager(layoutManager); jt = view.findViewById(R.id.jtInput); jt.requestFocus(); jt.setFocusableInTouchMode(true); jt.setFocusable(true); if (jt.requestFocus()) { InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(jt, InputMethodManager.SHOW_IMPLICIT); } Window w = requireActivity().getWindow(); w.setStatusBarColor(ContextCompat.getColor(requireActivity(), R.color.status)); recyclerView.setVisibility(View.INVISIBLE); // 仅初始化一次Adapter jtAdapter = new JTAdapter(requireContext(), jt.getText().toString()); recyclerView.setAdapter(jtAdapter); new Thread(this::contactsSearch).start(); jt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { String query = s.toString().trim(); if (query.length() > 0) { // 复用现有Adapter,更新查询条件 jtAdapter.updateQuery(query); recyclerView.setVisibility(View.VISIBLE); textChange(view); } else { recyclerView.setVisibility(View.INVISIBLE); } } }); }
问题根源说明
- 静态列表竞态:原代码中
jtList为静态变量,每次新建Adapter都会覆盖列表,线程加载数据时出现数据混乱。 - 未在UI线程更新:数据加载完成后未回到UI线程调用
notifyDataSetChanged(),软键盘输入时UI未收到更新通知,不会刷新列表;关闭键盘后UI重绘,此时数据已加载完成,才显示结果。 - 频繁创建Adapter:每次输入都新建Adapter,导致RecyclerView重复初始化,且数据加载滞后于Adapter设置,造成列表空白。
内容的提问来源于stack exchange,提问作者Andrew Hunt
相关产品推荐
相关产品推荐

