Je recadre ma dernière question, qui reste sans réponse, et j'ai réécrit le problème en suivant Google's BasicLocation .
Mon activité principale est définie comme:
E/MainActivity: Latit: 37.42342342342342
avec latlang. [Lat, Lang] est dans un fichier séparé :
I/System.out: Position:0 I/System.out: Position:1 I/System.out: Position:2
et le fichier de localisation, qui est le premier fragment du visualiseur est défini comme:
I/System.out: location null I/Google Maps Android API: Google Play services package version: 17785022 I/Choreographer: Skipped 31 frames! The application may be doing too much work on its main thread. I/System.out: Position:0 I/System.out: Position:1 I/System.out: Position:2 I/zygote: Do full code cache collection, code=202KB, data=177KB I/zygote: After code cache collection, code=129KB, data=91KB I/zygote: JIT allocated 56KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int) I/zygote: Background concurrent copying GC freed 44415(2MB) AllocSpace objects, 7(136KB) LOS objects, 49% free, 3MB/6MB, paused 294us total 102.458ms I/System.out: Position:3 I/System.out: Position:4 I/zygote: Do partial code cache collection, code=193KB, data=126KB I/zygote: After code cache collection, code=193KB, data=126KB I/zygote: Increasing code cache capacity to 1024KB I/zygote: JIT allocated 71KB for compiled code of void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int) I/zygote: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int) E/MainActivity: Latit: 37.42342342342342
Le problème avec cela est évident à partir du logcat
:
public class SunFragment extends Fragment { List<SunSession> sunsList; Typeface sunfont; //to be called by the MainActivity public SunFragment() { // Required empty public constructor } // Keys for storing activity state. // private static final String KEY_CAMERA_POSITION = "camera_position"; private static final String KEY_LOCATION_NAME = "location_name"; public String location;//="No location name found"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Retrieve location and camera position from saved instance state. if (savedInstanceState != null) { location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString(); System.out.println("OnCreate location "+location); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View rootView = inflater.inflate(R.layout.fragment_sun, container, false); onSaveInstanceState(new Bundle()); //SecondFragment secondFragment = new SecondFragment(); //secondFragment.getDeviceLocation(); RecyclerView rv = rootView.findViewById(R.id.rv_recycler_view); rv.setNestedScrollingEnabled(false); rv.setHasFixedSize(true); //MyAdapter adapter = new MyAdapter(new String[]{"Today", "Golden Hour", "Blue Hour", "Civil Twilight", "Nautical Twilight", "Astronomical Twilight", "Hello", "World"}); //rv.setAdapter(adapter); LinearLayoutManager llm = new LinearLayoutManager(getActivity()); rv.setLayoutManager(llm); System.out.println("location "+location); /* Reversegeocoding location */ String location="No location name found"; String errorMessage = ""; List<Address> addresses = null; Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault()); try { addresses = geocoder.getFromLocation( latlang.Lat, latlang.Lang, 1); } catch (IOException ioException) { // Catch network or other I/O problems. errorMessage = getString(R.string.service_not_available); // Log.e(TAG, errorMessage, ioException); if (getView() != null){ Snackbar.make(getView(), errorMessage, Snackbar.LENGTH_LONG).show(); } } catch (IllegalArgumentException illegalArgumentException) { // Catch invalid latitude or longitude values. errorMessage = getString(R.string.invalid_lat_long_used); if (getView() != null){ Snackbar.make(getView(), "Illegal Latitude = " + latlang.Lat + ", Longitude = " + latlang.Lang, Snackbar.LENGTH_LONG).show(); } } if (addresses == null || addresses.size() == 0) { if (errorMessage.isEmpty()) { System.out.println("Adress Empty No Address Found");// Snackbar.LENGTH_LONG).show(); location = "Lat:"+latlang.Lat+" Lang: "+latlang.Lang; } } else { location = addresses.get(0).getAddressLine(0);//+", "+ addresses.get(0).getLocality(); /* for(int i = 0; i <= addresses.get(0).getMaxAddressLineIndex(); i++) { location = addresses.get(0).getAddressLine(i); }*/ }
Cela montre, au début, l'emplacement est nul,
I / System.out: emplacement nul
puis la vue de recyclage du sunfragment est créée
public class latlang { public static double Lat; public static double Lang; }
et après cela, j'obtiens l'emplacement:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { // private LocationCallback locationCallback; // private FusedLocationProviderClient mFusedLocationClient; private FusedLocationProviderClient mFusedLocationClient; protected Location mLastLocation; private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.drawer_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager()); ViewPager viewPager = findViewById(R.id.view_pager); viewPager.setAdapter(sectionsPagerAdapter); DrawerLayout drawer = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); toggle.syncState(); navigationView.setNavigationItemSelectedListener(this); ImageButton leftNav = findViewById(R.id.left_nav); ImageButton rightNav = findViewById(R.id.right_nav); leftNav.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int tab = viewPager.getCurrentItem(); if (tab > 0) { tab--; viewPager.setCurrentItem(tab); } else if (tab == 0) { viewPager.setCurrentItem(tab); } } }); rightNav.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int tab = viewPager.getCurrentItem(); tab++; viewPager.setCurrentItem(tab); } }); } @Override public void onStart() { super.onStart(); if (!checkPermissions()) { requestPermissions(); } else { getLastLocation(); } }
5 Réponses :
J'ai trouvé quelque chose qui ne va pas dans votre code (je me trompe peut-être):
Pourquoi les champs de latlang
sont statiques
? Il ne semble pas qu'ils devraient le faire.
Dans SunFragment.onCreate ()
, vous lisez location
si savedInstanceState! = null
. savedInstanceState
n'est pas nul uniquement si l'activité contenant ce fragment a été restaurée à partir de l'état enregistré. Cela peut ne pas arriver du tout.
Vous devez utiliser les arguments
(Bundle) du fragment pour transmettre les données initiales au fragment
Vous devez implémenter l'interface Parcelable
pour latlang
afin de pouvoir passer une classe personnalisée via Bundle
Je pense que ce n'est pas tout, mais pour moi, cela semble suffisant pour que ce code ne fonctionne pas comme prévu
Comme indiqué dans la réponse précédente, il y a beaucoup de problèmes dans votre code.
En dehors de cela, sachez que le dernier emplacement connu peut ne pas toujours renvoyer une valeur. Le GPS a essentiellement deux types de données: les éphémérides (données de navigation précises) et l'almanach (données grossières). Maintenant, lorsque le récepteur est démarré à froid, c'est-à-dire après que le GPS a été éteint pendant plus de 8 à 10 minutes, il n'y a pratiquement pas de dernier emplacement connu (la durée peut varier en fonction de l'appareil, mais l'idée de base est la suivante).
Ainsi, lorsque vous n'obtenez pas le dernier emplacement connu, récupérez l'emplacement réel en direct à l'aide du client fusionné. De plus, puisque vous enregistrez les données dans les préférences partagées et que vous les récupérez dans votre fragment, je pense que votre fragment va fortement s'appuyer sur ces données. Je suggérerais donc l'une des deux approches suivantes pour obtenir un résultat correct
1) ne récupère pas du tout l'emplacement dans l'activité d'imbrication. Faites-le simplement dans le fragment où il est nécessaire. Cela ne fonctionnera pas si d'autres fragments de votre visionneur ont également besoin de l'emplacement.
2) Si vous devez avoir l'emplacement dans votre activité et qu'il s'agit d'une dépendance dans les fragments de conteneur, vous pouvez également utiliser deux approches ici. Mes approches reposent sur le bus événementiel. Bus événementiel, otto ou rxbus, tout fera l'affaire
2a) n'ajoute rien au visualiseur. en gros, commencez par récupérer entièrement l'emplacement, puis ajoutez des éléments au visualiseur une fois que vous avez obtenu le rappel de l'emplacement.
2b) Ajoutez des éléments au visualiseur depuis le début. Dans l'activité, une fois que vous avez obtenu l'emplacement, utilisez le bus d'événements pour informer les fragments de la même chose et pour obtenir l'événement dans les fragments, commencez réellement ce que vous devez faire.
J'ai déjà utilisé ces deux approches et tout fonctionne. Il appartient désormais entièrement à votre cas d'utilisation d'utiliser ce qui vous convient. Quoi qu'il en soit, c'est un code trop long et trop compliqué pour tout publier ici.
En ce moment, vous utilisez comme ceci:
@Override public void onStart() { super.onStart(); if (!checkPermissions()) { requestPermissions(); } else { if(getLastLocation() != null){ //replace your fragment }else{//Null Location} } }
Dans votre instruction else
vérifiez si getLastLocation ()
n'est pas nul. s'il n'est pas nul, remplacez le fragment.
@Override public void onStart() { super.onStart(); if (!checkPermissions()) { requestPermissions(); } else { getLastLocation(); } }
Utilisez une classe de service pour gérer précisément le service de localisation. Ici, je vous donne une classe LocationService personnalisée que j'ai utilisée dans de nombreux projets pour collecter les données de localisation en arrière-plan en continu
LocationService.kt
import android.Manifest import android.app.NotificationChannel import android.app.NotificationManager import android.app.Service import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Binder import android.os.Build import android.os.IBinder import android.os.Looper import android.util.Log import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationCallback import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationResult import com.google.android.gms.location.LocationServices class LocationService : Service() { //Custom Location Binder class inner class LocationBinder : Binder() { val locationService: LocationService get() = this@LocationService } companion object { val TAG = "LocationService_123" private val UPDATE_INTERVAL = (4 * 1000).toLong() /* 4 secs */ private val FASTEST_INTERVAL: Long = 2000 /* 2 sec */ private var INSTANCE: LocationService? = null fun isInstanceCreated(): Boolean { return (INSTANCE != null) } } private var mFusedLocationClient: FusedLocationProviderClient? = null private var mLocationListener: LocationListener? = null private var mLocationBinder = LocationBinder() fun setLocationListener(locationListener: LocationListener) { this.mLocationListener = locationListener } override fun onBind(intent: Intent): IBinder? { return mLocationBinder } override fun onCreate() { INSTANCE = this super.onCreate() mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this) if (Build.VERSION.SDK_INT >= 26) { val CHANNEL_ID = "ostad_gps" val channel = NotificationChannel( CHANNEL_ID, "Ostad GPS", NotificationManager.IMPORTANCE_DEFAULT) (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel) val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("") .setContentText("").build() startForeground(1, notification) } } override fun onDestroy() { INSTANCE = null super.onDestroy() } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Log.d(TAG, "onStartCommand: called.") getLocation() return Service.START_NOT_STICKY } private fun getLocation() { // ---------------------------------- LocationRequest ------------------------------------ // Create the location request to start receiving updates val mLocationRequestHighAccuracy = LocationRequest() mLocationRequestHighAccuracy.priority = LocationRequest.PRIORITY_HIGH_ACCURACY mLocationRequestHighAccuracy.interval = UPDATE_INTERVAL mLocationRequestHighAccuracy.fastestInterval = FASTEST_INTERVAL // new Google API SDK v11 uses getFusedLocationProviderClient(this) if (ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { Log.d(TAG, "getLocation: stopping the location mitchService.") stopSelf() return } Log.d(TAG, "getLocation: getting location information.") mFusedLocationClient!!.requestLocationUpdates( mLocationRequestHighAccuracy, object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult?) { val location = locationResult!!.lastLocation Log.d(TAG, "onLocationResult: got location result: $location") if (location != null) { if (mLocationListener != null) mLocationListener!!.onLocationChanged(location) } } }, Looper.myLooper() ) // Looper.myLooper tells this to repeat forever until thread is destroyed } }
ajoutez ceci dans votre AndroidManifest et démarrez le LocationService à partir de votre MainActivity.
En regardant le code, il semble que vous n'ayez pas besoin d'un emplacement très précis, vous serez d'accord avec le dernier emplacement connu. Cette valeur peut être nulle dans certains cas, comme vous l'avez déjà fait. La réponse simple à votre question est non, vous ne pouvez pas obtenir un emplacement non nul avant de créer SunFragment
. Les étapes suivantes consistent à charger l'emplacement en arrière-plan et à mettre à jour l'interface utilisateur une fois trouvée.
SunFragment
pour suivre les mises à jour de localisation Voici quelques codes dont vous avez besoin (veuillez les lire)
Utilisez LocationUtil
pour gérer les événements liés à la localisation (I préférez LocationManager
à FusedLocationProviderClient
);
sunsList.add( new SunSession( AppCache.getLastLocation("Searching location..."), "", sun_rise, "", sun_set, "", moon_rise, "", moon_set));
Utilisez AppCache
pour stocker le dernier emplacement;
@Override public void onStart() { super.onStart(); AppCache.registerPreferenceChangeListener(mPreferenceChangeListener); } @Override public void onStop() { super.onStop(); AppCache.unregisterPreferenceChangeListener(mPreferenceChangeListener); }
Mettez le code suivant dans votre MainActivity
onCreate ()
Cela appellera locationManager pour obtenir le dernier emplacement connu et mettre à jour l'application cache.
SunAdapter mAdapter; SharedPreferences.OnSharedPreferenceChangeListener mPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if(AppCache.KEY_LAST_LOCATION.equalsIgnoreCase(key)) { // location value has change, update data-set SunSession sunSession = sunsList.get(0); sunSession.setId(sharedPreferences.getString(key, "No Location")); sunsList.add(0, sunSession); mAdapter.notifyDataSetChanged(); } } };
Remplacez également fetchLocation ();
dans la méthode onRequestPermissionsResult
par la ligne de code ci-dessus, pour qu'elle ressemble comme;
public class LocationLite { public double lat; public double lon; public String address; }
Je n'ai pas utilisé votre classe latlang. (Veuillez vous assurer que tous les noms de classe suivent les normes de codage Java) Utilisez plutôt LocationLite
pour stocker l'emplacement dans le cache. J'ai également utilisé la bibliothèque google GSON pour convertir et restaurer pojo en JSON et en arrière.
@Override public void onRequestPermissionsResult(...){ switch (requestCode) { case 101: { ... // permission was granted //fetchLocation(); LocationUtil.updateLastKnownLocation(MainActivity.this); } else { // Show some error } return; } } }
Modifications finales dans SunFragment
.
Définissez SunAdapter
comme variable membre et SharedPreferences.OnSharedPreferenceChangeListener
pour écouter les modifications apportées à la valeur de l'emplacement.
LocationUtil.updateLastKnownLocation(MainActivity.this);
Vous devez soit retarder la création du fragment jusqu'à ce que
mLastLocation
soit initialisé pargetLastLocation
. Ou mettre à jour le fragment une fois quemLastLocation
est initialisé.Voulez-vous dire quelque chose comme
onsuccesslistner
? Je suis très nouveau sur Java, alors pouvez-vous nous aider un peu plus?Pas tout à fait en utilisant un auditeur. Il vous suffit d'ajouter une méthode pour informer le fragment des changements d'emplacement. Comment créez-vous le fragment? Est-ce via votre
SectionsPagerAdapter
?Oui. Fondamentalement, je l'ai créé dans android studio-> nouveau projet-> activité à onglets, et je viens de remplacer l'espace réservé de la sectionpageradapter par le sunFragment.
Bonjour, si quelqu'un est intéressé, je suis prêt à télécharger le code complet. Mais, aidez-moi gentiment à résoudre ce problème.
pourquoi vous ne pouvez pas enregistrer le dernier emplacement dans sharedpreference ??? et Appelez l'emplacement dans MainActivity et transmettez-le en fragment plutôt que pour obtenir le loc en fragment.
veuillez télécharger le code complet afin que nous ayons une idée de ce que vous faites
@BaRud Veuillez télécharger le code de la fonction
getLocation
dans l'activité? Cela nous aidera à savoir ce qui se passe exactement. 1. Je peux voir ce que fait le code sur les google-samples , mais dans votre cas, je ne vois pas comment vous poussez cet emplacement vers le fragment. 2. L'emplacement prendra plus de temps pour être reçu de l'appareil. Le fragment aura déjà commencé, vous devez donc ajouter un écouteur à l'activité dans le fragment afin que vous puissiez recevoir des mises à jour d'emplacement.Salut, le lien du code complet est joint. Veuillez jeter un œil.
Bonjour, j'ai téléchargé et exécuté votre code, je veux juste comprendre ce que vous essayez d'accomplir ici sur
SunFragment
. Comme je peux le voir, il y a recyclerview et la première entrée dit «Recherche d'emplacement», voulez-vous vous assurer que l'emplacement y arrive tout le temps?ddassa: oui ... exactement