The app
has a fragment with recyclerview
and FAB
. Clicking FAB
opens a dialog where the user enters addresses, among other information.
The address input fields are of type AutoCompleteTextView and receive address suggestions as the user enters the information.
This was done using GoogleApiClient
with GEO_DATA_API
, according to the following code:
public class RecyclerFragment extends Fragment
implements AlertDialog.OnClickListener,
GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ConnectionCallbacks {
private static final String TAG = "RecyclerFragment";
RecyclerView recyclerView;
RunDbHelper runDbHelper;
RecyclerViewAdapter recyclerViewAdapter;
private OnOkButtonListener mCallback;
private GoogleApiClient mGoogleApiClient;
private PlaceArrayAdapter mPlaceArrayAdapter;
private static final int GOOGLE_API_CLIENT_ID = 0;
private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
private static LatLngBounds BOUNDS_AUTOCOMPLETE;
public RecyclerFragment() {
this.mCallback = null;
}
public void setAutocompleteBounds(LatLng centerLatLng){
if (centerLatLng!= null && !centerLatLng.toString().isEmpty()){
Double latSW, lngSW, lngNE, latNE;
latSW = centerLatLng.latitude-.5;
lngSW = centerLatLng.latitude-.5;
lngNE = centerLatLng.latitude+.5;
latNE = centerLatLng.latitude+.5;
BOUNDS_AUTOCOMPLETE = new LatLngBounds(
new LatLng(latSW, lngSW), new LatLng(latNE, lngNE));
} else {
BOUNDS_AUTOCOMPLETE = new LatLngBounds(
new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recycler, container, false);
try {
mGoogleApiClient = new GoogleApiClient.Builder(getContext())
.addApi(Places.GEO_DATA_API)
.enableAutoManage(getActivity(), GOOGLE_API_CLIENT_ID, this)
.addConnectionCallbacks(this)
.build();
} catch ( Exception e) {
Log.i(TAG, "onCreateView: " + e);
}
mPlaceArrayAdapter = new PlaceArrayAdapter(getContext(), android.R.layout.simple_list_item_1,
BOUNDS_AUTOCOMPLETE, null);
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()));
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fabAdd);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Data entry dialog to add runs
dialogInsertRun();
}
});
return view;
}
public void dialogInsertRun() {
// Get the Activity for layout inflater as this dialog runs inside a fragment
LayoutInflater inflater = LayoutInflater.from(getActivity());
final View view = 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(view);
// Data entry field objects
final AutoCompleteTextView collectAddressACTV = (AutoCompleteTextView) view.findViewById(R.id.actv_new_collect_address);
final EditText collectPersonEditText = (EditText) view.findViewById(R.id.new_collect_person);
collectAddressACTV.setThreshold(3);
collectAddressACTV.setOnItemClickListener(mAutocompleteClickListener);
collectAddressACTV.setAdapter(mPlaceArrayAdapter);
addRunDialog.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
RunData runData = new RunData();
runData.collect_person = getStringOrEmpty(collectPersonEditText);
runData.collect_address = getStringOrEmpty(collectAddressACTV);
if (!(runData.collect_address.isEmpty() && runData.delivery_address.isEmpty())) {
runData = runDbHelper.insertRun(runData, getActivity());
if (runData != null) {
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 onConnected(@Nullable Bundle bundle) {
mPlaceArrayAdapter.setGoogleApiClient(mGoogleApiClient);
Log.i(TAG, "Google Places API connected.");
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(TAG, "Google Places API connection failed with error code: "
+ connectionResult.getErrorCode());
Toast.makeText(getContext(),
"Google Places API connection failed with error code:" +
connectionResult.getErrorCode(),
Toast.LENGTH_LONG).show();
}
@Override
public void onConnectionSuspended(int i) {
mPlaceArrayAdapter.setGoogleApiClient(null);
Log.e(TAG, "Google Places API connection suspended.");
}
private AdapterView.OnItemClickListener mAutocompleteClickListener
= new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final PlaceArrayAdapter.PlaceAutocomplete item = mPlaceArrayAdapter.getItem(position);
final String placeId = String.valueOf(item.placeId);
Log.i(TAG, "Selected: " + item.description);
PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi
.getPlaceById(mGoogleApiClient, placeId);
placeResult.setResultCallback(mUpdatePlaceDetailsCallback);
Log.i(TAG, "Fetching details for ID: " + item.placeId);
}
};
@Override
public void onDestroyView() {
super.onDestroyView();
if(mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
mGoogleApiClient.stopAutoManage(getActivity());
mGoogleApiClient.disconnect();
}
}
private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback
= new ResultCallback<PlaceBuffer>() {
@Override
public void onResult(PlaceBuffer places) {
if (!places.getStatus().isSuccess()) {
Log.e(TAG, "Place query did not complete. Error: " +
places.getStatus().toString());
return;
}
// Selecting the first object buffer.
}
};
}
After entering the information, they go to the database and pro recyclerview, forming a list of cardviews.
Each cardview has an edit button. At the touch of a button, a dialog opens with the form. editing. At this time, I'd like to use the same address suggestion feature.
All editing is ready. Only this GoogleApiClient will work ...
The recyclerview adapter code is this:
class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder>
implements AlertDialog.OnClickListener,
GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ConnectionCallbacks{
private static final String TAG = "RecyclerViewAdapter";
private static String mCollect, mDelivery, mRunEnded, mUnknownDestiny, mIndeterminate;
private GoogleApiClient mGoogleApiClient;
private PlaceArrayAdapter mPlaceArrayAdapter;
private static final int GOOGLE_API_CLIENT_ID = 1;
private Context context;
private List<RunData> dataList = new ArrayList<>();
private LayoutInflater inflater;
private RunDbHelper runDbHelper;
private static final LatLngBounds BOUNDS_MOUNTAIN_VIEW = new LatLngBounds(
new LatLng(37.398160, -122.180831), new LatLng(37.430610, -121.972090));
private static LatLngBounds BOUNDS_AUTOCOMPLETE;
RecyclerViewAdapter(Context context, List<RunData> dataList1) {
mCollect = context.getString(R.string.str_collect).trim();
mDelivery = context.getString(R.string.str_delivery).trim();
mIndeterminate = context.getString(R.string.str_indeterminate);
mRunEnded = context.getString(R.string.this_run_ended);
mUnknownDestiny = context.getString(R.string.unknown_destiny);
this.context = context;
this.dataList = dataList1;
this.runDbHelper = RunDbHelper.getInstance(this.context);
inflater = LayoutInflater.from(context);
}
@Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.fragment_recycler_row, parent, false);
try {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(Places.GEO_DATA_API)
.enableAutoManage(XXXXXXXXXXXXXXXXXX, GOOGLE_API_CLIENT_ID, this) // nada do que tentei colocar aqui no lugar do XXXXXXX funcionou!
.addConnectionCallbacks(this)
.build();
} catch ( Exception e) {
Log.i(TAG, "onCreateView: " + e);
}
mPlaceArrayAdapter = new PlaceArrayAdapter(context, android.R.layout.simple_list_item_1,
BOUNDS_AUTOCOMPLETE, null);
return new RecyclerViewHolder(view);
}
// Parse data from dataList to holder and setup all Views
// Here is part of the magic
@Override
public void onBindViewHolder(final RecyclerViewHolder holder, final int position) {
// holding regular views
holder.runID.setText(dataList.get(position).run_id);
holder.collectPerson.setText(dataList.get(position).collect_person);
holder.collectAddress.setText(dataList.get(position).collect_address);
// this booleans help set some views on/off accordingly
Boolean collected = dataList.get(position).parcel_collected.equals("1");
Boolean collectAddressIsEmpty = dataList.get(position).collect_address.isEmpty();
holder.ivEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RunData runData = new RunData();
runData.run_id = holder.runID.getText().toString();
runData.collect_person = holder.collectPerson.getText().toString();
runData.collect_address = holder.collectAddress.getText().toString();
dialogEditRun(runData, position);
}
});
}
String makeSureLatLng(String lat, String lng) {
return (lat == null || lng == null || lat.isEmpty() || lng.isEmpty() ? null : lat + "," + lng);
}
boolean isUsable(TextView textView) {
return (!(textView == null || textView.getText().toString().isEmpty()));
}
@Override
public int getItemCount() {
return dataList.size();
}
public void dialogEditRun(RunData runData, int position) {
// Get the Activity for layout inflater as this dialog runs inside a fragment
LayoutInflater inflater = LayoutInflater.from(context);
final View inflaterView = inflater.inflate(R.layout.dialog_edit_run, null);
// Data entry field objects
final String mRunID;
final AutoCompleteTextView collectAddressACTV = (AutoCompleteTextView) inflaterView.findViewById(R.id.actv_edit_collect_address);
final EditText collectPersonEditText = (EditText) inflaterView.findViewById(R.id.edit_collect_person);
final TextView parcel_collected = (TextView) inflaterView.findViewById(R.id.editDelivered);
mRunID = runData.run_id;
parcel_collected.setText(runData.parcel_collected);
collectAddressACTV.setText(runData.collect_address);
collectPersonEditText.setText(runData.collect_person);
// Set directions into recyclerViewAdapter for autocomplete
collectAddressACTV.setThreshold(3);
collectAddressACTV.setOnItemClickListener(mAutocompleteClickListener);
collectAddressACTV.setAdapter(mPlaceArrayAdapter);
// Dialog Builder
AlertDialog.Builder editRunDialog = new AlertDialog.Builder(context);
editRunDialog.setTitle(R.string.dialog_update_run_title).setView(inflaterView);
editRunDialog.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
RunData runData = new RunData();
int position = (int) run_date.getTag();
runData.run_id = mRunID;
runData.collect_person = getStringOrEmpty(collectPersonEditText);
runData.collect_address = getStringOrEmpty(collectAddressACTV);
if (!runData.collect_address.isEmpty() ){
// try to update, if success update recycler.
if (runDbHelper.updateRun(runData, context)){
// atualiza o recyclerview
dataList.remove(position);
notifyItemRemoved(position);
dataList.add(position,runData);
notifyItemRangeChanged(position, dataList.size());
notifyItemInserted(position);
} else {
Toast.makeText(context, "Not updated record", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(context, R.string.dialog_insert_run_toast_nowhere, Toast.LENGTH_SHORT).show();
}
}
});
editRunDialog.setNegativeButton(R.string.button_negative, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
editRunDialog.create();
editRunDialog.show();
}
@Override
public void onConnected(@Nullable Bundle bundle) {
mPlaceArrayAdapter.setGoogleApiClient(mGoogleApiClient);
Log.i(TAG, "Google Places API connected.");
}
@Override
public void onConnectionSuspended(int i) {
mPlaceArrayAdapter.setGoogleApiClient(null);
Log.e(TAG, "Google Places API connection suspended.");
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.e(TAG, "Google Places API connection failed with error code: "
+ connectionResult.getErrorCode());
Toast.makeText(context,
"Google Places API connection failed with error code:" +
connectionResult.getErrorCode(),
Toast.LENGTH_LONG).show();
}
@Override
public void onClick(DialogInterface dialog, int which) {
}
class RecyclerViewHolder extends RecyclerView.ViewHolder {
TextView runID, collectPerson, collectAddress;
TextView collectLat, collectLng;
ImageView ivEdit;
SwitchCompat swcCollect;
RecyclerViewHolder(View rowView) {
super(rowView);
runID = (TextView) rowView.findViewById(R.id.runId);
ivEdit = (ImageView) rowView.findViewById(R.id.ivEdit);
collectPerson = (TextView) rowView.findViewById(R.id.collectPerson);
collectAddress = (TextView) rowView.findViewById(R.id.collectAddress);
}
}
private AdapterView.OnItemClickListener mAutocompleteClickListener
= new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final PlaceArrayAdapter.PlaceAutocomplete item = mPlaceArrayAdapter.getItem(position);
final String placeId = String.valueOf(item.placeId);
Log.i(TAG, "Selected: " + item.description);
PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi
.getPlaceById(mGoogleApiClient, placeId);
placeResult.setResultCallback(mUpdatePlaceDetailsCallback);
Log.i(TAG, "Fetching details for ID: " + item.placeId);
}
};
private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback
= new ResultCallback<PlaceBuffer>() {
@Override
public void onResult(PlaceBuffer places) {
if (!places.getStatus().isSuccess()) {
Log.e(TAG, "Place query did not complete. Error: " +
places.getStatus().toString());
return;
}
// Selecting the first object buffer.
}
};
}
I tried to follow the RecyclerFragment
template, but something is wrong and I can not instantiate GoogleApiClient
in onCreateView
method.
Note that I was using Google Places WebService and with this API it works, but it should not be used in app
in production, so I'm switching to Google Places API for Android.
Tomara, can you help me.