Implementing search in listview - java

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

Related

Search Item in RecyclerviewMergeAdapter with different adapter in one recyclerview

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.

Android WebView - Text Selection Listener in 2018

I will create an app that user will be enter the website url to my app. Then I am showing this page in my app using WebView.
As you know, when user clicks the any text in the context a little bit long, android cursor will appear then we can select text as many as we want.
After selection, we will see that "COPY, SHARE, SELECT ALL" etc..
My question is that when user selects text, I want to show them different options. Let's say "MyCOPY, SendTwitter, SendMessage".
How can i do that?
What I did so far?
I am just creating bar at the top of the app. But I don't want this.
Here is the code:
private WebView view;
private final String TAG = MainActivity.class.getSimpleName();
private ActionMode actionMode;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.view = findViewById(R.id.webView);
view.loadUrl("https://stackoverflow.com/questions/28385768/android-how-to-check-for-successful-load-of-url-when-using-webview-loadurl");
view.setWebViewClient(new MyWebViewClient());
Log.d(TAG, view.getUrl());
view.setOnLongClickListener((v) -> {
if (actionMode != null)
return false;
actionMode = startSupportActionMode(actionCallBack);
return true;
});
}
Where startSupportActionMode(actionCallBack) is
private ActionMode.Callback actionCallBack = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
actionMode.getMenuInflater().inflate(R.menu.custommenu, menu);
actionMode.setTitle("Choose");
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()){
case R.id.example_item_1:
Toast.makeText(MainActivity.this, "Option 1 selected", Toast.LENGTH_SHORT).show();
actionMode.finish();
return true;
case R.id.example_item_2 :
Toast.makeText(MainActivity.this, "Option 2 selected", Toast.LENGTH_SHORT).show();
actionMode.finish();
return true;
default:
return false;
}
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
actionMode = null;
}
};
You can implement the ActionMode.Callback interface to create your own menu upon selection.
An action mode's lifecycle is as follows:
onCreateActionMode(ActionMode, Menu) once on initial creation
onPrepareActionMode(ActionMode, Menu) after creation and any time the
ActionMode is invalidated
onActionItemClicked(ActionMode, MenuItem)
any time a contextual action button is clicked
onDestroyActionMode(ActionMode) when the action mode is closed
just make sure that your text views allow for text selection (android:textIsSelectable="true")
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
// Called when the action mode is created; startActionMode() was called
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
return true;
}
// Called each time the action mode is shown. Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_share:
shareCurrentItem();
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
// Called when the user exits the action mode
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
then call startActionMode() to enable the contextual action mode when appropriate (source), such as inside a setOnLongClickListener

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

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.

detect keyboard search button

I change android soft keyboard to show search icon rather than Enter icon. My question is: how i can detect that user click on that search button?
In the edittext you might have used your input method options to search.
<EditText
android:imeOptions="actionSearch"
android:inputType="text"/>
Now use the Editor listener on the EditText to handle the search event as shown below:
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// Your piece of code on keyboard search click
return true;
}
return false;
}
});
You can use OnQuerySubmit listener to get keyboard Search button click event.
Here's how,
searchView.setOnQueryTextListener(queryTextListener);
SearchView.OnQueryTextListener queryTextListener
= new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
showLog("searchClickListener onQueryTextSubmit");
getPhotos(searchView.getQuery().toString());
return true;
}
#Override
public boolean onQueryTextChange(String s) {
return false;
}
};

Categories