1

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

}

Dereva
  • 23
  • 1
  • 6

1 Answers1

0

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);
        }*/
    }
});
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • I think `onDestroy()` wasn't called, but `onDestroyView()` probably was. FragmentPager detaches and re-attaches fragments. – EpicPandaForce Jan 11 '18 at 14:25
  • Hm, but my TabAdapter extends FragmentPagerAdapter, where onDestroy isn't called. So I thought that the problem was in something else. Although true! he recreates the fragment and this is enough (rewrite comment, could not contact using @) – Dereva Jan 11 '18 at 14:29
  • Problem with tabs solved! Thank you! But filtration is still incorrect – Dereva Jan 11 '18 at 14:46
  • There is no SearchView related code in your question, neither about its setup nor anything else, really – EpicPandaForce Jan 11 '18 at 14:48
  • But how does `adapter.getFilter().filter(editable);` actually filter? Show your adapter code – EpicPandaForce Jan 11 '18 at 15:52
  • just added. actually it's your code i've copy-pasted from this example https://stackoverflow.com/questions/40630322/how-to-implement-filterable-in-realmrecyclerviewadapter/40632613#40632613 – Dereva Jan 11 '18 at 16:11
  • That would work, but you create a new RealmResults which you should pass out and swap out the Results in the Fragment and the change listener should be removed from previous and added to new, but I'll only be able to edit that into the answer in like 2 hours - basically you need a listener for when you filter the adapter and update the data outside (in the fragment) as well – EpicPandaForce Jan 11 '18 at 16:29
  • I got confused. In updateData's this.results = results there are incompatible types (Result vs. Collection) so addChangeListener don't resolved. – Dereva Jan 11 '18 at 19:24
  • uergh. I guess you'll need to cast to `RealmResults`. Figured it's inherited but I guess it's its own method on RealmResults/RealmList. – EpicPandaForce Jan 11 '18 at 19:30
  • Your code seems very logic and laconic, but for some reason afterTextChanged's "if-else conditions" are performed instead of onChange's ones. Accordingly, if I delete them, just empty list will be show when there are no query matches – Dereva Jan 12 '18 at 14:33
  • Ah yeah I did forget to mention that you should use `.sort("name").findAllAsync()` (or `findAllSortedAsync("name")` on older versions) – EpicPandaForce Jan 12 '18 at 14:52
  • Ah, of course! "A RealmResults object cannot be passed between different threads" Thank you very-very much for all your help! it works great! For my little experience analyze of working code is better than just reading doc – Dereva Jan 12 '18 at 15:59