Search code examples
androidandroid-recyclerviewandroid-viewpagerrealmandroid-tablayout

RealmRecyclerView updates only after switching between tabs. Empty view appears with delay


There is ViewPager with 3 pages (FragmentPagerAdapter) RealmRecyclerView in 1st page.

When Realm is empty "No items" message and "Add" button are represents. It's ok.

So, I've added my first object to Realm. EmptyView is still here though I expected to see RealmRecyclerView (RRV).

When I switching between tab1 and tab2 nothing changes. But if I swithes to tab3 and then back to my tab1 - RRV finally appears. And it doesn't depend on the content of the page. The appearance of RRV is affected only by the return from the tab3 as such.

I don't understand the reason. I rechecked my TabViewPager, TabAdapter and Fragments. Everything is normal. There is nothing superfluous, which probably could affect this in my opinion.

Second problem:

When i filter my RRV through the query by SearchView widget EmptyView appears with delay.

Eg: if there is "12345" item, EmptyView appears only when query is "1234567" but not "123456" as expected..

Аt the first mismatch RRV remains visible although it's empty. And only if difference in two or more characters RRV is dissappearing. Removal of superfluous characters also doesn't give a correct representation.

I think there is one reason in both cases. Could you point on the mistake?

FirstFragment.java

public class FirstFragment extends Fragment {

Realm realm;
RecyclerView realmRecyclerView;
FirstAdapter adapter;
TextView emptyText;
Button buttonAdd;

public FirstFragment () {
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setHasOptionsMenu(true);
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View fragmentView = inflater.inflate(R.layout.fragment_first, container, false);
    realm = Realm.getDefaultInstance();
    realmRecyclerView = fragmentView.findViewById(R.id.recyclerView_first);

    RealmResults<Object> results = realm.where(Object.class).findAll();

    emptyText = fragmentView.findViewById(R.id.tv_no_data);
    buttonAdd = fragmentView.findViewById(R.id.button_add);

    setUpRealmRecyclerView();
    initButtonAdd();

    if (!results.isEmpty()) {
        realmRecyclerView.setVisibility(View.VISIBLE);
        emptyText.setVisibility(View.GONE);
        buttonAdd.setVisibility(View.GONE);

    } else {

        realmRecyclerView.setVisibility(View.GONE);
        emptyText.setVisibility(View.VISIBLE);
        buttonAdd.setVisibility(View.VISIBLE);
    }

    return fragmentView;
}

private void setUpRealmRecyclerView() {
    adapter = new FirstAdapter(realm, realm.where(Object.class).findAll());
    realmRecyclerView.setHasFixedSize(true);
    realmRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    realmRecyclerView.setAdapter(adapter);

    FirstTouchHelper callback = new FirstTouchHelper(realm);
    ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
    touchHelper.attachToRecyclerView(realmRecyclerView);
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    realmRecyclerView.setAdapter(null);
    realm.close();
}

private void initButtonAdd() {

//create new object dialog

}

@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
    inflater.inflate(R.menu.actionbar_menu, menu);

    SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
    final SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
    SearchableInfo searchableInfo = searchManager.getSearchableInfo(getActivity().getComponentName());
    searchView.setSearchableInfo(searchableInfo);
    searchView.setIconifiedByDefault(true);

    EditText editTextSearch = (EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);

    editTextSearch.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable editable) {
            adapter.getFilter().filter(editable);

            if (adapter.getItemCount()==0) {
                realmRecyclerView.setVisibility(View.GONE);
                buttonAdd.setVisibility(View.VISIBLE);

            } else {
                realmRecyclerView.setVisibility(View.VISIBLE);                    
                buttonAdd.setVisibility(View.GONE);
            }
        }
    });

    super.onCreateOptionsMenu(menu, inflater);
}

}

Adapter for RRV:

public class FirstAdapter
    extends RealmRecyclerViewAdapter<Object, RecyclerView.ViewHolder>
    implements Filterable{

Realm realm;

public FirstAdapter(Realm realm, OrderedRealmCollection<Object> data) {
    super(data, true, true);
    this.realm = realm;
    setHasStableIds(true);
}

@Override
public long getItemId(int index) {
    return getItem(index).getId();
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rrv_item, parent, false);
    ObjectClass holder = new ObjectClass(v);
    return holder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    Object object = getData().get(position);
    ObjectClass classHolder = (ObjectClass) holder;
    classHolder.name.setText(object.getName());

}

public void filterResults(String text) {
    text = text == null ? null : text.toLowerCase().trim();
    RealmQuery<Object> query = realm.where(Object.class);

    if (text == null || "".equals(text)) {
        updateData(query
                .findAll()
                .sort("name"));
    } else {
        updateData(query
                .contains("name", text, Case.INSENSITIVE)
                .findAll()
                .sort("name"));
        }
}


public Filter getFilter() {
    ObjectFilter filter = new ObjectFilter(this);
    return filter;
}


private class ObjectFilter
        extends Filter {
    private final FirstAdapter adapter;

    private ObjectFilter(FirstAdapter adapter) {
        super();
        this.adapter = adapter;
    }

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        return new FilterResults();
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        adapter.filterResults(constraint.toString());
    }
}



private class ObjectClass
        extends RecyclerView.ViewHolder {
    TextView name;

    private ObjectClass(View itemView) {
        super(itemView);

        name = itemView.findViewById(R.id.name_text_view);
    }
}

}


Solution

  • The reason why clicking the 3rd tab then clicking the 1st tab "reloads" and shows your views properly is because the offscreenPageLimit by default is 1, so when tab 1 is selected, tab 3 is destroyed, and when tab 3 is selected, tab 1 is destroyed.

    So your view is re-created and so it'll show properly.

    But to solve your issue, you should use RealmChangeListener to receive notifications when Realm is written to.

    public class FirstFragment extends Fragment {
    
        Realm realm;
        RealmResults<Object> results;
        RealmChangeListener<RealmResults<Object>> changeListener = new RealmChangeListener<RealmResults<Object>>() {
            @Override
            public void onChange(RealmResults<Object> results) {
                if (!results.isEmpty()) {
                    realmRecyclerView.setVisibility(View.VISIBLE);
                    emptyText.setVisibility(View.GONE);
                    buttonAdd.setVisibility(View.GONE);
    
                } else {
    
                    realmRecyclerView.setVisibility(View.GONE);
                    emptyText.setVisibility(View.VISIBLE);
                    buttonAdd.setVisibility(View.VISIBLE);
                }
            }
        };
    
        RecyclerView realmRecyclerView;
        PickProductAdapter adapter;
        TextView emptyText;
        Button buttonAdd;
    
        public FirstFragment () {
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setHasOptionsMenu(true);
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View fragmentView = inflater.inflate(R.layout.fragment_first, container, false);
            realm = Realm.getDefaultInstance();
            realmRecyclerView = fragmentView.findViewById(R.id.recyclerView_first);
    
            results = realm.where(Object.class).findAllAsync();
            results.addChangeListener(changeListener);
    
            emptyText = fragmentView.findViewById(R.id.tv_no_data);
            buttonAdd = fragmentView.findViewById(R.id.button_add);
    
            setUpRealmRecyclerView();
            initButtonAdd();
    
    
            return fragmentView;
        }
    
        private void setUpRealmRecyclerView() {
            adapter = new PickProductAdapter(realm, results);
            realmRecyclerView.setHasFixedSize(true);
            realmRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
            realmRecyclerView.setAdapter(adapter);
    
            FirstTouchHelper callback = new FirstTouchHelper(realm);
            ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
            touchHelper.attachToRecyclerView(realmRecyclerView);
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            results.removeAllChangeListeners();
            realmRecyclerView.setAdapter(null);
            realm.close();
        }
    
        private void initButtonAdd() {
    
        //create new object dialog
    
        }
    
        @Override
        public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
            inflater.inflate(R.menu.first_actionbar_menu, menu);
    
            // set searchview
        }
    
    }
    

    EDIT: The reason why your empty view doesn't update is because it's independent from what's in the adapter, so you need to add some callback method that you can call when a filter is active.

    public class FirstFragment extends Fragment {
        ...
        public void updateData(RealmResults<Object> results) {
             if(this.results != null && this.results.isValid()) {
                 this.results.removeAllChangeListeners();
             }
             this.results = results;
             results.addChangeListener(changeListener);
        }
    

    And

    public class FirstAdapter
        extends RealmRecyclerViewAdapter<Object, RecyclerView.ViewHolder>
        implements Filterable{
    
    FirstFragment firstFragment; // could be interface or somethin'
    Realm realm;
    
    public FirstAdapter(FirstFragment firstFragment, Realm realm, OrderedRealmCollection<Object> data) {
        ...
    
    ...
    
    @Override
    public void updateData(OrderedRealmCollection<Object> collection) {
        super.updateData(collection);
        firstFragment.updateData((RealmResults<Object>)collection);
    }
    

    EDIT:

    Also

    RealmQuery<Object> query = realm.where(Object.class);
    
    if (text == null || "".equals(text)) {
        updateData(query
                .sort("name")
                .findAllAsync());
    } else {
        updateData(query
                .contains("name", text, Case.INSENSITIVE)
                .sort("name")
                .findAllAsync());
        }
    

    And

    editTextSearch.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        }
    
        @Override
        public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
    
        }
    
        @Override
        public void afterTextChanged(Editable editable) {
            adapter.getFilter().filter(editable);
    
            // this should be in `onChange()`
            /*if (adapter.getItemCount()==0) {
                realmRecyclerView.setVisibility(View.GONE);
                buttonAdd.setVisibility(View.VISIBLE);
    
            } else {
                realmRecyclerView.setVisibility(View.VISIBLE);                    
                buttonAdd.setVisibility(View.GONE);
            }*/
        }
    });