[英]How to save custom ListFragment state with orientation change
我更徹底地希望這個問題實際上會更容易理解。
活動目的 :允許用戶從圖庫中選擇圖像; 在ListFragment中顯示圖像的縮略圖以及標題用戶給出的圖像; 用戶完成操作后,保存每個圖像的uri和標題,而用戶的名字就是該圖像的集合。
問題 :旋轉設備時,FragmentList將丟失用戶已經選擇的所有圖像和標題,即列表的所有行均丟失。
嘗試解決問題 :
實現RetainedFragment以在設備旋轉時保存List集合。 以前我還沒有做過這件事,並且想過:“啊,適配器在創建時被喂入一個空白的List集合。我將保存列表的狀態,然后在調用Activity的onCreate時,我可以將保留的List喂入Adapter構造函數,並且會工作的。” 但事實並非如此。
然后我想:“當然不能用,您還沒有通知適配器更改!” 所以我把adapter.notifyDataSetChanged()
放在onCreate中。 這沒用。
然后,我將adapter.notifyDataSetChanged()
移到onStart
以為我可能需要在活動的生命周期中稍后通知適配器。 沒用
注意:我在同一應用程序中有另一個活動,該活動使用相同的自定義ListViewFragment,並且通過設備方向更改來保留ListFragment的狀態。 該活動有兩個原則上的區別:片段被硬編碼到.xml中(我認為這不會有所作為,除了也許Android原生保存的.xml片段不同於以編程方式添加的片段外); 並且該活動使用Loader和LoaderManager並從我構建的提供程序(從我的SQLite數據庫收集數據)中獲取其數據。 看這兩個活動之間的差異是什么使我想到“您沒有以某種方式適當地處理向適配器提供的數據”,並啟發了我在設備旋轉時使用RetainedFragment保存List集合。
...這促使我思考如何解決,就像Android在其關於LoaderManager的Loader頁面上所說的那樣:
“與活動或片段相關聯的抽象類,用於管理一個或多個Loader實例。這有助於應用程序與活動或片段生命周期一起管理運行時間較長的操作;最常見的用法是與CursorLoader一起使用,但是應用程序自由編寫自己的加載程序以加載其他類型的數據。”
我想到的是“正在加載其他類型的數據”部分,“我可以使用LoaderManager加載列表數據嗎?我對此避諱的兩個原因:1)我已經(至少在概念上)應該可以工作; 2) )我不認為目前根本不是一個“長時間運行的操作”。
研究方向
StackOverflow傻瓜式方法,可在方向改變時處理片段
一勞永逸,如何正確地將Fragments的實例狀態保存在后台堆棧中?
保存后退堆棧片段。
未在下面粘貼的代碼中顯示,但是我的活動動態創建了其他三個片段,如果savedInstanceState !=null
並且在不對onSaveInstanceState()
做任何工作的情況下保存了這些片段的狀態,則使用以下內容(這就是為什么感覺像我的問題不在於在onSaveInstanceState
做某事,因為Android會處理保存我的其他片段狀態,因此它也不應該使用ListFragment來做嗎?似乎應該這樣做)。
if(savedInstanceState.containsKey(AddActivity_Frag1.F1_TAG)){ frag1 = (AddActivity_Frag1)getFragmentManager().getFragment(savedInstanceState, AddActivity_Frag1.F1_TAG); }
我的查詢周圍的許多StackOverflow問題似乎都與如何通過更改方向保存ListFragment的滾動位置有關,但是我不需要這樣做(盡管我確實讀過它們,旨在尋找可能有所幫助的技巧)。
活動-刪除了許多(希望與之無關)的內容:
public class AddActivity extends Activity{
// data collection
List<ImageBean> beanList;
// adapter
AddCollectionAdapter adapter;
// ListViewFragment tag
private static final String LVF_TAG = "list fragment tag";
// fragment handles
ListViewFragment listFrag;
// Handles images; LruCache for bitmapes
ImageHandler imageHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add2);
// Create ImageHandler that holds LruCache
imageHandler = new ImageHandler(this, getFragmentManager());
// Obtain retained List<ImageBean> or create new List<ImageBean>.
RetainedFragment retainFragment = RetainedFragment.findOrCreateRetainFragment(getFragmentManager());
beanList = retainFragment.list;
if(beanList == null){
beanList = new ArrayList<ImageBean>();
retainFragment.list = beanList;
}
// create fragments
if(savedInstanceState == null){
listFrag = new ListViewFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.add_fragFrame, listFrag, LVF_TAG);
ft.commit();
}else{
listFrag = (ListViewFragment)getFragmentManager().findFragmentByTag(LVF_TAG);
}
// create adapter
adapter = new AddCollectionAdapter(this, beanList);
// set list fragment adapter
listFrag.setListAdapter(adapter);
}
@Override
protected void onStart() {
// TESTING: If device orientation has changed List<ImageBean> was saved
// with a RetainedFragment. Seed the adapter with the retained
// List.
adapter.notifyDataSetChanged();
super.onStart();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// Android automatically saves visible fragments here. (?)
super.onSaveInstanceState(outState);
}
/*
* ImageBean.
*/
public static class ImageBean{
private String collectionName; // Title of image collection
private String imageUri; // Image URI as a string
private String imageTitle; // Title given to image
public ImageBean(String name, String uri, String title){
collectionName = name;
imageUri = uri;
imageTitle = title;
}
public String getCollectionName() {
return collectionName;
}
public String getImageUri() {
return imageUri;
}
public String getImageTitle() {
return imageTitle;
}
}
/*
* Called when user is finished selecting images.
*
* Performs a bulk insert to the Provider.
*/
private void saveToDatabase() {
int arraySize = beanList.size();
final ContentValues[] valuesArray = new ContentValues[arraySize];
ContentValues values;
String imageuri;
String title;
int counter = 0;
for(ImageBean image : beanList){
imageuri = image.getImageUri();
title = image.getImageTitle();
values = new ContentValues();
values.put(CollectionsTable.COL_NAME, nameOfCollection);
values.put(CollectionsTable.COL_IMAGEURI, imageuri);
values.put(CollectionsTable.COL_TITLE, title);
values.put(CollectionsTable.COL_SEQ, counter +1);
valuesArray[counter] = values;
counter++;
}
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg0) {
getContentResolver().bulkInsert(CollectionsContentProvider.COLLECTIONS_URI, valuesArray);
return null;
}
@Override
protected void onPostExecute(Void result) {
// End this activity.
finish();
}
};
task.execute();
}
public ImageHandler getImageHandler(){
return imageHandler;
}
}
class RetainedFragment extends Fragment{
private static final String TAG = "RetainedFragment";
// data to retain
public List<AddActivity.ImageBean> list;
public static RetainedFragment findOrCreateRetainFragment(FragmentManager fm){
RetainedFragment fragment = (RetainedFragment)fm.findFragmentByTag(TAG);
if(fragment == null){
fragment = new RetainedFragment();
fm.beginTransaction().add(fragment, TAG);
}
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}
ListFragment:
public class ListViewFragment extends ListFragment {
ListFragListener listener;
public interface ListFragListener{
public void listFragListener(Cursor cursor);
}
@Override
public void onCreate(Bundle savedInstanceState) {
// Retain this fragment across configuration change
setRetainInstance(true);
super.onCreate(savedInstanceState);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Set listener
if(activity instanceof ListFragListener){
listener = (ListFragListener)activity;
}else{
//Instantiating activity does not implement ListFragListener.
}
}
@Override
public void onListItemClick(ListView listView, View v, int position, long id) {
// no action necessary
}
}
適配器:
public class AddCollectionAdapter extends BaseAdapter {
// data collection
List<ImageBean> beanList;
// layout inflator
private LayoutInflater inflater;
// context
Context context;
public AddCollectionAdapter(Context context, List<ImageBean> beanList){
this.context = context;
this.beanList = beanList;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return beanList.size();
}
@Override
public Object getItem(int position) {
return beanList.get(position);
}
@Override
public long getItemId(int arg0) {
// collection not from database nor is going directly to database; this is useless.
return 0;
}
// holder pattern
private class ViewHolder{
ImageView imageView;
TextView titleView;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View xmlTemplate = convertView;
if(xmlTemplate == null){
//inflate xml
xmlTemplate = inflater.inflate(R.layout.frag_listview_row, null);
// initilaize ViewHolder
holder = new ViewHolder();
// get views that are inside the xml
holder.imageView = (ImageView)xmlTemplate.findViewById(R.id.add_lvrow_image);
holder.titleView = (TextView)xmlTemplate.findViewById(R.id.add_lvrow_title);
// set tag
xmlTemplate.setTag(holder);
}else{
holder = (ViewHolder)xmlTemplate.getTag();
}
// Get image details from List<ImageBean>
ImageBean bean = beanList.get(position);
String imageUri = bean.getImageUri();
String title = bean.getImageTitle();
// Set Holder ImageView bitmap; Use parent activity's ImageHandler to load image into Holder's ImageView.
((AddActivity)context).getImageHandler().loadBitmap(imageUri, holder.imageView, Constants.LISTVIEW_XML_WIDTH, Constants.LISTVIEW_XML_HEIGHT);
// Set Holder's TextView.
holder.titleView.setText(title);
// return view
return xmlTemplate;
}
}
解決了。 將日志語句放在重要位置后,我發現RetainedFragment的列表始終為空。 撓頭后在RetainedFragment中注意到了這一點:
fm.beginTransaction().add(fragment, TAG);
我錯過了commit()
!
在我添加完狀態之后,現在通過配置更改來保留狀態。
有關保存在試驗和災難中發現的ListFragment狀態的更多信息:
如果您通過以下方式添加片段:
if(savedInstanceState == null){
listFrag = new ListViewFragment();
// programmatically add fragment to ViewGroup
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.add_fragFrame, listFrag, LVF_TAG);
}
然后這些都可以在else
工作:
1) This one works because Android takes care of saving the Fragment:
listFrag = (ListViewFragment)getFragmentManager().findFragmentByTag(LVF_TAG);
2) This one works because the fragment was specifically saved into bundle in
onSaveInstanceState:
listFrag = (ListViewFragment)getFragmentManager().getFragment(savedInstanceState, LVF_TAG);
為了使編號2起作用,這發生在onSaveInstanceState()中:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getFragmentManager().putFragment(outState, LVF_TAG, listFrag);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.