Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 580 Vote(s) - 3.48 Average
  • 1
  • 2
  • 3
  • 4
  • 5
RecyclerView in React Native: notifyItemInserted() and notifyDataSetChanged() have no effect

#1
I am experimenting with integrating a Firebase-backed RecyclerView in a React Native app. With hardcoded data it works well, but upon inserting rows loaded dynamically and calling either notifyItemInserted() or notifyDataSetChanged() on the RecyclerView.Adapter, the RecyclerView itself does not reflect the change. This manifests as an initial blank view until the app's refresh button is tapped, which has the sole effect of re-rendering the RecyclerView from the React Native side, or until scrolling or a screen orientation change.

After reading through a dozen or more questions on this site about similar issues not involving React Native and having tried most of the common solutions, I suspect this issue is related specifically to React Native.

Finding a solution to this could be helpful to many, given the ongoing performance issues with React Native's list view implementations.

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>
Reply

#2
The root view in ReactNative is ReactRootView which `onlayout` is an empty method.

when call `notifyDatasetChanged` in RecyclerView, it's actually request `layout` to relayout its children. And the layout method will call `super.layout` to travel the whole view tree first. So that't a problem when the root view is ReactRootView.

You can manually call `RecyclerView.onlayout(boolean changed, int l, int t, int r, int b)` to trigger its children relayout to make notifyDatasetChanged work.
Reply

#3
The problem is that `requestLayout` does not work well when the `RecyclerView` is a native UI component.

The following hack made all those issues go away:


I now overwrite the `requestLayout` method inside my `RecyclerView`.
Then before any `notify*` method, or even `scrollToPosition` calls or any method that invokes a re-layout, I allow my custom `requestLayout` method to force a re-layout.

The end result looks like this:

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

[To see links please register here]

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());
}
});
}
}
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through