1

我正在試驗在React Native應用程序中集成Firebase支持的RecyclerView。對於硬編碼數據,它工作得很好,但是在插入動態加載的行並在RecyclerView.Adapter上調用notifyItemInserted()或notifyDataSetChanged()時,RecyclerView本身並不反映更改。直到應用程序的刷新按鈕被點擊後,它纔會以初始空白視圖的形式出現,它具有從React Native側重新呈現RecyclerView,或直到滾動或屏幕方向更改的唯一效果。React Native中的RecyclerView:notifyItemInserted()和notifyDataSetChanged()不起作用

在閱讀本網站上關於類似問題(涉及React Native並且嘗試了大多數常見解決方案)之後,我猜這個問題與React Native特別相關。

鑑於React Native的列表視圖實施中持續存在的性能問題,尋找解決方案可能對許多人有所幫助。

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> 
+0

您可以在'addPost(p)'方法中插入日誌嗎? –

+0

那裏應該記錄什麼? – Isaac16

+0

剛剛添加了RecyclerView.AdapterDataObserver並從中記錄。 onChanged()方法從不調用。 – Isaac16

回答

1

ReactNative中的根視圖是ReactRootView,其中onlayout是一個空方法。

當在RecyclerView中調用notifyDatasetChanged時,它實際上是要求layout來重新佈局它的孩子。佈局方法將調用super.layout首先遍歷整個視圖樹。所以當根視圖是ReactRootView時沒有問題。

您可以手動呼叫RecyclerView.onlayout(boolean changed, int l, int t, int r, int b)來觸發其子節點重新佈局,以使notifyDatasetChanged正常工作。

相關問題