当前位置: 首页 > 知识库问答 >
问题:

React Native中的RecyclerView:notifyItemInserted()和notifyDataSetChanged()不起作用

田慈
2023-03-14

我正在尝试在React原生应用程序中集成一个支持Firebase的RecolyerView。对于硬编码的数据,它工作得很好,但是在插入动态加载的行并在RecyclerView.Adapter上调用notifyItemInserted()或notifyDataSetChanged()时,RecyclerView本身并不反映更改。在点击应用程序的“刷新”按钮之前,这会显示为一个初始的空白视图,其唯一的效果是从React本机端重新呈现RecolyerView,或者直到滚动或屏幕方向发生变化。

在阅读了这个站点上的十几个或更多关于不涉及React Native的类似问题的问题,并且尝试了大多数常见的解决方案之后,我怀疑这个问题与React Native有关。

鉴于React Native的list view实现正在出现的性能问题,找到一个解决方案对许多人都是有帮助的。

public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {

    ArrayList<Post> mDataset;

    public PostsAdapter(ArrayList<Post> list){
        mDataset = list;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public LinearLayout mPostView;
        public ViewHolder(LinearLayout v) {
            super(v);
            mPostView = v;
        }
    }

    @Override
    public PostsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
                .inflate(R.layout.post, parent, false);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        TextView tv = (TextView) holder.mPostView.findViewById(R.id.title);
        tv.setText(mDataset.get(position).title);
    }

    @Override
    public int getItemCount() {
        return mDataset.size();
    }

}

 public class RNPostsViewManager extends SimpleViewManager{
    private ArrayList<Post> mDataset = new ArrayList<>();
    public static final String REACT_CLASS = "AndroidPostsView";
    private RecyclerView mRecyclerView;
    private PostsAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private DatabaseReference mDatabase;
    private Query initialDataQuery;
    private ChildEventListener initialDataListener;

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @UiThread
    public void addPost (Post p){
        mDataset.add(p);
        mAdapter.notifyItemInserted(mDataset.size()-1);
    }

    @Override
    public RecyclerView createViewInstance( ThemedReactContext context) {

        mAdapter = new PostsAdapter(mDataset);

        mRecyclerView = new RecyclerView(context){
            @Override
            protected void onAttachedToWindow() {
                super.onAttachedToWindow();
                initialDataQuery.addChildEventListener(initialDataListener);
            }
        };

        mLayoutManager = new LinearLayoutManager(context.getCurrentActivity(), LinearLayoutManager.VERTICAL, false);
        DividerItemDecoration mDecoration = new DividerItemDecoration(context, 1);
        mDecoration.setDrawable(ContextCompat.getDrawable(context.getCurrentActivity(), R.drawable.sep));
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.addItemDecoration(mDecoration);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setAdapter(mAdapter);

        mDatabase = FirebaseDatabase.getInstance().getReference();
        initialDataQuery = mDatabase.child("wp-posts").orderByChild("unixPostDate").limitToFirst(100);
        initialDataListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
                Post p = dataSnapshot.getValue(Post.class);
                addPost(p);
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
            }
            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
            }
            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        };

        return mRecyclerView;
    }

}

// layout file post.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"

    android:background="@android:color/background_light"
    android:clickable="true"
    android:focusable="true"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:padding="10dp"
    android:weightSum="1">

    <Button
        android:id="@+id/button"
        style="@style/Widget.AppCompat.Button.Small"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="20dp"
        android:background="@android:drawable/ic_menu_more"
        android:gravity="center_vertical" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="fill_vertical"
        android:orientation="horizontal"
        android:weightSum="1">

        <TextView
            android:id="@+id/title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="fill_vertical"
            android:layout_weight="1"
            android:gravity="top"
            android:text="TextView"
            android:textColor="@color/title"
            android:textStyle="bold" />
    </LinearLayout>

</LinearLayout>

共有1个答案

伏子辰
2023-03-14

问题是,当recyclerView是本机UI组件时,requestlayout不能很好地工作。

下面的黑客攻击使所有这些问题不了了之:

我现在覆盖了recyclerView中的requestLayout方法。然后,在任何notify*方法、甚至scrolltoposition调用或任何调用重新布局的方法之前,我允许自定义的requestlayout方法强制重新布局。

最终结果看起来是这样的:

private boolean mRequestedLayout = false;


public void aMethodThatUpdatesStuff(int indexToUpdate, ReadableMap updatedChild) {
    final SPAdapter adapter = (SPAdapter) getAdapter();
    mRequestedLayout = false;
    adapter.updateDataAtIndex(indexToUpdate, updatedChild); // <-- this runs notifyItemChanged inside
}

@Override
public void requestLayout() {
    super.requestLayout();
    // We need to intercept this method because if we don't our children will never update
    // Check https://stackoverflow.com/questions/49371866/recyclerview-wont-update-child-until-i-scroll
    if (!mRequestedLayout) {
        mRequestedLayout = true;
        this.post(new Runnable() {
            @SuppressLint("WrongCall")
            @Override
            public void run() {
                mRequestedLayout = false;
                layout(getLeft(), getTop(), getRight(), getBottom());
                onLayout(false, getLeft(), getTop(), getRight(), getBottom());
            }
        });
    }
}
 类似资料:
  • 问题内容: 我试图在FlatList 上调用函数,但无法正常工作。 我在最后打电话给我,它没有更新。我想稍后在无限滚动中使用此逻辑,但现在无法使其工作。 问题答案: 您在FlatList中寻找的属性是onEndReachedThreshold而不是onEndThreshold。

  • 我延长了 当我打电话的时候: 什么也没发生。 刷新视图的唯一方法是再次设置适配器(请参见此答案): 我对此解决方案有两个问题: 当我再次设置适配器时,我可以看到屏幕上有一个闪烁 listview返回第一个位置。 有什么想法吗?

  • 我有Class for在我的自定义项目中。现在我有,并且Tow Image Button(Edit and Delete)在我的行中。当我启动删除查询时,从GetView()并调用但那里什么也没发生。请参阅下面的代码: 公共视图 getView(最终整型位置,视图转换视图,视图组父视图) { 这里不工作……请任何人告诉我我在哪里做mistack?

  • 问题内容: 我现在有两个类:RemindersDAO.java和ViewLocalReminders.java。 我试图访问ViewLocalReminders.java中的变量,并且试图从RemindersDAO.java调用它。我通过使用getter / setter方法组合来做到这一点。但是,由于某种原因,我的变量值在getter方法中一直设置为0。这是代码: ViewLocalRemind

  • 问题内容: 我现在有两个类:RemindersDAO.java和ViewLocalReminders.java。 我试图访问ViewLocalReminders.java中的变量,并且试图从RemindersDAO.java调用它。我通过使用getter / setter方法组合来做到这一点。但是,由于某种原因,我的变量值在getter方法中一直设置为0。这是代码: ViewLocalRemind

  • 问题内容: 我正在随处使用,并且可以在Firefox上正常使用。它不再与IE9一起工作,也不再在IE8中工作。我能做什么? 问题答案: 为什么要依赖具有对象的浏览器,而不是只包含Douglas Crockford的脚本文件。您可以在这里找到缩略文件:http : //www.json.org/js.html 导入后,您不必担心浏览器中现有的方法。