问题
I'm new to android. I am currently developing an app that displays multiple locations on a map which has its marker locations taken from a central database that I have already set up and I know is working how can I fix my problem.
Here is my code for MainActivity.java
package com.example.defiblocator;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import org.w3c.dom.Text;
public class MainActivity extends FragmentActivity implements OnMapReadyCallback,
LocationListener,GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
GoogleMap mapAPI;
SupportMapFragment mapFragment;
Location mLastLocation;
Marker mCurrLocationMarker;
GoogleApiClient mGoogleApiClient;
LocationRequest mLocationRequest;
public static TextView data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fetchData process = new fetchData();
process.execute();
mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.mapAPI);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mapAPI = googleMap;
LatLng fox = new LatLng(52.187907,-0.143284);
mapAPI.addMarker(new MarkerOptions().position(fox).title("test"));
mapAPI.moveCamera(CameraUpdateFactory.newLatLng(fox));
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
buildGoogleApiClient();
mapAPI.setMyLocationEnabled(true);
}
} else {
buildGoogleApiClient();
mapAPI.setMyLocationEnabled(true);
}
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
mGoogleApiClient.connect();
}
@Override
public void onConnected(Bundle bundle) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onLocationChanged(Location location) {
mLastLocation = location;
if (mCurrLocationMarker != null) {
mCurrLocationMarker.remove();
}
//Place current location marker
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(latLng);
markerOptions.title("Current Position");
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
mCurrLocationMarker = mapAPI.addMarker(markerOptions);
//move map camera
mapAPI.moveCamera(CameraUpdateFactory.newLatLng(latLng));
mapAPI.animateCamera(CameraUpdateFactory.zoomTo(11));
//stop location updates
if (mGoogleApiClient != null) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
}
And this is my code for fetching the data from the database and converting it into a JSON.
fetchData.java
package com.example.defiblocator;
import android.os.AsyncTask;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class fetchData extends AsyncTask <Void,Void,Void>{
String data = "";
String json_url;
String dataParsed = "";
String singleParsed = "";
public Integer x = 0;
@Override
protected void onPreExecute(){
json_url = "http://defiblocator.ml/json_get_data.php";
}
@Override
protected Void doInBackground(Void... voids) {
try {
URL url = new URL(json_url);
HttpURLConnection httpURLConnection = (HttpURLConnection)url.openConnection();
InputStream inputStream = httpURLConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
data = data + line;
while(line != null){
line = bufferedReader.readLine();
}
JSONArray JA = new JSONArray(data);
for(int i =0 ; i <JA.length(); i++){
JSONObject JO = (JSONObject) JA.get(i);
singleParsed = JO.get("name") + "," + JO.get("lat") + "," +JO.get("lng") + ",";
dataParsed = dataParsed + singleParsed ;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException | JSONException e){
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
MainActivity.data.setText(dataParsed);
}
}
My error only occurs in PostExecute() when I try and send my converted data back to the main activity fro processing into name, latitude, and longitude. I will do this with 3 separate arrays if there is an easier method than what I have please share but otherwise any help would be greatly appreciated as I believe I can change the text view into a string and split it into its constituent parts from there.
回答1:
As mentioned in the comments, you need to implement a callback using an interface to use the results of an Async Task. First create an interface.
New -> Jave Class -> Interface -> CallbackInterface .java. Define the method prototypes.
CallbackInterface.java
public interface CallbackInterface {
public void onSuccess(String callbackData);
public void onFailure();
}
In your MainActivity, define a class which implements the interface. Note that, the methods must be defined here and must contain the code to make use of the result of your Async Task MainActivity.java
public class MainActivity extends AppCompatActivity {
TextView data;
Button performAsync;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Add this without fail.
data = findViewById(R.id.dataTV);
performAsync = findViewById(R.id.performAsync);
performAsync.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Recreating your "process"
new AsyncTaskClass(new CallbackClass()).execute();
}
});
}
/*Create a class which implements the interface. It is a good practise to have
onSuccess and onFailure methods */
public class CallbackClass implements CallbackInterface {
@Override
public void onSuccess(String callbackData) {
//do your callback functions here. In your case, setting the textview to result of parsed data.
data.setText(callbackData);
}
@Override
public void onFailure() {
}
}
}
Finally, in your fetchData class, (AsyncTaskClass in my code) , do your asynchronous operations and finally in postExecute, call the onSuccess method with appropriate parameters. Note that you should pass the class as a parameter to the fetchData class and using the constructor, you must assign it to the instance of the interface. AsyncTaskClass.java
public class AsyncTaskClass extends AsyncTask {
CallbackInterface callbackInterface;
String dataParsed;
public AsyncTaskClass(CallbackInterface callbackInterface) {
this.callbackInterface = callbackInterface;
}
@Override
protected Object doInBackground(Object[] objects) {
dataParsed = "Processed Data";
return null;
}
@Override
protected void onPreExecute() {
dataParsed = "";
}
@Override
protected void onPostExecute(Object o) {
callbackInterface.onSuccess(dataParsed);
}
}
While this may seem too much, this is the only way you can communicate between the AsyncTaskClass and the Class which makes use of it. Feel free to ask for any clarifications in case you can't understand. I checked the above answer in my project and it works perfectly.
回答2:
A simpler alternative to the callback approach would be to provide the fetchData
with a reference to the TextView. This solution also highlights that you omitted the retrieval of the TextView initially.
So in MainActivity.onCreate()
:
@Override
protected void onCreate(Bundle savedInstanceState) {
//....
fetchData process = new fetchData((TextView)findViewById(R.id.<text_view_id_in_xml>);
//...
}
And in fetchData
add a constructor to accept the text view:
public class fetchData extends AsyncTask <Void,Void,Void>{
//....
private TextView myTv;
public fetchData(TextView tv) {
super();
myTv = tv;
}
//....
@Override
protected void onPostExecute(Void aVoid) {
myTv.setText(dataParsed);
myTv = null;
}
}
You may find the callback approach provided in other solutions more generally suitable for many situations but in this specific case this approach seems simpler.
Note also that AsyncTask
has been deprecated in Release R.
Also, in your onLocationChanged
you don't have to remove/add a marker to move it - you can just update the saved marker position:
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
if (mCurrLocationMarker != null) {
// previously added - move it
mCurrLocationMarker.setPosition(latLng);
} else {
// first time - add it - same as OP code.
}
来源:https://stackoverflow.com/questions/62213662/how-can-i-fix-void-android-widget-textview-settextjava-lang-charsequence-on