问题
Following is my adapter for a RecyclerView
where i need to update one of the TextView
which shows distance of current location to another location .
Now , to get the current location , i am using mFusedLocationClient.requestLocationUpdates(mFusedLocationRquest,mLocationCallback,null);
If i am going with the below code and have just enabled gps in app with runtime permission , i face NPE on line :
float distance = updatedLoc.distanceTo(des);
App does not crash when i open with gps already enabled (probably because the last known loc is not null in that case ).
So to make sure that updatedLoc
is not null , i want to make sure that my TextView
is updated after the location callback only , how would that be possible ?
In this API's callback , createLocationCallback()
, i am unable to update the TextView properly (tried using holder as global var , but it was messy and items weren't updated properly .)
Thanks in advance.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.PlaceViewHolder> implements android.location.LocationListener{
private static final long LOCATION_REFRESH_TIME = 2000;
private static final float LOCATION_REFRESH_DISTANCE = 100;
private static final int MY_PERMISSION_ACCESS_COURSE_LOCATION = 1001;
private Context mContext;
private PlaceBuffer mPlaces;
Location updatedLoc = null;
PlaceViewHolder myHolder;
boolean canGetLocation = false;
android.location.LocationListener locationListener;
double lat1,long1;
MySimpleCallback mySimpleCallback;
String dist;
private FusedLocationProviderClient mFusedLocationClient;
private LocationRequest mFusedLocationRquest;
private LocationCallback mLocationCallback;
/**
* Creates a callback for receiving location events.
*/
private void createLocationCallback() {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
updatedLoc = locationResult.getLastLocation();
Log.d("testingvalue",String.valueOf(updatedLoc==null));
//mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
}
};
}
/**
* Constructor using the context and the db cursor
*
* @param context the calling context/activity
*/
public PlaceListAdapter(Context context, PlaceBuffer places) {
this.mContext = context;
this.mPlaces = places;
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
createLocationCallback();
createLocationRequest();
if (ActivityCompat.checkSelfPermission(mContext, android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions((Activity) mContext,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
101);
}
mFusedLocationClient.requestLocationUpdates(mFusedLocationRquest,mLocationCallback,null);
}
private void createLocationRequest() {
mFusedLocationRquest = new LocationRequest();
// Sets the desired interval for active location updates. This interval is
// inexact. You may not receive updates at all if no location sources are available, or
// you may receive them slower than requested. You may also receive updates faster than
// requested if other applications are requesting location at a faster interval.
mFusedLocationRquest.setInterval(100);
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates faster than this value.
mFusedLocationRquest.setFastestInterval(100);
mFusedLocationRquest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Called when RecyclerView needs a new ViewHolder of the given type to represent an item
*
* @param parent The ViewGroup into which the new View will be added
* @param viewType The view type of the new View
* @return A new PlaceViewHolder that holds a View with the item_place_card layout
*/
@Override
public PlaceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Get the RecyclerView item layout
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.item_place_card, parent, false);
return new PlaceViewHolder(view);
}
/**
* Binds the data from a particular position in the cursor to the corresponding view holder
*
* @param holder The PlaceViewHolder instance corresponding to the required position
* @param position The current position that needs to be loaded with data
*/
@Override
public void onBindViewHolder(PlaceViewHolder holder, int position) {
String placeName = mPlaces.get(position).getName().toString();
String placeAddress = mPlaces.get(position).getAddress().toString();
double lat = mPlaces.get(position).getLatLng().latitude;
double lng = mPlaces.get(position).getLatLng().longitude;
//Location currentLocation = getCurrentLocation();
//Location currLoc = new GPSTracker(mContext).getLocation();
//Toast.makeText(mContext, "distance is" + dist + "km", Toast.LENGTH_SHORT).show();
Location des = new Location("destination");
des.setLatitude(lat);
des.setLongitude(lng);
float distance = updatedLoc.distanceTo(des);
distance = distance / 1000;
dist = "";
if (distance < 1) {
dist = "less than km away";
} else {
dist = String.format("%.2f", distance) + " km";
}
holder.nameTextView.setText(placeName);
holder.addressTextView.setText(dist);
holder.taskTextView.setText(MainActivity.preference.getString(mPlaces.get(position).getId(), "chv"));
}
public void swapPlaces(PlaceBuffer newPlaces) {
mPlaces = newPlaces;
if (mPlaces != null) {
// Force the RecyclerView to refresh
this.notifyDataSetChanged();
}
}
/**
* Returns the number of items in the cursor
*
* @return Number of items in the cursor, or 0 if null
*/
@Override
public int getItemCount() {
if (mPlaces == null) {
Log.d("countcheck", "zero bro");
return 0;
}
return mPlaces.getCount();
}
@Override
public void onLocationChanged(Location location) {
updatedLoc = location;
}
@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
@Override
public void onProviderEnabled(String s) {
}
@Override
public void onProviderDisabled(String s) {
}
/**
* PlaceViewHolder class for the recycler view item
*/
class PlaceViewHolder extends RecyclerView.ViewHolder {
TextView nameTextView;
TextView addressTextView;
TextView taskTextView;
public PlaceViewHolder(View itemView) {
super(itemView);
nameTextView = (TextView) itemView.findViewById(R.id.name_text_view);
addressTextView = (TextView) itemView.findViewById(R.id.address_text_view);
taskTextView = (TextView) itemView.findViewById(R.id.task_text_view);
}
}
}
回答1:
When the onBindViewHolder
is called, updatedLoc
is still null, means location provider has not received location yet.
You should wrap the updatedLoc
in onBindViewHolder
against to null case like below
if(updatedLoc != null){
float distance = updatedLoc.distanceTo(des);
distance = distance / 1000;
dist = "";
if (distance < 1) {
dist = "less than km away";
} else {
dist = String.format("%.2f", distance) + " km";
}
} else {
dist = "Waiting for location...";
}
holder.nameTextView.setText(placeName);
holder.addressTextView.setText(dist);
And whenever you receive the location, onBindViewHolder
must be called again to calculate and show distance instead of Waiting for location...
So you need to update createLocationCallback
method. Just add notifyDataSetChanged()
private void createLocationCallback() {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
updatedLoc = locationResult.getLastLocation();
Log.d("testingvalue",String.valueOf(updatedLoc==null));
//mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
notifyDataSetChanged();
}
};
}
回答2:
I'm not sure but I can't see notifyDataSetChanged()
inside your callback.
Try to add it.
来源:https://stackoverflow.com/questions/45209085/android-how-to-use-mfusedlocationclient-callback-resulting-location-in-curre