我有一个显示联系人列表的FragmentActivity
。
这是我的onCreate
方法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_human_list);
if (findViewById(R.id.human_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
((HumanListFragment) getSupportFragmentManager()
.findFragmentById(R.id.human_list))
.setActivateOnItemClick(true);
}
if (savedInstanceState == null || !savedInstanceState.getBoolean("displayed_contacts"))
displayContacts();
}
我的onSaveInstanceState
:
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean("displayed_contacts", true);
}
我不确定这是否相关,但这是我的displayContacts
,以防万一:
private void displayContacts() {
// Init variables
String[] SelectColumns = new String[] { Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY, Contacts.PHOTO_URI };
String rawContactID, displayName, phoneNumber;
InputStream thumbnailPhoto;
Cursor c, infoC;
// Outer cursor (fetches all contact IDs)
c = getContentResolver().query(
Contacts.CONTENT_URI,
SelectColumns,
Contacts.HAS_PHONE_NUMBER + " = 1 ",
null,
Contacts.DISPLAY_NAME_PRIMARY);
Log.v(getPackageName(), "Found " + (c != null ? c.getCount() : "0") + " contacts");
try {
if (c.moveToFirst()) {
do {
// Columns
rawContactID = c.getString(c.getColumnIndex(SelectColumns[0]));
displayName = c.getString(c.getColumnIndex(SelectColumns[1]));
String[] selectPhone = {CommonDataKinds.Phone.NUMBER};
thumbnailPhoto = openThumbnail(Long.valueOf(rawContactID));
infoC = getContentResolver().query(
CommonDataKinds.Phone.CONTENT_URI,
selectPhone,
CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[] {rawContactID},
null
);
infoC.moveToFirst();
phoneNumber = infoC.getString(0);
// Adds items to ListView
HumanContent.addItem(new HumanContent.HumanItem(rawContactID, displayName, phoneNumber != "n/a" ? phoneNumber : "", thumbnailPhoto));
Log.v(getPackageName(), "Cursor position: " + c.getPosition() + ", contact ID: " + rawContactID);
infoC.close();
} while (c.moveToNext());
c.close();
}
displayed_contacts = true;
} catch (Exception e) {
Log.e(getPackageName(), e.getMessage());
}
}
现在就是这样:
当我使用后退键退出应用程序时,然后通过图标再次打开它;即使将列表保存在内存中,列表也会重新创建:所以我在同一视图中获得了双重联系人列表。
在这种情况下, savedInstanceState
为空,因此达到了if条件,但实际上该视图已经包含了我之前的联系人列表。是什么赋予了?如何避免重新创建列表?我已经尝试过使用实例变量,但无济于事。
我还想避免100%重复列表 - 如果我可以重用现有的视图,真棒。
答案 0 :(得分:8)
首先,您的savedInstanceState为空的原因 - 系统仅保存由于系统限制而被销毁的活动的状态。如果退出活动,它将被永久销毁,并且不会保存任何状态。
相关文档: http://developer.android.com/training/basics/activity-lifecycle/recreating.html
当您的活动被销毁时,因为用户按下后退或 活动完成了自己,系统的活动概念 实例永远消失,因为行为表明了活动 不再需要了。但是,如果系统破坏了应付的活动 系统约束(而不是正常的应用程序行为),然而 实际的Activity实例消失了,系统会记住它 存在,如果用户导航回它,系统会创建 使用一组已保存数据的活动的新实例 描述了活动被销毁时的状态。
因此,您的特定问题似乎是,当您的Activity消失时,您的静态HumanContact类仍然在内存中,而您的新Activity将使用另一个联系人副本加载它。
有几种方法可以解决这个问题。首先,您可以在HumanContent上实现一个方法来清除其所有项目,并在您启动Activity的新实例时调用它。这样可以确保您的数据是最新的,但这意味着您必须重新加载您的联系人。
其次,如果你想真正避免重新加载数据,我建议为独立于Activity的联系人创建某种缓存。您应该将Activity视为相当短暂的,它可以并且将被频繁地销毁和重新创建,而缓存可以持久存在。
HumanContent似乎已经在某种程度上填补了这一责任。您正在使用它来存储您的数据,并且它会持续超出您的Activity的生命周期。您还可以向其添加一个方法,检查它是否已加载联系人,如果没有加载它们自己。这样它就可以完全控制加载和缓存数据,而活动只能负责显示这些数据。请注意这种类型的解决方案,即您没有在内存中存储太多数据,只要您希望数据发生更改,就会重新加载缓存,并注意在某些情况下系统可能会重新启动您的进程,因此您的缓存必须准备好重新加载您的数据,以防它被销毁。
至于保留您的视图,如果用户退出您的Activity,则调用finish(),并且您的Activity将被销毁。请记住,在这种情况下,系统不再具有Activity的任何概念,因此无法保留这些视图以供重用。
答案 1 :(得分:3)
简单。在您的活动
中按照这些步骤操作然后肯定你不会得到内容复制。
答案 2 :(得分:1)
始终建议使用缓存来保留数据。正如Groucho所提到的,你可以使用存储选项 请查看以下链接了解详情。
http://developer.android.com/guide/topics/data/data-storage.html
答案 3 :(得分:1)
我曾遇到过类似的问题。我不确定这是多么合法,但我使用的代码如下所示,它修复了很多:
private static ViewGroup view1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mCurrentActivity = getActivity();
if (view1 == null) {
settleFragment(inflater);
} else {
if (view1.getParent() != null && view1.getParent() instanceof ViewGroup) {
((ViewGroup) view1.getParent()).removeView(view1);
}
}
return view1;
}
您可以使用settleFragment(inflater);
方法进行调整。
神奇的是关于名为view1
的静态变量。片段初始化后,它将填满数据。 Fragments的其他实例只会将view1
从其前父级中删除,并将其粘贴到自己身上。
文件存储缓存速度非常快,因为对象保存在ram中。如果OS垃圾收集对象,则静态变量将再次为null,并且在重新创建视图时不会发生重复。
答案 4 :(得分:1)
你无法阻止执行onCreate
功能。但是,您可以应用解决方法以避免重复列表,您可以从列表视图中删除所有项目,然后让它重新填充列表视图。
答案 5 :(得分:1)
@Override
public void onPause ()
{
super.onPause();
//...........
}
@Override
public void onStop ()
{
super.onPause();
//............
}
检查您是否可以在片段中使用这些方法?
答案 6 :(得分:1)
实现这一目标的最简单方法是执行Nullcheck 即
if(yourcomponent==null) {
initializecomponent();
}
这可能会阻止您的视图重新创建。
答案 7 :(得分:0)
完成在displayContacts()中加载数据后,可以在共享首选项中保存标记(displayed_contacts)。在onCreate()中检查首选项值,并相应地调用displayContacts()。
答案 8 :(得分:0)
我在使用Fragments时遇到了同样的问题。
我们有如上所述的现有方法。如果您发现自己处于困境,那么您可以尝试如下所述: -
答案 9 :(得分:0)
您将检查控制器。
而不是尝试检查您存储的值。 首先将一个变量作为Bundle。 将其保存在onCreate方法中。
Bundle bdlSaveInstant; // Declare at top before onCreate() method.
/* Initialise in onCreate() method. After
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_human_list);
bdlSaveInstant = savedInstanceState;
*/
@override
onBackPressed()
{
bdlSaveInstant.putBoolean("displayed_contacts", true);
}
@override
onDestroy()
{
bdlSaveInstant.putBoolean("displayed_contacts", true);
}
尝试此操作。
答案 10 :(得分:0)
在你的on create方法中只需清除你的列表。因为每次它都会转到创建方法并清除列表(哪个)是你以前的数据加载。
OR
检查你的列表是否(list.size == null)然后在else块(list.setAdapter)中调用你的API。
希望它能奏效。