Android
Orienteringsändringar
Sök…
Anmärkningar
Spara och återställa aktivitetstillstånd
När din aktivitet börjar stoppa, anropar onSaveInstanceState()
så att din aktivitet kan spara tillståndsinformation med en samling nyckelvärdespar. Standardimplementeringen av den här metoden sparar automatiskt information om tillståndet i aktivitetens visningshierarki, till exempel texten i en EditText
widget eller rullningspositionen för en ListView
.
För att spara ytterligare tillståndsinformation för din aktivitet måste du implementera onSaveInstanceState()
och lägga till nyckelvärdenspar till Bundle-objektet. Till exempel:
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);
}
}
Systemet kommer att kalla den metoden innan en aktivitet förstörs. Senare kommer systemet att ringa på onRestoreInstanceState
där vi kan återställa tillstånd från paketet:
@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);
}
Instansläget kan också återställas i standardaktivitet # onCreate-metoden, men det är bekvämt att göra det i onRestoreInstanceState
som säkerställer att alla initialiseringar har gjorts och gör att underklasser kan välja om standardimplementeringen ska användas. Läs detta stackoverflow-inlägg för mer information.
Observera att onSaveInstanceState
och onRestoreInstanceState
inte garanteras att de ska kallas samman. Android åberopar onSaveInstanceState()
när det finns en chans att aktiviteten kan förstöras. Men det finns fall där onSaveInstanceState
kallas men aktiviteten förstörs inte och som ett resultat onRestoreInstanceState
inte åberopas.
Spara och återställa fragmentstillstånd
Fragment har också en onSaveInstanceState()
-metod som kallas när deras tillstånd behöver sparas:
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);
}
}
Då kan vi dra ut data från det sparade tillståndet i 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;
}
}
För att fragmentstillståndet ska sparas ordentligt måste vi vara säkra på att vi inte onödigt återskapar fragmentet vid konfigurationsändringar. Detta innebär att man är försiktig så att man inte initialiserar befintliga fragment när de redan finns. Alla fragment som initialiseras i en aktivitet måste letas upp efter tagg efter en konfigurationsändring:
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();
}
}
}
Detta kräver att vi är noga med att inkludera en tagg för uppslagning när vi lägger ett fragment i aktiviteten inom en transaktion:
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();
}
}
}
Med detta enkla mönster kan vi korrekt använda fragment och återställa deras tillstånd genom konfigurationsändringar.
Bevara fragment
I många fall kan vi undvika problem när en aktivitet skapas igen genom att helt enkelt använda fragment. Om dina åsikter och tillstånd ligger inom ett fragment kan vi enkelt låta fragmentet behållas när aktiviteten skapas om:
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;
}
}
Denna strategi hindrar fragmentet från att förstöras under aktivitetslivscykeln. De hålls istället kvar i Fragment Manager. Se de officiella Android-dokumenten för mer information .
Nu kan du kontrollera om fragmentet redan finns med taggen innan du skapar ett och fragmentet behåller dess tillstånd över konfigurationsändringar. Se Hantering Runtime Förändringar guide för mer information .
Låsning av skärmorientering
Om du vill låsa skärmorienteringsändringen för någon skärm (aktivitet) i din Android-applikation behöver du bara ställa in android:screenOrientation
egenskapen för en <activity>
i AndroidManifest.xml :
<activity
android:name="com.techblogon.screenorientationexample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<!-- ... -->
</activity>
Nu tvingas den aktiviteten att alltid visas i " stående " -läge.
Hantera konfigurationsändringar manuellt
Om din applikation inte behöver uppdatera resurser under en specifik konfigurationsändring och du har en prestandabegränsning som kräver att du undviker aktivitetsstart, kan du förklara att din aktivitet hanterar själva konfigurationsändringen, vilket hindrar systemet från att starta om din aktivitet.
Denna teknik bör dock betraktas som en sista utväg när du måste undvika omstart på grund av en konfigurationsändring och inte rekommenderas för de flesta applikationer. För att ta detta tillvägagångssätt måste vi lägga till android:configChanges
noden till aktiviteten i AndroidManifest.xml :
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
När en av dessa konfigurationer ändras startas inte aktiviteten utan får istället ett samtal till 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();
}
}
Se Hantera ändringsdokumenten . Mer information om vilka konfigurationsändringar du kan hantera i din aktivitet, se android: configChanges- dokumentationen och klassen Configuration .
Hantering av AsyncTask
Problem:
- Om efter att
AsyncTask
startar finns enAsyncTask
förstörs och återskapas ägaraktiviteten. - När
AsyncTask
vill den uppdatera användargränssnittet som kanske inte gäller längre.
Lösning:
Med hjälp av lastare kan man enkelt övervinna aktivitetens förstörelse / rekreation.
Exempel:
Huvudaktivitet:
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;
}
}
Notera:
Det är viktigt att använda antingen v4-kompatibilitetsbiblioteket eller inte, men inte använda en del av det ena och det andra, eftersom det kommer att leda till kompilationsfel. För att kontrollera kan du titta på importen för android.support.v4.content
och android.content
(du borde inte ha båda).
Lås skärmens rotation programmatiskt
Det är mycket vanligt att man under utvecklingen kan hitta mycket användbart för att låsa / låsa upp skärmen under specifika delar av koden .
Till exempel, medan en dialog med information visar utvecklaren kanske vill låsa skärmens rotation för att förhindra att dialogrutan tas bort och den aktuella aktiviteten från att byggas om för att låsa upp den igen när dialogen avslutas.
Även om vi kan uppnå rotationslåsning från manifestet genom att göra:
<activity
android:name=".TheActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
</activity>
Man kan göra det programmatiskt också genom att göra följande:
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);
}
}
}
Och sedan ringa följande för att låsa respektive låsa upp enhetens rotation
lockDeviceRotation(true)
och
lockDeviceRotation(false)