Android
Orientatiewijzigingen
Zoeken…
Opmerkingen
Activiteitstatus opslaan en herstellen
Wanneer uw activiteit begint te stoppen, roept het systeem onSaveInstanceState()
zodat uw activiteit onSaveInstanceState()
kan opslaan met een verzameling sleutel / waarde-paren. De standaardimplementatie van deze methode slaat automatisch informatie op over de status van de weergavehiërarchie van de activiteit, zoals de tekst in een EditText
widget of de schuifpositie van een ListView
.
Als u aanvullende onSaveInstanceState()
voor uw activiteit wilt opslaan, moet u onSaveInstanceState()
implementeren en sleutel / waarde-paren toevoegen aan het Bundle-object. Bijvoorbeeld:
public class MainActivity extends Activity {
static final String SOME_VALUE = "int_value";
static final String SOME_OTHER_VALUE = "string_value";
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
// Save custom values into the bundle
savedInstanceState.putInt(SOME_VALUE, someIntValue);
savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
}
Het systeem roept die methode aan voordat een activiteit wordt vernietigd. Dan zal het systeem later onRestoreInstanceState
waar we de status van de bundel kunnen herstellen:
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
someIntValue = savedInstanceState.getInt(SOME_VALUE);
someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}
De instantiestatus kan ook worden hersteld in de standaard Activity # onCreate-methode, maar het is handig om dit te doen in onRestoreInstanceState
waardoor alle initialisatie is uitgevoerd en subklassen kunnen beslissen of de standaardimplementatie wordt gebruikt. Lees dit stapeloverloopbericht voor meer informatie.
Merk op dat onSaveInstanceState
en onRestoreInstanceState
niet gegarandeerd samen worden gebeld. Android roept onSaveInstanceState()
wanneer de kans bestaat dat de activiteit wordt vernietigd. Er zijn echter gevallen waarin onSaveInstanceState
wordt aangeroepen maar de activiteit niet wordt vernietigd en als gevolg hiervan op onRestoreInstanceState
niet wordt aangeroepen.
Fragmentstatus opslaan en herstellen
Fragmenten hebben ook een methode onSaveInstanceState()
die wordt aangeroepen wanneer hun status moet worden opgeslagen:
public class MySimpleFragment extends Fragment {
private int someStateValue;
private final String SOME_VALUE_KEY = "someValueToSave";
// Fires when a configuration change occurs and fragment needs to save state
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SOME_VALUE_KEY, someStateValue);
super.onSaveInstanceState(outState);
}
}
Vervolgens kunnen we gegevens uit deze opgeslagen status halen in onCreateView
:
public class MySimpleFragment extends Fragment {
// ...
// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_simple_fragment, container, false);
if (savedInstanceState != null) {
someStateValue = savedInstanceState.getInt(SOME_VALUE_KEY);
// Do something with value if needed
}
return view;
}
}
Om de fragmentstatus goed te kunnen opslaan, moeten we er zeker van zijn dat we het fragment niet onnodig opnieuw maken bij configuratiewijzigingen. Dit betekent dat je ervoor moet zorgen dat je bestaande fragmenten niet opnieuw initialiseert wanneer ze al bestaan. Fragmenten die in een activiteit worden geïnitialiseerd, moeten na een configuratiewijziging per tag worden opgezocht:
public class ParentActivity extends AppCompatActivity {
private MySimpleFragment fragmentSimple;
private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";
@Override
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) { // saved instance state, fragment may exist
// look up the instance that already exists by tag
fragmentSimple = (MySimpleFragment)
getSupportFragmentManager().findFragmentByTag(SIMPLE_FRAGMENT_TAG);
} else if (fragmentSimple == null) {
// only create fragment if they haven't been instantiated already
fragmentSimple = new MySimpleFragment();
}
}
}
Dit vereist dat we ervoor zorgen dat we een tag opnemen om op te zoeken wanneer we een fragment in de activiteit in een transactie plaatsen:
public class ParentActivity extends AppCompatActivity {
private MySimpleFragment fragmentSimple;
private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... fragment lookup or instantation from above...
// Always add a tag to a fragment being inserted into container
if (!fragmentSimple.isInLayout()) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragmentSimple, SIMPLE_FRAGMENT_TAG)
.commit();
}
}
}
Met dit eenvoudige patroon kunnen we fragmenten correct hergebruiken en hun status herstellen via configuratiewijzigingen.
Fragmenten behouden
In veel gevallen kunnen we problemen voorkomen wanneer een activiteit opnieuw wordt gemaakt door eenvoudigweg fragmenten te gebruiken. Als uw weergaven en status zich binnen een fragment bevinden, kunnen we het fragment gemakkelijk behouden wanneer de activiteit opnieuw wordt gemaakt:
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment when activity is re-initialized
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
Deze aanpak voorkomt dat het fragment wordt vernietigd tijdens de activiteitenlevenscyclus. Ze worden in plaats daarvan bewaard in de Fragment Manager. Raadpleeg de officiële Android-documenten voor meer informatie .
Nu kunt u controleren of het fragment al bestaat per tag voordat u er een maakt en het fragment behoudt zijn status bij configuratiewijzigingen. Raadpleeg de handleiding Veranderingen in runtime-wijzigingen voor meer informatie .
Vergrendelschermstand
Als u de schermoriëntatie van elk scherm (activiteit) van uw Android-applicatie wilt vergrendelen, moet u gewoon de eigenschap android:screenOrientation
van een <activity>
in AndroidManifest.xml :
<activity
android:name="com.techblogon.screenorientationexample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<!-- ... -->
</activity>
Nu moet die activiteit altijd in de portretmodus worden weergegeven.
Configuratiewijzigingen handmatig beheren
Als uw toepassing geen bronnen hoeft bij te werken tijdens een specifieke configuratiewijziging en u een prestatiebeperking heeft die vereist dat u het opnieuw opstarten van de activiteit voorkomt, dan kunt u verklaren dat uw activiteit de configuratiewijziging zelf afhandelt, wat voorkomt dat het systeem uw werkzaamheid.
Deze techniek moet echter worden beschouwd als een laatste redmiddel wanneer u opnieuw opstarten moet voorkomen vanwege een configuratiewijziging en wordt niet aanbevolen voor de meeste toepassingen. Om deze aanpak te volgen, moeten we het knooppunt android:configChanges
aan de activiteit in AndroidManifest.xml :
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
Wanneer een van deze configuraties verandert, wordt de activiteit niet opnieuw onConfigurationChanged()
maar wordt in plaats daarvan een oproep ontvangen naar onConfigurationChanged()
:
// Within the activity which receives these changes
// Checks the current device orientation, and toasts accordingly
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
Zie de Documenten afhandelen . Zie de documentatie van android: configChanges en de klasse Configuration voor meer informatie over welke configuratiewijzigingen u kunt verwerken in uw activiteit.
AsyncTask gebruiken
Probleem:
- Als nadat de
AsyncTask
gestart, er een schermrotatie is, wordt de eigendomsactiviteit vernietigd en opnieuw gemaakt. - Wanneer de
AsyncTask
voltooid, wil deze de gebruikersinterface bijwerken die mogelijk niet meer geldig is.
Oplossing:
Met behulp van laders kan men gemakkelijk de vernietiging / recreatie van activiteiten overwinnen.
Voorbeeld:
Hoofdactiviteit:
public class MainActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<Bitmap> {
//Unique id for the loader
private static final int MY_LOADER = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoaderManager loaderManager = getSupportLoaderManager();
if(loaderManager.getLoader(MY_LOADER) == null) {
loaderManager.initLoader(MY_LOADER, null, this).forceLoad();
}
}
@Override
public Loader<Bitmap> onCreateLoader(int id, Bundle args) {
//Create a new instance of your Loader<Bitmap>
MyLoader loader = new MyLoader(MainActivity.this);
return loader;
}
@Override
public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) {
// do something in the parent activity/service
// i.e. display the downloaded image
Log.d("MyAsyncTask", "Received result: ");
}
@Override
public void onLoaderReset(Loader<Bitmap> loader) {
}
}
AsyncTaskLoader:
public class MyLoader extends AsyncTaskLoader<Bitmap> {
private WeakReference<Activity> motherActivity;
public MyLoader(Activity activity) {
super(activity);
//We don't use this, but if you want you can use it, but remember, WeakReference
motherActivity = new WeakReference<>(activity);
}
@Override
public Bitmap loadInBackground() {
// Do work. I.e download an image from internet to be displayed in gui.
// i.e. return the downloaded gui
return result;
}
}
Notitie:
Het is belangrijk om de v4-compatibiliteitsbibliotheek te gebruiken of niet, maar gebruik geen deel van het ene en een deel van het andere, omdat dit tot compilatiefouten leidt. Om dit te controleren, kunt u kijken naar de invoer voor android.support.v4.content
en android.content
(u zou niet beide moeten hebben).
Vergrendel de rotatie van het scherm programmatisch
Het is heel gebruikelijk dat het tijdens de ontwikkeling erg handig kan zijn om het apparaatscherm tijdens specifieke delen van de code te vergrendelen / ontgrendelen .
Terwijl een dialoogvenster met informatie wordt weergegeven, wil de ontwikkelaar bijvoorbeeld de rotatie van het scherm vergrendelen om te voorkomen dat het dialoogvenster wordt gesloten en de huidige activiteit opnieuw wordt opgebouwd om het opnieuw te ontgrendelen wanneer het dialoogvenster wordt gesloten.
Hoewel we rotatie-vergrendeling van het manifest kunnen bereiken door te doen:
<activity
android:name=".TheActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
</activity>
Je kunt het ook programmatisch doen door het volgende te doen:
public void lockDeviceRotation(boolean value) {
if (value) {
int currentOrientation = getResources().getConfiguration().orientation;
if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
}
}
}
En dan het volgende aanroepen om respectievelijk de apparaatrotatie te vergrendelen en ontgrendelen
lockDeviceRotation(true)
en
lockDeviceRotation(false)