0
The app
in question, has a main activity and two fragments.
There are two information panels, one contains the address list and the other displays a map showing the addresses of the list.
The list is presented in a RecyclerView
, composed of cardviews
at each address.
Insertion and updating of addresses occurs in two fragment
s different. But in both I will use a AutoCompleteTextView
and the Google Place API for Android, using an object GoogleApiClient
.
I found no examples with fragments, only using the activity
.
I’m following this tutorial: Android Places API: Autocomplete with getPlaceByID
How do I use it inside a fragment
? Where the instantiation of the GoogleApiClient
should be done?
UPDATING
I tried to follow Ack Lay’s lead, and I made some progress, but I ended up getting tangled up with the mistakes.
It turns out that my code is a little different from the example. The field, alias, fields, which will receive the suggestions, are not present during the onCreateView()
because they are part of a form. that appears when clicking on the FAB defined in this event.
Note that it is already working with Google Places API Web Service
, which I adapted from another tutorial. I later read that instead of using this API, on Android, it should be used Google Places API for Android
and this is the reason for the change.
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.Toast;
import java.util.List;
public class RecyclerFragment extends Fragment implements AlertDialog.OnClickListener {
private static final String TAG = "RecyclerFragment";
RecyclerView recyclerView;
RunDbHelper runDbHelper;
RecyclerViewAdapter recyclerViewAdapter;
private OnOkButtonListener mCallback;
private CardView cardViewMessageIsEmpty;
public RecyclerFragment() {
this.mCallback = null;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recycler, container, false);
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fabAdd);
cardViewMessageIsEmpty = (CardView) view.findViewById(R.id.emptyMessageCardView);
runDbHelper = RunDbHelper.getInstance(getContext());
List<RunData> mList = runDbHelper.getAllRuns();
recyclerViewAdapter = new RecyclerViewAdapter(getContext(), mList);
recyclerView = (RecyclerView) view.findViewById(R.id.rvRunList);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(recyclerViewAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Data entry dialog to add runs
dialogInsertRun();
}
});
cardViewMessageIsEmpty.setVisibility((mList == null || mList.isEmpty()) ? View.VISIBLE : View.INVISIBLE);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public void dialogInsertRun() {
// Get the Activity for layout inflater as this dialog runs inside a fragment
LayoutInflater inflater = LayoutInflater.from(getActivity());
final View inflaterView = inflater.inflate(R.layout.dialog_new_run, null);
// Dialog Builder
AlertDialog.Builder addRunDialog = new AlertDialog.Builder(getActivity());
addRunDialog.setTitle(R.string.dialog_insert_run_title)
.setView(inflaterView);
// Data entry field objects
final EditText runParcelEditText = (EditText) inflaterView.findViewById(R.id.new_run_parcel);
final AutoCompleteTextView collectAddressACTV = (AutoCompleteTextView) inflaterView.findViewById(R.id.actv_new_collect_address);
final EditText collectPersonEditText = (EditText) inflaterView.findViewById(R.id.new_collect_person);
final AutoCompleteTextView deliveryAddressACTV = (AutoCompleteTextView) inflaterView.findViewById(R.id.actv_new_delivery_address);
final EditText deliveryPersonEditText = (EditText) inflaterView.findViewById(R.id.new_delivery_person);
// Set directions into recyclerViewAdapter for autocomplete
collectAddressACTV.setAdapter(new GooglePlacesAutocompleteAdapter(getActivity(), R.layout.dialog_new_run_autocomplete));
deliveryAddressACTV.setAdapter(new GooglePlacesAutocompleteAdapter(getActivity(), R.layout.dialog_new_run_autocomplete));
addRunDialog.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
RunData runData = new RunData();
runData.run_parcel = getStringOrEmpty(runParcelEditText);
runData.collect_person = getStringOrEmpty(collectPersonEditText);
runData.collect_address = getStringOrEmpty(collectAddressACTV);
runData.delivery_person = getStringOrEmpty(deliveryPersonEditText);
runData.delivery_address = getStringOrEmpty(deliveryAddressACTV);
if (!(runData.collect_address.isEmpty() && runData.delivery_address.isEmpty())) {
runData = runDbHelper.insertRun(runData, getActivity());
if (runData != null) {
cardViewMessageIsEmpty.setVisibility(View.INVISIBLE);
recyclerViewAdapter = new RecyclerViewAdapter(getActivity(), runDbHelper.getAllRuns());
recyclerView.setAdapter(recyclerViewAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mCallback.addMarkersToMap(runData);
}
} else {
Toast.makeText(getActivity(), R.string.dialog_insert_run_toast_nowhere, Toast.LENGTH_LONG).show();
}
}
});
addRunDialog.setNegativeButton(R.string.button_negative, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
addRunDialog.create();
addRunDialog.show();
}
private String getStringOrEmpty(EditText editText) {
String mString = editText.getText().toString();
mString = (mString == null || mString.isEmpty() ? "" : mString);
return mString;
}
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//Log.d(TAG, "onClick: ");
}
@Override
public void onAttach(Context context) {
if (context instanceof OnOkButtonListener) {
mCallback = (OnOkButtonListener) context; // keep a reference to eula activity for interface
} else {
throw new ClassCastException(context.toString()
+ getString(R.string.exception_onokbutton_listener));
}
super.onAttach(context);
}
public void setCustomObjectListener(OnOkButtonListener listener) {
this.mCallback = listener;
}
public interface OnOkButtonListener {
void addMarkersToMap(RunData runData);
}
}
The fields are collectAddressACTV
and deliveryAddressACTV
in the method dialogInsertRun()
XML of the dialog
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/activity_minimal_distance">
<android.support.design.widget.TextInputLayout
android:id="@+id/input_new_run_parcel"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/new_run_parcel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/collect_what"
android:imeOptions="flagNoExtractUi"
android:inputType="text" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/input_actv_new_collect_address"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<AutoCompleteTextView
android:id="@+id/actv_new_collect_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/collect_where"
android:imeOptions="actionNext|flagNoExtractUi"
android:inputType="textPostalAddress"
android:maxLines="2"></AutoCompleteTextView>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/input_new_collect_person"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/new_collect_person"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/collect_who"
android:imeOptions="flagNoExtractUi"
android:inputType="textPersonName" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/input_actv_new_delivery_address"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<AutoCompleteTextView
android:id="@+id/actv_new_delivery_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/delivery_where"
android:imeOptions="actionNext|flagNoExtractUi"
android:inputType="text"
android:maxLines="2">
</AutoCompleteTextView>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/input_new_delivery_person"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/new_delivery_person"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/delivery_who"
android:imeOptions="flagNoExtractUi"
android:inputType="textPersonName" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
And this class defines the adapter that for the autocomplete Google Places API Web Service
. It is similar to the tutorial link adapter class I mentioned.
public class GooglePlacesAutocompleteAdapter extends ArrayAdapter<String> implements Filterable {
// autocomplete code
private static final String PLACES_API_BASE = "https://maps.googleapis.com/maps/api/place";
private static final String TYPE_AUTOCOMPLETE = "/autocomplete";
private static final String OUT_JSON = "/json";
//------------ make your specific key ------------
private static final String API_KEY = "AIza................................";
private ArrayList<String> resultList;
public GooglePlacesAutocompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
@Override
public int getCount() {
return resultList.size();
}
@Override
public String getItem(int index) {
return resultList.get(index);
}
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString(), getUserCountry(getContext()));
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return filter;
}
public static ArrayList<String> autocomplete(String input, String countryCode) {
ArrayList<String> resultList = null;
HttpURLConnection conn = null;
StringBuilder jsonResults = new StringBuilder();
try {
StringBuilder stringBuilder = new StringBuilder(PLACES_API_BASE + TYPE_AUTOCOMPLETE + OUT_JSON);
stringBuilder
.append("?key=" + API_KEY)
.append("&components=country:" + countryCode)
.append("&input=" + URLEncoder.encode(input, "utf8"));
URL url = new URL(stringBuilder.toString());
System.out.println("URL: " + url);
conn = (HttpURLConnection) url.openConnection();
InputStreamReader in = new InputStreamReader(conn.getInputStream());
// Load the results into a StringBuilder
int read;
char[] buff = new char[1024];
while ((read = in.read(buff)) != -1) {
jsonResults.append(buff, 0, read);
}
} catch (MalformedURLException e) {
// Log.e(TAG, "Error processing Places API URL", e);
return resultList;
} catch (IOException e) {
// Log.e(TAG, "Error connecting to Places API", e);
return resultList;
} finally {
if (conn != null) {
conn.disconnect();
}
}
try {
// Create a JSON object hierarchy from the results
JSONObject jsonObj = new JSONObject(jsonResults.toString());
JSONArray predsJsonArray = jsonObj.getJSONArray("predictions");
// Extract the Place descriptions from the results
resultList = new ArrayList<>(predsJsonArray.length());
for (int i = 0; i < predsJsonArray.length(); i++) {
System.out.println(predsJsonArray.getJSONObject(i).getString("description"));
System.out.println("============================================================");
resultList.add(predsJsonArray.getJSONObject(i).getString("description"));
}
} catch (JSONException e) {
//Log.e(TAG, "Cannot process JSON results", e);
}
return resultList;
}
/**
* Get ISO 3166-1 alpha-2 country code for this device (or null if not available)
*
* @param context Context reference to get the TelephonyManager instance from
* @return country code or null
*/
@Nullable
public static String getUserCountry(Context context) {
try {
final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
final String simCountry = tm.getSimCountryIso();
if (simCountry != null && simCountry.length() == 2) { // SIM country code is available
return simCountry.toLowerCase(Locale.US);
} else if (tm.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { // device is not 3G (would be unreliable)
String networkCountry = tm.getNetworkCountryIso();
if (networkCountry != null && networkCountry.length() == 2) { // network country code is available
return networkCountry.toLowerCase(Locale.US);
}
}
String localeCountry = context.getResources().getConfiguration().locale.getCountry();
if (localeCountry != null && localeCountry.length() == 2) {
return localeCountry.toLowerCase(Locale.US);
}
} catch (Exception e) {
return null;
}
return null;
}
}
I’m understanding and moving here, but the autocomplete field is in a dialog that is not open in oncreateview(). I tried to adapt the code in several ways, but I ended up finding some mistakes that I could not solve. I will modify the question by adding the code and some comments.
– Rene Freak
@Renefreak is in the same context, regardless if it is in a dialog, etc. It will follow the same line of reasoning. Which error we failed to solve?
– viana
There were several errors, which I eliminated, until an error in the onConnected() method. When trying to debug, ended up generating errors in the other Ragment that wasn’t even moving. I put the code in the question. I hope it helps to solve. Vlw
– Rene Freak
What is the name of the error?! Is the error in this class? You have to point them out but you can’t solve it.
– viana
@Renefreak put the XML of your Dialog so I can check here what might be the error.
– viana
Posted. Note that this class is working with the web service. My goal now is to transform it to use the correct API.
– Rene Freak
Let’s go continue this discussion in chat.
– viana
@Renefreak in the dialogue apparently did not happen anything wrong. But I have no idea what you’re doing here: collectAddressACTV.setAdapter(new Googleplacesautocompleteadapter(getActivity(), R.layout.dialog_new_run_autocomplete));
– viana
Googleplacesautocompleted pter is the API adapter. I put the code so you can see the class, but it’s similar to the tutorial link.
– Rene Freak