Search code examples
androidandroid-fragmentsrealm

How to show empty view reliably for RecyclerView only when RealmResults is empty


I understand there are a lot of information about it out there, but I haven't found one that matches my case yet.

I have a recycleview on a fragment that is always open, so the fragment basically never re-creates itself.

This is my code to load the adapter.

    reLoad(); //method shown below
    mRecycler.setAdapter(new SolicitationAdapter(myRealm.where(SolicitationDatabase.class).findAllAsync()));

And this is the logic I came up with:

public void reLoad() {
    if (!myRealm.where(SolicitationDatabase.class).findAll().isEmpty()) {
        mNothingHere.setVisibility(View.GONE);
        mRecycler.setVisibility(View.VISIBLE);
    } else {
        mNothingHere.setVisibility(View.VISIBLE);
        mRecycler.setVisibility(View.GONE);
    }
}

It works great the first time the user opens the app.

The trouble starts when the user creates a record, since the fragment doesn't re-create itself it never reloads.

The reason I haven't been able to reload after user adds something is because the method to add a new record is on a singleton being called from a different activity. Which means when I try to do it I get a nullpointerexception when declaring the the recycleview and the textview.

Edit - What I tried (reloading views from another place)

I have a class called PostHelper, this class is in charge of posting a new record.

This is the constructor:

public PostHelper(Context context, Activity activity) {
    this.mContext = context;
    this.mActivity = activity; //I call this in order to use "findViewById"

This is where the post happens:

public String addSolicitation(File _file, boolean fromQueue) {
//loading view
    TextView nothingHere = (TextView) mActivity.findViewById(R.id.nothing_here);
    RecyclerView recycler = (RecyclerView) mActivity.findViewById(R.id.recycler);
...so on until after the post:
SolicitationAdapter n = new SolicitationAdapter(myRealm.where(SolicitationDatabase.class).findAll());
n.notifyDataSetChanged();

nothingHere.setVisibility(View.GONE);
recycler.setVisibility(View.VISIBLE);

And this is the stacktrace:

06-01 21:43:37.511 9122-9122/? E/AndroidRuntime: FATAL EXCEPTION: main
                                             Process: com.example.ga.realm3, PID: 9122
                                             io.reactivex.exceptions.OnErrorNotImplementedException: Attempt to invoke virtual method 'void android.widget.TextView.setVisibility(int)' on a null object reference

Edit 2 - I load PostHelper class using the following:

mPostHelper = new PostHelper(this, PostSolicitationActivity.this);

Solution

  • You're supposed to make sure that SolicitationAdapter is a RealmRecyclerViewAdapter, like so:

    public class SolicitationAdapter extends RealmRecyclerViewAdapter<SolicitationDatabase, SolicitationViewHolder> {
         public SolicitationAdapter(OrderedRealmCollection<SolicitationDatabase> results) {
             super(results, true);
         }
     ...
    }
    

    And then what you need to do is that you put the RealmResults as a field reference in your Activity:

    public class PostSoliticiationActivity extends AppCompatActivity {
         RealmResults<Solicitation> results;
         Realm realm;
         RealmChangeListener<RealmResults<Solicitiation> realmChangeListener = (results) -> {
             if(results.isLoaded() && results.isValid()) {
                if(results.isEmpty()) {
                    mNothingHere.setVisibility(View.GONE);
                    mRecycler.setVisibility(View.VISIBLE);
                } else {
                    mNothingHere.setVisibility(View.VISIBLE);
                    mRecycler.setVisibility(View.GONE);
                }
             }
         }
    
         SolicitationAdapter adapter;
    
         @Override
         protected void onCreate(Bundle bundle) {
             super.onCreate(bundle);
             setContentView(R.layout.soliticiation_activity);
             // bind views 
             realm = Realm.getDefaultInstance();
             results = realm.where(SolicitationDatabase.class).findAllSortedAsync("id");
                                    // .sort("id").findAllAsync(); in 4.3.0+
             results.addChangeListener(realmChangeListener);
             adapter = new SoliticiationAdapter(results);
             mRecycler.setAdapter(adapter);
             // layout manager as well
         }
    
         @Override
         public void onDestroy() {
             results.removeChangeListener(realmChangeListener);
             realm.close();
             super.onDestroy();
         }
    }
    

    So things you don't need:

    1.) reLoad() method

    2.) onPostAdded callback

    3.) PostActionListener

    As long as you just add the SoliticiationDatabase to the Realm in a transaction, it'll all work without manually syncing ui.