ListView内EditText失焦时用户输入被清空的原因排查
解决ListView中EditText焦点异常与内容丢失问题
你的问题根源在于ListView适配器的getView方法没有正确实现视图复用,同时输入内容未同步到数据源Model中,加上焦点管理不当,导致了焦点跳变和内容丢失。下面是具体的修复方案:
核心问题拆解
- 视图完全未复用:每次调用
getView都重新inflate布局,完全废弃了ListView的视图复用机制。滚动时旧视图被销毁重建,之前输入的内容自然丢失,焦点也会因视图重建出现异常。 - 输入内容未持久化:EditText的输入只停留在View层面,没有更新到对应的
ProductionLineModel对象中。重建视图时会从Model读取初始值,覆盖用户输入。 - 临时状态未绑定到Model:非"GECLAIMED"分支里的
PalletUpdated是局部数组,会被复用的视图共享,导致状态混乱。
修复步骤与代码示例
1. 实现ViewHolder模式复用视图
把视图引用缓存到ViewHolder中,避免重复findViewById和inflate:
static class ViewHolder { TextView tvitemcode; TextView tvItemDesc; TextView tvPalletQty; EditText etvLocation; EditText barcode; TextView tvProgress; TextView textView; }
2. 修改getView方法,复用ConvertView
@NonNull @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; ProductionLineModel model = getItem(position); // 复用视图,避免重复inflate if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(mContext); convertView = inflater.inflate(mResource, parent, false); holder = new ViewHolder(); // 初始化所有视图引用 holder.tvitemcode = convertView.findViewById(R.id.textView37); holder.tvItemDesc = convertView.findViewById(R.id.textView38); holder.tvPalletQty = convertView.findViewById(R.id.textView39); holder.etvLocation = convertView.findViewById(R.id.PROD_location_etv); holder.barcode = convertView.findViewById(R.id.PROD_barcode); holder.tvProgress = convertView.findViewById(R.id.Progress_tv); holder.textView = convertView.findViewById(R.id.textView40); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 填充基础数据 holder.tvitemcode.setText(model.getItemcode()); holder.tvItemDesc.setText(model.getItemdescription()); holder.tvPalletQty.setText(model.getPalletqty()); if (Objects.equals(status, "GECLAIMED")) { holder.barcode.setVisibility(View.VISIBLE); holder.tvProgress.setVisibility(View.GONE); // 从Model读取保存的输入内容 holder.etvLocation.setText(model.getLocation()); holder.barcode.setText(model.getBarcodeInput()); // 输入监听:同步内容到Model,优化焦点处理 holder.barcode.setOnEditorActionListener((v, actionId, event) -> { boolean isEnterEvent = event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN; if (actionId == EditorInfo.IME_ACTION_DONE || isEnterEvent) { String newScan = holder.barcode.getText().toString(); model.setBarcodeInput(newScan); // 同步输入内容到Model if (!newScan.isEmpty()) { if (Objects.equals(model.getPalletcode(), newScan)) { if (dataBaseHelper.UpdateProdPallet(model.getPalletcode(), model.getOrderNummer())) { Toast.makeText(mContext, "Pallet geaccepteerd", Toast.LENGTH_SHORT).show(); Log.d("DEBUG", "Pallet accepted"); } else { Toast.makeText(mContext, "Pallet NIET geaccepteerd", Toast.LENGTH_SHORT).show(); Log.e("DEBUG", "Pallet NOT accepted due: Database"); } } else { Toast.makeText(mContext, "Pallet NIET geaccepteerd", Toast.LENGTH_SHORT).show(); Log.e("DEBUG", "Pallet NOT accepted due: Codes not equal"); } } else { Toast.makeText(mContext, "Scan eerst een barcode", Toast.LENGTH_SHORT).show(); Log.e("DEBUG", "Pallet NOT accepted due: Nothing inserted"); } // 隐藏键盘并清除焦点,避免焦点跳变 InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); v.clearFocus(); return true; } return false; }); } else { holder.barcode.setVisibility(View.INVISIBLE); holder.tvProgress.setVisibility(View.VISIBLE); // 从Model读取保存的输入内容和状态 holder.etvLocation.setText(model.getLocationInput()); if (model.isPalletUpdated()) { holder.tvProgress.setText("Scan uw locatie"); holder.textView.setText("Locatie"); } else { holder.tvProgress.setText("Scan uw pallet"); holder.textView.setText("Barcode"); } holder.etvLocation.setOnEditorActionListener((v, actionId, event) -> { boolean isEnterEvent = event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN; if (actionId == EditorInfo.IME_ACTION_DONE || isEnterEvent) { String newScan = holder.etvLocation.getText().toString(); model.setLocationInput(newScan); // 同步输入内容到Model if (!newScan.isEmpty()) { if (Objects.equals(model.getPalletcode(), newScan)) { if (!model.isPalletUpdated()) { if (dataBaseHelper.UpdateProdPallet(model.getPalletcode(), model.getOrderNummer())) { Toast.makeText(mContext, "Pallet geaccepteerd", Toast.LENGTH_SHORT).show(); model.setPalletUpdated(true); // 更新Model状态 holder.tvProgress.setText("Scan uw locatie"); holder.textView.setText("Locatie"); } else { Toast.makeText(mContext, "Pallet NIET geaccepteerd", Toast.LENGTH_SHORT).show(); } } else { if (dataBaseHelper.UpdateProdPallet(model.getPalletcode(), model.getOrderNummer())) { Toast.makeText(mContext, "Locatie verstuurd", Toast.LENGTH_SHORT).show(); model.setPalletUpdated(false); // 更新Model状态 holder.tvProgress.setText("Scan uw pallet"); holder.textView.setText("Barcode"); } else { Toast.makeText(mContext, "Pallet NIET geaccepteerd", Toast.LENGTH_SHORT).show(); } } } else { Toast.makeText(mContext, "Pallet NIET geaccepteerd", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(mContext, "Scan eerst een barcode", Toast.LENGTH_SHORT).show(); } // 隐藏键盘并清除焦点 InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); v.clearFocus(); return true; } return false; }); } return convertView; }
3. 更新ProductionLineModel类
新增存储输入内容和状态的字段,确保数据持久化:
public class ProductionLineModel { // 原有字段... private String barcodeInput; private String locationInput; private boolean isPalletUpdated; // 新增getter和setter public String getBarcodeInput() { return barcodeInput; } public void setBarcodeInput(String barcodeInput) { this.barcodeInput = barcodeInput; } public String getLocationInput() { return locationInput; } public void setLocationInput(String locationInput) { this.locationInput = locationInput; } public boolean isPalletUpdated() { return isPalletUpdated; } public void setPalletUpdated(boolean palletUpdated) { isPalletUpdated = palletUpdated; } // 原有构造方法... }
额外优化建议
- 给ListView添加焦点管理属性,避免Item布局抢焦点:
<ListView android:id="@+id/Lines_lv" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="beforeDescendants"/>
- 确保硬件扫描器的输入不会触发额外焦点请求,EditText需保留
android:focusable="true"和android:focusableInTouchMode="true"属性,但不要在代码中强制调用requestFocus()。
内容的提问来源于stack exchange,提问作者Joossieπ
相关产品推荐
相关产品推荐

