安卓力量当屏幕更改方向时重新绘制片段
我的应用程序由导航抽屉和4个片段构成。 这个应用程序的旧版本使用活动,所以我需要改变片段中的活动。安卓力量当屏幕更改方向时重新绘制片段
目前一切运作良好,但在一个Fragment
我有80 Buttons
用户可以设置文字和背景颜色和方法调用DialogActivity
是在管理所有Fragments
和onActivityResults
通过对话框称为内Fragments
用于管理的唯一一个MainActivity
用户更改。
当屏幕方向更改为横向时,会发生此问题。 如果我按Buttons
并设置文字和颜色所有作品与肖像但如果改变方向的屏幕与风景我得到的东西像“背景”的像“阴影”和我没有改变它们的属性,当我点击他们,但如果我再次旋转屏幕变化的按钮变得可见。 奇怪的是,在片段背景中,我看到的按钮与正确的更新,但不是在顶部...(我发布的照片,这很难解释)
我改变的旧按钮仍然改变,因为我把它保存到数据库,但与景观我不能更新澈其他按钮...
CODE:
MainActivity.java
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
static String clickedButtonViewId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
if (findViewById(R.id.content_frame) != null){
getSupportFragmentManager().beginTransaction()
.add(R.id.content_frame, new OrarioFragment()).commit();
}
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
FragmentManager fragmentManager = getSupportFragmentManager();
if (id == R.id.nav_orario) {
fragmentManager.beginTransaction()
.replace(R.id.content_frame, new OrarioFragment())
.commit();
} else if (id == R.id.nav_calendario) {
fragmentManager.beginTransaction()
.replace(R.id.content_frame, new CalendarioFragment())
.commit();
} else if (id == R.id.nav_voti) {
fragmentManager.beginTransaction()
.replace(R.id.content_frame, new VotiFragment())
.commit();
} else if (id == R.id.nav_registrazioni) {
fragmentManager.beginTransaction()
.replace(R.id.content_frame, new RegistrazioniFragment())
.commit();
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
public void addMateria(View v){
/* Prendo il nome della risorsa cosi nel ricompilare il progetto non perdo *
* tutti i riferimenti ai bottoni salvati nel database */
clickedButtonViewId = getResources().getResourceEntryName(v.getId());
//StartActivityForResult perche mi aspetto la materia inserita dall'altra activity
Intent myIntent = new Intent(MainActivity.this, ActivityAddMateria.class);
startActivityForResult(myIntent, 1);
//onStop();
}
//Take back data from ActivityAddMateria
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1) {
if (resultCode == RESULT_OK) {
MySQLiteHelper db = new MySQLiteHelper(this);
//Cambio subito il Button
int resId = getResources().getIdentifier(clickedButtonViewId, "id", getPackageName());
final Button clickedtextView = (Button) findViewById(resId);
String result = data.getStringExtra("result"); //Take the materia from Dialog
int color = data.getIntExtra("color", 1); //Take the color from Dialog
//Controllo se il Button è già presente nel db se presente aggiorno se non presente inserisco
boolean modifica = db.Exists(clickedButtonViewId);
//Se voglio ripristinare il bottone di default
if (color == getResources().getColor(R.color.blue_orario)) {
//Ripristino la grafica di Default
Drawable style = setButtonColor(color);
clickedtextView.setBackground(style);
clickedtextView.setText("New");
//Se la materia è nel database la cancello
if (modifica) {
db.deleteSingleMateria(clickedButtonViewId);
}
} else {
//Quando inserisco un normale bottone colorato
if (!modifica) {
//Materia da inserire in un nuovo spazio
db.addMateriaToDb(new Materia(clickedButtonViewId, result, color));
} else {
//Materia già presente nel Button quindi aggiorno la materia
db.updateMateria(new Materia(clickedButtonViewId, result, color));
Toast.makeText(getApplicationContext(), "Materia modificata!",
Toast.LENGTH_LONG).show();
}
//Inserisco la materia nel DB dei voti_media
db.addMateriaVotiFromOrario(new MaterieVoti(result, 0.0));
clickedtextView.setText(result);
//clickedtextView.setBackgroundColor(color);
//clickedtextView.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
Drawable style = setButtonColor(color);
clickedtextView.setBackground(style);
}
}
if (resultCode == RESULT_CANCELED) {
//Nessuna materia inserita
}
}
}//onActivityResult
编辑
好吧,我发现这个问题。
在MainActivity我有力这行代码的第一片段中显示
if (findViewById(R.id.content_frame) != null){
getSupportFragmentManager().beginTransaction()
.add(R.id.content_frame, new OrarioFragment()).commit();
}
而当屏幕方向改变在MainActivity被重建并且如果加载相同的片段通过旧片段,因为我使用.add()
那么我应该如何设置片段显示,当应用程序开始避免这个问题?
我管理抽屉有问题吗?
Android文档解释了关于保存状态和关于在活动和片段中保存状态的非常好的文章。
-
Android Documentation:
https://developer.android.com/guide/components/activities/activity-lifecycle.html -
Additional Article:
https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
保存和恢复活动状态有些情况下,你的活动是由于正常的应用程序的行为摧毁了几个方案,比如当用户按下“后退”按钮,或者您的活动通过调用finish()方法来标记自己的销毁。如果活动处于停止状态并且很长一段时间未使用活动,或者前台活动需要更多资源,系统还可能销毁包含活动的进程以恢复内存。
当您的活动因用户按下“后退”或活动自行完成而被销毁时,该活动实例的系统概念将永远消失,因为该行为表示活动不再需要。但是,如果系统由于系统限制(而不是正常的应用程序行为)而破坏了活动,那么虽然实际的活动实例已经消失,但系统会记住它已存在,因此如果用户导航回它,系统会创建一个新的使用一组保存的数据描述活动在销毁时的状态。系统用于恢复先前状态的已保存数据称为实例状态,是存储在Bundle对象中的键值对的集合。
默认情况下,系统使用Bundle实例状态来保存有关活动布局中每个View对象的信息(例如输入到EditText小部件中的文本值)。 因此,如果您的活动实例被销毁并重新创建,那么布局的状态将恢复到之前的状态,而您不需要任何代码。但是,您的活动可能包含更多想要恢复的状态信息,例如跟踪用户活动进度的成员变量。
保存您的活动状态 当你的活动开始,停止,系统调用的onSaveInstanceState()方法,因此您的活动可以保存状态信息与键值对的集合。此方法的默认实现可以保存有关活动视图层次结构状态的瞬态信息,例如EditText小部件中的文本或ListView小部件的滚动位置。你的应用应该在onPause()方法之后和onStop()之前实现onSaveInstanceState()回调。不要在onPause()中实现此回调。
警告:您必须始终调用onSaveInstanceState()的超类实现,以便默认实现可以保存视图层次结构的状态。
要保存活动的其他状态信息,必须重写onSaveInstanceState()并将键值对添加到在您的活动意外销毁的事件中保存的Bundle对象。例如:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
注意:为了让Android系统以恢复您的活动的意见的状态,每个视图必须有一个唯一的ID,由机器人提供:id属性。
要保存持久数据(例如用户首选项或数据库数据),当您的活动处于前台时,应该采取适当的机会。如果没有这样的机会出现,您应该在onStop()方法中保存这些数据。
恢复您的活动状态 当您的活动被重建后,先前被破坏,可以从捆绑恢复保存的状态,该系统传递到您的活动。 onCreate()和onRestoreInstanceState()回调方法都会收到包含实例状态信息的相同Bundle。
因为无论系统是在创建活动的新实例还是重新创建前一个实例,都会调用onCreate()方法,您必须在尝试读取之前检查状态Bundle是否为null。如果它为空,那么系统正在创建一个活动的新实例,而不是恢复之前被销毁的实例。
例如,下面的代码片段展示了如何在的onCreate()恢复了一些状态数据:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
相反的onCreate()中恢复状态,你可以选择实施onRestoreInstanceState (),系统在onStart()方法之后调用。该系统调用onRestoreInstanceState()只如果有恢复已保存的状态,这样你就不会需要检查包是否为空:
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
注意:始终调用父类实现onRestoreInstanceState的( ),所以默认实现可以恢复视图层次的状态。
是的,但为什么我在屏幕方向更改时有像照片那样的“阴影”,以及为什么我无法看到按钮随屏幕风景而改变。 如果我回到肖像所有工作完美,这是问题的景观,并不认为问题是InstanceState ....不是? 随着instanceState我无法避免风景的“阴影”效应 – Dario
你有两个方向相同的布局,或者你有布局和layout-land文件夹的方向? – JRG
相同的布局,我没有两个不同的屏幕方向布局。 当屏幕方向改变时,我可能需要销毁碎片。 也许“阴影”效应它是两个片段UI的重叠? 因为背景中的阴影,如果我改变一个按钮与风景我看到在阴影背景更新,但不是在顶部(这很难解释:D) – Dario
显示您的代码。 – Bryan
@Bryan添加了代码,如果需要更多只是告诉:) – Dario
你也可以分享你的布局吗? – JRG