Android:ItemAddActivity调用finish()返回后,RecyclerView项onClick意外触发
看起来你遇到的这个意外触发点击事件的问题,核心是ViewModel Observer重复注册加上RecyclerView点击事件处理方式的不当导致的,我帮你梳理下问题根源和具体修复步骤:
一、问题根源拆解
重复注册的Observer触发意外回调
在ItemListActivity的viewItemDetail方法中,每次点击图片都会向mViewModel.getAllItems()注册一个新的Observer,但你从未移除过这些Observer。当从ItemAddActivity返回后,ViewModel的条目数据更新,所有之前注册过的Observer(包括之前点击图片时留下的旧Observer)都会被触发,这时候旧Observer里保存的itemPosition还是当时的旧位置,对应到新数据列表里就会跳转到错误的条目,甚至多次触发跳转。XML绑定点击+位置获取的不稳定性
用android:onClick在XML里绑定RecyclerView条目的点击事件,再通过getChildLayoutPosition获取位置的方式,在RecyclerView的视图复用机制下很容易出现位置偏移——尤其是数据更新后视图重绘时,子View的位置和数据列表的位置可能无法准确对应。
二、具体修复步骤
1. 重构RecyclerView点击事件处理(核心修复)
把点击事件的处理逻辑移到Adapter内部,通过接口回调传递点击事件,直接拿到当前点击的Item对象,彻底避免位置偏移和Observer重复注册的问题。
步骤1:给Adapter添加点击回调接口
修改ItemListAdapter,新增点击回调接口并在绑定视图时设置点击事件:
public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ItemViewHolder> { private final LayoutInflater mInflater; private List<Item> mItems; private OnItemClickListener mClickListener; // 定义点击回调接口 public interface OnItemClickListener { void onItemClick(Item clickedItem); } // 设置回调的方法 public void setOnItemClickListener(OnItemClickListener listener) { this.mClickListener = listener; } public ItemListAdapter(Context context) { mInflater = LayoutInflater.from(context); } @Override public void onBindViewHolder(ItemViewHolder holder, int position) { if (mItems != null) { Item currentItem = mItems.get(position); String uriString = "none"; if (currentItem.getImageUri() != null) { uriString = currentItem.getImageUri().toString(); // TODO GlideApp.with(holder.pictureImageView.getContext()).load(uriString).placeholder(R.color.colorPrimaryDark).dontTransform().into(holder.pictureImageView); } else { holder.pictureImageView.setVisibility(View.INVISIBLE); } // 给ImageView绑定点击事件,触发回调 holder.pictureImageView.setOnClickListener(v -> { if (mClickListener != null) { mClickListener.onItemClick(currentItem); } }); } else { // TODO: data not ready yet. } } // 其他原有代码保持不变... }
步骤2:移除XML中的点击绑定
修改RecyclerView项布局item_list_rv_item.xml,删除android:onClick="viewItemDetail":
<ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item_list_rv_image" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimaryDark" android:adjustViewBounds="true" android:padding="5dp" android:layout_margin="3dp" />
步骤3:在Activity中设置Adapter的点击回调
修改ItemListActivity的onCreate方法,移除原有的viewItemDetail方法,改用Adapter的回调跳转详情页:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_item_list); Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar); setSupportActionBar(myToolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mRecyclerView = findViewById(R.id.item_list_rv); final ItemListAdapter adapter = new ItemListAdapter(this); // 设置点击回调,直接跳转到详情页 adapter.setOnItemClickListener(item -> { Intent intent = new Intent(ItemListActivity.this, ItemDetailActivity.class); intent.putExtra("itemId", item.getId()); startActivity(intent); }); mRecyclerView.setAdapter(adapter); mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, LinearLayoutManager.VERTICAL)); mViewModel = ViewModelProviders.of(this).get(ClosetSampleViewModel.class); mViewModel.getAllItems().observe(this, items -> { adapter.setItems(items); }); } // 移除原来的viewItemDetail方法,不再需要
2. 修复ItemAddActivity中的潜在内存泄漏
在ItemAddActivity的onAddItemClicked方法中,你注册了mViewModel.getAllCategories()的Observer但未移除,这会导致内存泄漏,建议补充移除逻辑:
public void onAddItemClicked(View view) { if (currentPhotoPath == null) { Toast.makeText(this, "Can't add item without image.", Toast.LENGTH_LONG).show(); } else { final String itemName = itemNameEditText.getText().toString(); final String itemDescription = itemDescriptionEditText.getText().toString(); final Uri imageUri = Uri.parse(currentPhotoPath); SharedPreferences prefs = getApplicationContext().getSharedPreferences(ITEM_IMAGE_SHARED_PREFS_FILE, MODE_PRIVATE); SharedPreferences.Editor prefsEditor = prefs.edit(); prefsEditor.putString(PREF_PHOTO_PATH, null); prefsEditor.apply(); if (itemCategorySpinner.getSelectedItemPosition() >= 0) { // 创建Observer对象,方便后续移除 Observer<List<Category>> categoryObserver = new Observer<List<Category>>() { @Override public void onChanged(@Nullable List<Category> categories) { addItem(new Item(itemName, itemDescription, imageUri, categories.get(itemCategorySpinner.getSelectedItemPosition()))); // 获取数据后立即移除Observer,避免内存泄漏 mViewModel.getAllCategories().removeObserver(this); } }; mViewModel.getAllCategories().observe(this, categoryObserver); } else { addItem(new Item(itemName, itemDescription, imageUri, null)); } } }
三、修复后的效果
- 从ItemAddActivity返回后,RecyclerView会正常刷新新数据,不会再触发意外的详情页跳转
- 点击图片的跳转逻辑更稳定,不会因为视图复用出现位置错误
- 解决了潜在的内存泄漏问题,代码健壮性提升
内容的提问来源于stack exchange,提问作者user11522513

