Recyclerview 适配器上的 GoogleAPIClient

GoogleAPIClient on recyclerview adapter

此应用程序有一个片段,可在单个 table 数据库上执行 CRUD 操作。

在绘制 FAB 和 recyclerview 的 class 上完成。

FAB 打开带有插入表单的 alertDialog。

recyclerview 将数据库行显示为卡片视图。每个卡片视图都有一个编辑按钮和一个删除按钮。

编辑按钮打开一个带有编辑表单的警告对话框。

数据库存储每个都有起点和终点的旅程。

当输入或编辑地点时,用户会得到建议。

为此我使用了 Google Places Web 服务 API 但我注意到我必须使用 Google 为 Android 放置 API,因此 GoogleApiClient

请注意,该应用程序运行良好,成功执行了 CRUD,甚至从 Web 服务获得了方向建议。

我能够使插入表单使用正确的 API,但不能使编辑表单使用。

片段代码如下:

public class RecyclerFragment extends Fragment
        implements AlertDialog.OnClickListener,
        GoogleApiClient.OnConnectionFailedListener,
        GoogleApiClient.ConnectionCallbacks {

    private static final String TAG = "RecyclerFragment";
    RunDbHelper runDbHelper; // Database helper class
    RecyclerView recyclerView;
    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;
    }

    // main activity calls this method to pass the location so it can calculate bounds for the direction suggestions
    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 = BOUNDS_MOUNTAIN_VIEW;
        }
    }

    @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 insert new runs into database
                dialogInsertRun();
            }
        });
        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 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 EditText runParcelEditText = (EditText) view.findViewById(R.id.new_run_parcel);
        final AutoCompleteTextView collectAddressACTV = (AutoCompleteTextView) view.findViewById(R.id.actv_new_collect_address);

        final EditText collectPersonEditText = (EditText) view.findViewById(R.id.new_collect_person);
        final AutoCompleteTextView deliveryAddressACTV = (AutoCompleteTextView) view.findViewById(R.id.actv_new_delivery_address);
        final EditText deliveryPersonEditText = (EditText) view.findViewById(R.id.new_delivery_person);

        collectAddressACTV.setThreshold(3);
        collectAddressACTV.setOnItemClickListener(mAutocompleteClickListener);
        collectAddressACTV.setAdapter(mPlaceArrayAdapter);

        deliveryAddressACTV.setThreshold(3);
        deliveryAddressACTV.setOnItemClickListener(mAutocompleteClickListener);
        deliveryAddressACTV.setAdapter(mPlaceArrayAdapter);

        addRunDialog.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int id) {

                RunData runData = new RunData(); // POJO that holds data from database

                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);

                // must insert at least one of the places (starting or ending point) or both
                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;
    }

    @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.");
    }

    public interface OnOkButtonListener {
        void addMarkersToMap(RunData runData);
    }

    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.
        }
    };
}

这里是适配器代码:

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {

private static final String TAG = "RecyclerViewAdapter";

private Context context;
private List<RunData> dataList = new ArrayList<>();
private LayoutInflater inflater;
private RunDbHelper runDbHelper;

RecyclerViewAdapter(Context context, List<RunData> dataList1) {

    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 inflateView = inflater.inflate(R.layout.fragment_recycler_row, parent, false);
    return new RecyclerViewHolder(inflateView);
}

@Override
public void onBindViewHolder(final RecyclerViewHolder holder, final int position) {

    holder.runID.setText(dataList.get(position).run_id);
    holder.collectAddress.setText(dataList.get(position).collect_address);
    holder.deliveryAddress.setText(dataList.get(position).delivery_address);     

    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_address = holder.collectAddress.getText().toString();
            runData.delivery_address = holder.deliveryAddress.getText().toString();                
            dialogEditRun(runData, position);
        }
    });
}

@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 AutoCompleteTextView deliveryAddressACTV = (AutoCompleteTextView) inflaterView.findViewById(R.id.actv_edit_delivery_address);

    mRunID = runData.run_id;
    collectAddressACTV.setText(runData.collect_address);
    deliveryAddressACTV.setText(runData.delivery_address);

    // HERE THE FIELDS AND THE SUGGESTIONS ARE LINKED
    // Set directions into recyclerViewAdapter for autocomplete
    // here is still using G. Places WebService API. Should be G. Places API for Android, using GoogleApiClient
    collectAddressACTV.setAdapter(new GooglePlacesAutocompleteAdapter(context, R.layout.dialog_new_run_autocomplete));
    deliveryAddressACTV.setAdapter(new GooglePlacesAutocompleteAdapter(context, R.layout.dialog_new_run_autocomplete));

    // 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_address = getStringOrEmpty(collectAddressACTV);
            runData.delivery_address = getStringOrEmpty(deliveryAddressACTV);

            if (!(runData.collect_address.isEmpty() && runData.delivery_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, "No record updated", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(context, "Inform at least one direction", 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();
}

private String getStringOrEmpty(EditText editText) {
    String mString = editText.getText().toString();
    mString = (mString.isEmpty() ? "" : mString);
    return mString;
}

class RecyclerViewHolder extends RecyclerView.ViewHolder {
    TextView runID, collectAddress, deliveryAddress;
    ImageView ivEdit;

    RecyclerViewHolder(View rowView) {
        super(rowView);
        runID = (TextView) rowView.findViewById(R.id.runId);
        collectAddress = (TextView) rowView.findViewById(R.id.collectAddress);
        deliveryAddress = (TextView) rowView.findViewById(R.id.deliveryAddress);
        ivEdit = (ImageView) rowView.findViewById(R.id.ivEdit);
    }
}
}

当我尝试添加代码以实例化 reclycler 适配器内的 GoogleApiClient 时,在 onCreateViewHolder(ViewGroup parent, int viewType), Android 工作室不会让我。我找不到 enableAutoManage

的正确参考
            mGoogleApiClient = new GoogleApiClient.Builder(context)
                    .addApi(Places.GEO_DATA_API)
                    .enableAutoManage(HEREISTHEERROR, GOOGLE_API_CLIENT_ID, this) // nada do que tentei colocar aqui no lugar do XXXXXXX funcionou!
                    .addConnectionCallbacks(this)
                    .build();

总而言之,我想要完成的是在向显示在 alertDialog 中的表单中添加或编辑路线数据时收到建议。

必须为 android 使用 Google 个位置 API,因此 GoogleApiClient。

不确定这是正确的方法,所以...欢迎任何建议或解决方法。

我已经解决了这个问题。

enableAutoManage()只能有一个GoogleApiClient。对 API 的其他调用必须处理生命周期。

Accessing Google APIs