Implementing Material Search Bar over Google Maps activity - android

So, I am working on a mapping application (for a botanical garden) that lets me search data that I have stored in an SQL database to store names and coordinates of various plant life (I have the database set up and working).
The main issue I am running into is getting the search bar functioning properly. I have the material search bar overlaying on my map properly, and I can search for my SQL data, however, I am using a recyclerview to display my SQL data, which overlays over my map, which makes the search functionality completely useless since it is covering the map.
I am really unsure how to either get recyclerview to collapse properly, or implement a different method so that my SQL data populates in the material search bar results, rather than just overlapping my map. Here is my OnCreate method, where I set up the material search bar and the recycler view. Any suggestions/help would be appreciated, since I am struggling to find guides/documentation online.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
//Code used to remove/add droplist to material search bar
recyclerView = (RecyclerView)findViewById(R.id.recycler_search);
layoutManager= new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
materialSearchBar = (MaterialSearchBar)findViewById(R.id.search_bar);
database = new Database(this);
materialSearchBar.setText("Search around The Gardens");
materialSearchBar.setCardViewElevation(10);
// loadSuggestList();
materialSearchBar.addTextChangeListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
List<String> suggest = new ArrayList<>();
for(String search:suggestList)
{
if(search.toLowerCase().contains(materialSearchBar.getText().toLowerCase()))
{
suggest.add(search);
}
}
materialSearchBar.setLastSuggestions(suggest);
}
#Override
public void afterTextChanged(Editable s) {
}
});
materialSearchBar.setOnSearchActionListener(new MaterialSearchBar.OnSearchActionListener() {
#Override
public void onSearchStateChanged(boolean enabled) {
if(!enabled)
{
recyclerView.setAdapter(adapter);
}
}
#Override
public void onSearchConfirmed(CharSequence text) {
startSearch(text.toString());
}
#Override
public void onButtonClicked(int buttonCode) {
}
});
//USED IN DATABASE CLASS
adapter = new SearchAdapter(this,database.getTrees());
recyclerView.setAdapter(adapter);
SQL data with recyclerview overlapping maps

Related

Android notifyDataSetChanged behave differently

I have two different applications, that update a list in both cases.
App1
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dataList.add("NEW CITY");
cityAdapter.notifyDataSetChanged();
}
});
Here, my floating action button, update the list with const string (as test input). If I don't use notifyDataSetChanged the list will not show the 'NEW CITY' text.
App2
This one is a bit more complex than the App1, because now use a fragment to add/edit city.
The two listeners looks like this.
addCityButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new AddCityFragment().show(getSupportFragmentManager(),"ADD_CITY");
}
});
cityList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
City clickedCity = cityAdapter.getItem(i);
AddCityFragment.newInstance(clickedCity, i).show(getSupportFragmentManager(),
"EDIT_CITY");
return false;
}
});
I have two methods that implement the logic which are invoked when the fragment is loaded.
#Override
public void onOkPressListener(City newCity) {
dataList.add(newCity);
}
#Override
public void onEditPressListener(City editCity, int index) {
City currentCity = dataList.get(index);
currentCity.setCity(editCity.getCity());
currentCity.setProvince(editCity.getProvince());
}
For some reason, the list updates (add/edit) even though I didn't call notifyDataSetChanged in each method. Is the list refreshed somehow when the fragment is closed? I feel like I ignore some sort of a Android activity lifecycle method here.

ArrayList getting changed without any operation on it

I am trying to write a login in Android. The Logic is I am initiating an ArrayList in a constructor of a PopupWindow. In that PopupWindow I am showing a list using RecyclerView, by passing this ArrayList into the constructor of the Adapter Class. In that list I am using an EditText to search the list using addTextChangedListener.
The code is as follows,
MainActivity.Java
ArrayList<CompanyModel> companyList , backupCompanyList;
CompanyAdapter companyAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
//initialisation of both arraylists
companyList = getCompanylist();
backupCompanyList = companyList;
}
// inner class declared in the MainActivity.java
public class ShowCompanyData{
public ShowCompanyData(){
//initialise popupwindow
//get view of recyclerview and other view components of the popupwindow , and setadapter to the recyclerview
companyAdapter = new CompanyAdapter(context , companyList );
et_search.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String text = et_search.getText().toString().toLowerCase(Locale.getDefault());
companyAdapter.filter(text);
}
});
}
}
//this button belongs to the Layout file of MainActivity.
public void showPopupList(View v){
// this is a button click where i am showing the company list popupwindow
companyListPopupWindow.showAtLocation(layout, Gravity.CENTER, 0, 0);
}
CompanyAdapter.java
public class CompanyAdapter extends RecyclerView.Adapter<CompanyAdapter.ViewHolder> {
Context context;
ArrayList<CompanyModel> mainArrayList;
ArrayList<CompanyModel> list;
// other imp methods are also written , but not shown because not necessary to show here
public CompanyAdapter(Context context, ArrayList<CompanyModel> mainArrayList) {
this.context = context;
this.mainArrayList = mainArrayList;
this.list = new ArrayList<>();
this.list.addAll(mainArrayList);
}
public void filter(String charText) {
charText = charText.toLowerCase(Locale.getDefault());
mainArrayList.clear();
if (charText.length() == 0) {
mainArrayList.addAll(list);
} else {
for (CompanyModel wp : list) {
if (wp.getCompanyName().toLowerCase(Locale.getDefault()).contains(charText)) {
mainArrayList.add(wp);
}
}
}
notifyDataSetChanged();
}
}
Here the issue I am facing is, when I search something in in the EditText of PopupWindow where the list of Company's is shown, the ArrayList backupCompanyList is getting modified same as the companyList ArrayList.
My question is, I am not assigning anything to the backupCompanyList, also not passing it as a parameter to the Adapter Class, still when I debug the app the backupCompanyList are showing same contents as companyList, after searching anything in the EditText.
Where the backupCompanyList should contains the data (unchanged) assigned in OnCreate and should not modify the changes, because there are no operations or assignments done to the backupCompanyList in the entire program.
Can anyone guide me to overcome this issue.
Note :
I have not written full code, I have written only necessary code
Both ArrayLists (companyList & backupCompanyList) are showing proper data before entering any text into EditText of search. And issue is occuring only after searching.
In your Activity's onCreate method, your are assigning companyList reference to backupCompanyList reference. Both companyList and backupCompanyList are referring to the same ArrayList object reference returned from getCompanyList() method. That's why, it's reflecting both lists are changing together. In actual, there's only one ArrayList object.
Instead of:
companyList = getCompanyList();
backupCompanyList = companyList;
Use
companyList = getCompanyList();
backupCompanyList = new ArrayList<>(companyList);

Slow fragment loading (API data binding)

I have a fragment with a listview.
When I click on the Navigation Drawer to open this fragment it takes 2-3 seconds to load.
I make an API call from AsyncTask. I call it from my onResume() in my fragment because I want to inflate the layout and then call the API and show ProgressDialog in opened fragment and wait for the AsyncTask to finish then populate ListView. Problem is that my fragment takes 2-3 seconds(frozen screen) and then loads completely with API call finished and list populated. There is no way to show fragment and than populate list. I tried in all Override methods in fragment. Always the same result. Also when I remove all code from fragment, it loads fast.
Any tips why is that happening?
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_history, container, false);
historySearchET = (EditText) v.findViewById(R.id.historySearchEditText);
listview = (ListView) v.findViewById(R.id.historyListView);
emptyView = v.findViewById(R.id.empty);
return v;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
historySearchET.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
#Override
public void afterTextChanged(Editable s) {
Log.d("aaa", "*** Search value changed: " + s.toString());
adbHistory.getFilter().filter(s.toString());
listview.setAdapter(adbHistory);
}
});
}
#Override
public void onResume() {
super.onResume();
APIManager apiManager = APIManager.getInstance(getActivity());
historyList = apiManager.getUserHistroy();
adbHistory= new HistoryAdapter (getActivity(), 0, historyList);
listview.setAdapter(adbHistory);
listview.setEmptyView(emptyView);
}
}
EDIT: Added code.
This is fastest solution that I got worked out. Still 2-3 seconds and frozen screen in meantime. Tried overriding different methods but still same problem, sometimes slower-sometimes faster.

Possible to initialize all UI elements in one method?

Is it possible to initialize all UI elements of certain type (like all TextViews or all LineraLayouts or ...) in a some kind of loop?
I have many layouts with a lot of the elements of the same type and it's really painful to do it all just by typing.
You can use RoboGuice .It doesn't use loops, but helps you to Inject your View, Resource, System Service, or any other object in to your code.
RoboGuice is a framework that brings the simplicity and ease of Dependency Injection to Android, using Google's own Guice library.
To give you an idea, take a look at this simple example of a typical Android activity:
class AndroidWay extends Activity {
TextView name;
ImageView thumbnail;
LocationManager loc;
Drawable icon;
String myName;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name = (TextView) findViewById(R.id.name);
thumbnail = (ImageView) findViewById(R.id.thumbnail);
loc = (LocationManager) getSystemService(Activity.LOCATION_SERVICE);
icon = getResources().getDrawable(R.drawable.icon);
myName = getString(R.string.app_name);
name.setText( "Hello, " + myName );
}
}
This example is 19 lines of code. If you're trying to read through onCreate(), you have to skip over 5 lines of boilerplate initialization to find the only one that really matters: name.setText(). And complex activities can end up with a lot more of this sort of initialization code.
Compare this to the same app, written using RoboGuice:
class RoboWay extends RoboActivity {
#InjectView(R.id.name) TextView name;
#InjectView(R.id.thumbnail) ImageView thumbnail;
#InjectResource(R.drawable.icon) Drawable icon;
#InjectResource(R.string.app_name) String myName;
#Inject LocationManager loc;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name.setText( "Hello, " + myName );
}
}
In this example, onCreate() is much easier to take in at a glance. All the platform boilerplate is stripped away and you're left with just your own app's business logic. Do you need a SystemService? Inject one. Do you need a View or Resource? Inject those, too, and RoboGuice will take care of the details.
RoboGuice's goal is to make your code be about your app, rather than be about all the initialization and lifecycle code you typically have to maintain in Android.
This text is from here
I have/had done something similar. Just for your reference, here's the code:
public class AbcActivity extends Activity
{
protected boolean changesPending;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.login_screen);
setViews(); //this method is created and called to take care of the buttons and edittext fields, and can probably hold a number of other fields/widgets as well
}
/** Take care of the Buttons and EditTexts here*/
private void setViews()
{
EditText userEdit = (EditText)findViewById(R.id.editText1);
EditText passwordEdit = (EditText)findViewById(R.id.editText2);
Button loginButton = (Button)findViewById(R.id.login_button);
loginButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
login(); // some random method
}
});
Button cancelButton = (Button)findViewById(R.id.cancel_button);
cancelButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
cancel(); //another random method
}
});
userEdit.addTextChangedListener(new TextWatcher()
{
public void onTextChanged(CharSequence s, int start, int before, int count)
{
changesPending = true;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void afterTextChanged(Editable s) {}
});
passwordEdit.addTextChangedListener(new TextWatcher()
{
public void onTextChanged(CharSequence s, int start, int before, int count)
{
changesPending = true;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void afterTextChanged(Editable s) {}
});
}
}
Hope this helps.
If you are trying to handle a large number of Views it may be worthwhile handling creation of these Views at runtime, attaching them to the relevant container. For example:
ViewGroup container = (ViewGroup) findViewById(R.id.container);
for(int i = 0; i < NUM_TEXT_VIEWS; i++){
TextView tv = new TextView(this); // where 'this' is your Activity
tv.setText("This is TextView " + i);
container.addView(tv);
}
Properties set in your xml file for a View usually have a corresponding Java method call.

Getting current suggestion from `AutoCompleteTextView`

How do you get the current top suggestion in an AutoCompleteTextView? I have it suggesting items, and I have a text change listener registered. I also have a list on the same screen. As they type, I want to scroll the list to the current "best" suggestion. But I can't figure out how to access the current suggestions, or at least the top suggestion. I guess I'm looking for something like AutoCompleteTextView.getCurrentSuggestions():
autoCompleteTextView.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
String currentText = autoCompleteTextView.getText();
String bestGuess = autoCompleteTextView.getCurrentSuggestions()[0];
// ^^^ mewthod doesn't exist
doSomethingWithGuess(bestGuess);
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// do nothing
}
public void afterTextChanged(Editable s) {
// do nothing
}
});
I've done what you want to do with the following code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.autocomplete_1);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line, COUNTRIES);
AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit);
textView.setAdapter(adapter);
adapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
Log.d(TAG, "dataset changed");
Object item = adapter.getItem(0);
Log.d(TAG, "item.toString "+ item.toString());
}
});
}
item.toString will print the text that is displayed on the first item.
Note that this will happen even if you aren't showing the pop-up (suggestions) yet. Also, you should check if there are any items that passed the filter criteria (aka the user's input).
To solve the first problem:
int dropDownAnchor = textView.getDropDownAnchor();
if(dropDownAnchor==0) {
Log.d(TAG, "drop down id = 0"); // popup is not displayed
return;
}
//do stuff
To solve the second problem, use getCount > 0
AutoCompleteTextView does not scroll down to the best selection, but narrows down the selection as you type. Here is an example of it: http://developer.android.com/resources/tutorials/views/hello-autocomplete.html
As I see it from AutoCompleteTextView there is no way to get current list of suggestions.
The only way seem to be writing custom version of ArrayAdapter and pass it to AutoCompleteTextView.setAdapter(..). Here is the source to ArrayAdapter. You must only change a method in inner class ArrayFilter.performFiltering() so that it exposes FilterResults:
.. add field to inner class ArrayFilter:
public ArrayList<T> lastResults; //add this line
.. before end of method performFiltering:
lastResults = (ArrayList<T>) results; // add this line
return results;
}
Using it like this (adapted example from link):
AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.autocomplete_country);
CustomArrayAdapter<String> adapter = new CustomArrayAdapter<String>(this, R.layout.list_item, COUNTRIES);
textView.setAdapter(adapter);
// read suggestions
ArrayList<String> suggestions = adapter.getFilter().lastResult;

Categories

Resources