Search Item in RecyclerviewMergeAdapter with different adapter in one recyclerview - java

i have some code with use plugin RecyclerViewMergeAdapater in here
https://github.com/martijnvdwoude/recycler-view-merge-adapter...
then i want to make search function in that, but it not working both only one adapter working, else adapter not working...
here my code
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_new_chat, menu);
MenuItem menuItem = menu.findItem(R.id.menu_item_search);
SearchView searchView = (SearchView) menuItem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
if (!newText.trim().isEmpty()) {
RealmResults<User> users = RealmHelper.getInstance().searchForUser(newText, false);
mergeAdapter.containsAdapter(new UsersAdapter(users, true, NewChatActivity.this));
rvNewChat.setAdapter(mergeAdapter);
} else {
mergeAdapter.containsAdapter(new UsersAdapter(userList, true, NewChatActivity.this));
rvNewChat.setAdapter(mergeAdapter);
}
inviteAdapter.getFilter().filter(newText);
return false;
}
});
i hope result of search 2 adapter is working...
has anyone ever used it?

If you want to apply search results to your MergeAdapter, I would iterate over the list of adapters you added to your MergeAdapter and filter out items matching your criteria. Once these items are filtered out you could submit them back to the adapter.
In the example below I stored them in a map called internalAdapterRegistry.
private val internalAdapterRegistry = mutableMapOf<String, YourAdapter>()
Assuming that we are looking for a match in the list of Products (your custom data class):
data class Product(var product_name: String, var product_quantity: Int)
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.your_menu, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem?.actionView as SearchView
searchView.findViewById<AutoCompleteTextView>(R.id.search_src_text).threshold = 3
searchView.imeOptions = EditorInfo.IME_ACTION_DONE
searchView.setOnQueryTextListener(object :
SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
fun filter(text: String) {
val textSearch = text.toLowerCase()
if (textSearch.isNotEmpty()){
for (adapter in internalAdapterRegistry) {
val listOfYourItems = adapter.value.currentList
adapter.value.submitList(listOfYourItems.filter { product -> product.product_name.contains(textSearch, ignoreCase = true) })
}
}
}
filter(newText)
return false
}
})
If you want to make it work when your are removing characters from your search bar, you will need to store currentList in a separate variable. I did not implement it here. Just wanted to give you an idea how you can play this one.

Related

Searchview in Android Java is working but returns wrong value position. How can ı get correct value?

I have a project about Movies which using The Movie Db Api. I got my searchview to display the correct results whenever I search for a particular movie. But when I click on the item in the searchview it returns a result from the original list.
For example : I am searching second item but it returns first item.
How do I fix this? Or do I need to override a method for a onclick for the searchview?
Thank you. (I will post my code (I will take out the nonrelated code), my custom adapter has the searchview code in it. The other is my fragment that contains my searchview and listview list).
MovieFragment.class related code
#Override
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// inside inflater we are inflating our menu file.
inflater.inflate(R.menu.search_menu, menu);
// below line is to get our menu item.
MenuItem searchItem = menu.findItem(R.id.actionSearch);
// getting search view of our item.
SearchView searchView = (SearchView) searchItem.getActionView();
// below line is to call set on query text listener method.
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
// inside on query text change method we are
// calling a method to filter our recycler view.
filter(newText);
return false;
}
});
}
private void filter(String text) {
// creating a new array list to filter our data.
ArrayList<MovieResult> filteredlist = new ArrayList<MovieResult>();
// running a for loop to compare elements.
for (MovieResult item : listOfMovies) {
// checking if the entered string matched with any item of our recycler view.
if (item.getTitle().toLowerCase().contains(text.toLowerCase())) {
// if the item is matched we are
// adding it to our filtered list.
filteredlist.add(item);
}
}
if (filteredlist.isEmpty()) {
// if no item is added in filtered list we are
// displaying a toast message as no data found.
} else {
// at last we are passing that filtered
// list to our adapter class.
mAdapter.filterList(filteredlist);
}
}
MovieAdapter.class related code
#SuppressLint("NotifyDataSetChanged")
public void filterList(ArrayList<MovieResult> filterlist) {
mMovies = filterlist;
notifyDataSetChanged();
}
Can you please try this
Not using search view from android studio layout it's Edit text field for search
val filteredItemsValues: ArrayList<Items> = ArrayList()
kotlin code
layoutBinding.searchEdTxt.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
filter(s.toString())
}
override fun afterTextChanged(s: Editable?) {
}
})
filter function
private fun filter(text: String) {
val filterers: ArrayList<Items> = ArrayList()
for (item in filteredItemsValues) {
if (item.name.lowercase(Locale.getDefault())
.contains(text.lowercase(Locale.getDefault()))
) {
filterers.add(item)
}
}
if (filterers.isEmpty()) {
// Toast.makeText(this, "No Data Found..", Toast.LENGTH_SHORT).show()
} else {
itemListAdapter.filterList(filterers)
}
}
adapter
fun filterList(filtered: List<Items?>) {
items = filtered as List<Items>
notifyDataSetChanged()
}
Read carefully and use accordingly this code.

How to hide and show menu-items on action bar

I have a menu item system implemented into my action bar, I am trying to get a menu item to become invisible once clicked and then to become visible again after clicking a different menu item.
The reason for this is to prevent a menu item getting clicked more than once.
I have done the following attempt below however, I get a null pointer exception error. I believe I may need to implement some if statements here but I'm not sure how to go about it.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.tasklistmenu,menu);
return true;
}//OnCreateOptionsMenu
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.allTasks:
item.setVisible(false);
MenuItem notStartedStatus = findViewById(R.id.notStartedStatus);
notStartedStatus.setVisible(true);
MenuItem inProgressStatus = findViewById(R.id.inProgressStatus);
inProgressStatus.setVisible(true);
MenuItem completeStatus = findViewById(R.id.completeStatus);
completeStatus.setVisible(true);
Log error
Attempt to invoke interface method 'android.view.MenuItem
android.view.MenuItem.setVisible(boolean)' on a null object reference
NEW CODE
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem allTasks = menu.findItem(R.id.allTasks);
MenuItem notStartedStatus = menu.findItem(R.id.notStartedStatus);
MenuItem completeStatus = menu.findItem(R.id.completeStatus);
MenuItem inProgressStatus = menu.findItem(R.id.inProgressStatus);
if(tasksIsVisible) {
allTasks.setVisible(true);
} else {
allTasks.setVisible(false);
}
if(notStartedIsVisible) {
notStartedStatus.setVisible(true);
} else {
notStartedStatus.setVisible(false);
}
if(completeIsVisible) {
completeStatus.setVisible(true);
} else {
completeStatus.setVisible(false);
}
if(inProgressIsVisible) {
inProgressStatus.setVisible(true);
} else {
inProgressStatus.setVisible(false);
}
return true;
}//OnPrepareOptions
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.allTasks:
tasksIsVisible = false;
invalidateOptionsMenu();
Its not enough to change the isVisible variable. You have to call the setVisible() method every time you want to change the visibility. That method does more than just setting a boolean value, so just changing a boolean value will not do.
After changing the isVisible value to false, you need to call invalidateOptionsMenu() which will re-launch the menu by calling onPrepareOptionsMenu() again.
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem notStartedStatus = menu.findItem(R.id.notStartedStatus);
if(isVisible)
{
notStartedStatus.setVisible(true);
}
else
{
notStartedStatus.setVisible(false);
}
return true;
}
Try this code for making the menu items unvisible:
...
isVisible = false;
invalidateOptionsMenu();
...

Trying to detect ActionMode memory leak

I have been trying to find the source of ActionMode memory leak for days now without luck. I have an activity with several fragments and when I leave the fragment having ActionMode (while auto cancelling it), LeakCanary detects a memory leak.
I have nulled both ActionMode and ActionMode.Callback on destroy() and even tried doing it on onDestroyActionMode().
Here is my LeakCanary screenshot:
https://i.imgur.com/RUbdqj3.png
I hope someone points me in the right direction.
P.S. I have suspected it has something to do with ActionMode.Callback. Though, I could not find any methods for the CallBack that destroys it. I start the ActionMode using startSupportActionMode(mActionModeCallback). I have tried to find a method to remove the mActionModeCallback from that, too, but no methods.
Here is my full ActionMode code:
private ActionMode mActionMode;
private ActionMode.Callback mActionModeCallback;
public void startCAB()
{
if (mActionMode == null)
mActionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback);
}
private void buildActionModeCallBack()
{
mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_cab, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
... Some Code ...
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
mActionModeCallback = null; // Tried with and without this.
}
};
}
public void finishActionMode()
{
mActionMode.finish();
}
#Override
public void onDestroy()
{
super.onDestroy();
mActionMode = null;
mActionModeCallback = null;
}
Parent Activity containing fragments:
#Override
public void onTabUnselected(TabLayout.Tab tab)
{
clearCAB();
}
private void clearCAB()
{
int index = mPagerAdapter.getCurrentFragmentIndex();
FragmentOne fragmentOne = (FragmentOne) mPagerAdapter.instantiateItem(mViewPager, index);
fragmentOne.finishActionMode();
}
According to my experience, if your ActionMode.Callback object use the Anonymous inner class it may cause your fragment memory leak.
Maybe you can create a new class and implements ActionMode.Callback then use it to put in startSupportActionMode() parameter:
public class YourFragment extends skip implements skip, ActionMode.Callback {
private ActionMode mActionMode;
public void startCAB()
{
if (mActionMode == null)
mActionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(new SafeActionModeCallback(this));
}
public void finishActionMode()
{
mActionMode.finish();
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_cab, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
// ... Some Code ...
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
}
SafeActionModeCallback:
public class SafeActionModeCallback implements ActionMode.Callback {
// you can also use the WeakReference
private ActionMode.Callback callback;
public SafeActionModeCallback(ActionMode.Callback callback) {
this.callback = callback;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return callback.onCreateActionMode(mode, menu);
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return callback.onPrepareActionMode(mode, menu);
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return callback.onActionItemClicked(mode, item);
}
#Override
public void onDestroyActionMode(ActionMode mode) {
callback.onDestroyActionMode(mode);
callback = null;
}
}
It seems the ActionMode in the activity has a reference to the fragment's layout which is causing the memory leak and preventing the fragment from getting GC'ed. I couldn't find a way to remove the reference.
In my use case, I'm using a ListView inside the fragment that was activating the activity's ActionMode (via listener.setMultiChoiceModeListener).
My hacky solution: In the fragment's onDestroyView, remove the listView (or whichever view activated the ActionMode) from the layout and remove all listeners for the list view. I made a kotlin extension method for it:
fun ListView.removeViewAndClearListeners() {
setMultiChoiceModeListener(null)
setOnScrollListener(null)
onItemClickListener = null
(parent as? ViewGroup)?.removeView(this)
}
After doing this, the leak is gone.
I am still wondering why you are relying on ActionMode.Callback. I had an application where I was supposed to create a Custom Menu on long press and I wasted almost 2 months on this issue :
ActionModeCallback does not work
I am not sure If you are aware of this or not, The ActionMode Callback barely works on all devices. After a lot of research, I came to know that devices who are focusing too much on battery consumption and optimization will not let your background services and some callbacks work as expected.
Try testing your code on MI or Oppo/Vivo devices. It will jump directly to onDestroyActionMode instead of calling onActionItemClicked

Implementing search in listview

I'm working on a PDF reader. I've created a list view which will hold all the PDF files you're having in your mobile.
Now when I'm implementing a search on this listview using EditText field, the problem is, it is not able to search files properly, like for example, I've 5 files named as
sample.pdf, resume-sample.pdf, resume sample.pdf, resume.pdf, sampleresume.pdf.
Now if I search:
"sample" I'll get the following result: sample.pdf and resume sample.pdf
"resume" I'll get the following result: resume sample.pdf and resume.pdf
As we can see this is not the exact result of what we expect, it should have listed all the files having either "sample" or "resume" in it.
It is able to search only when the 2 words are separated having space between them, it is not able to read the second word if there is no space or _ or - between the 2 words.
Please let me know if it is possible to implement a perfect search for my problem, thank you.
This is the code for my EditText listener:
search.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence a, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence a, int start, int before, int count) {
pdf.arradapter.getFilter().filter(a.toString());
}
#Override
public void afterTextChanged(Editable a) {
}
});
You can use Search View instead. Just create a menu.xml file which contain this
<item
android:id="#+id/search_icon"
android:icon="#android:drawable/ic_menu_search"
android:title="#string/search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always|collapseActionView" />
inflate the menu inside your activity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.your_menu_name, menu);
return true;
}
implement SearchView.OnQueryTextListener() to your activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id. search_icon:
SearchView searchView = (SearchView) item.getActionView();
searchView.setInputType(InputType.TYPE_CLASS_TEXT);
searchView.setQueryHint("PLACEHOLDER");
searchView.setOnQueryTextListener(this)
return true;
default:
return super.onOptionsItemSelected(item);
}
}
you can manipulate your list inside onQueryTextChange(String newText) method
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
newText = newText.toLowerCase();
List<Item> newTransaction = new ArrayList<>();
for (Item item : getData()) {
String name = item.getItem_name().toLowerCase();
if (name.contains(newText))
newTransaction.add(item);
}
adapter.setFilters(newTransaction);
return true;
}
}
Adapter is your custom adapter for your Adapter List View, getData return list, and inside the setFilters method
public void setFilters(List<Item> filters) {
items = new ArrayList<>();
items.addAll(filters);
notifyDataSetChanged();
}
I hope that helps. You can check this tutorial if you are confused here

Set search widget back button functionality

I have implemented the Android search widget in my navigation drawer based app. I have set it to open the keyboard and focus the editText when clicking on the search icon. I want to set the back button (up button) to hide the keyboard. I have searched the web for the R.id of the up button, and found this android.R.id.home. So I have set it to be:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
...
case android.R.id.home:
hideKeyboard();
break;
...
}
I debugged the code and noticed that clicking on the navigation bar icon fires up the android.R.id.home, but hitting the up button of the search widget doesn't even enter the onOptionsItemSelected(MenuItem item) function.
I have also tried this:
searchView.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard();
}
}
});
But didn't work.
How can I hide the keyboard when pressing the back (up) button?
Setting the search view:
private void setSearchView(Menu menu) {
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() {
public boolean onQueryTextChange(String newText) {
Home.getFilter().filter(newText);
return true;
}
public boolean onQueryTextSubmit(String query) {
return true;
}
};
searchView.setOnQueryTextListener(queryTextListener);
}
The following code should work:
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setOnCloseListener(new OnCloseListener() {
#Override
public bool OnClose() {
searchView.clearFocus();
return true;
});
However this didn't work for me for some reason. :-/
I found the workaround below here:
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
#Override
public void onViewDetachedFromWindow(View v) {
searchView.clearFocus();
}
#Override
public void onViewAttachedToWindow(View v) {
}
});
I don't think that using android.R.id.home will work since I think that onOptionsItemSelected(android.R.id.home) will only be called once the SearchView has been closed.

Categories