2014-09-08 2 views
1

Я более подробно с надеждой, что вопрос будет на самом деле легче понять.Как сохранить пользовательское состояние ListFragment с изменением ориентации

Цель деятельности: позволяет пользователям выбирать изображения из галереи; отобразить эскиз изображения в ListFragment вместе с именем пользователя, дал изображение; когда пользователь закончил сохранять uri и заголовок каждого изображения, а имя пользователя дало эту коллекцию изображений.

Задача: Когда устройство повернуто, FragmentList теряет все изображения и названия, которые пользователь уже выбрал, то есть все строки списка отсутствуют.

Покушение решения проблем:

  • Реализована RetainedFragment, чтобы сохранить коллекцию List при повороте устройства. Раньше я этого не делал и вычислял «А, адаптер получает пустую коллекцию списков при создании. Я сохраню состояние списка, а затем, когда вызывается функция onCreate, я могу скопировать сохраненный список в конструктор адаптера, и он «Я буду работать». Но это не так.

  • Тогда я подумал: «Конечно, он не работает, вы не уведомили об этом переходник!» Поэтому я положил adapter.notifyDataSetChanged() в onCreate. Это не сработало.

  • Затем я переместил adapter.notifyDataSetChanged() в onStart, думая, что мне может потребоваться уведомить адаптер позже о жизненном цикле деятельности. Не работает.

Примечания: У меня есть другая активность в этом же приложении, которые используют это же обычай ListViewFragment и состояние ListFragment в настоящее время сохраняются при изменении ориентации устройства. Эта деятельность имеет два принципиальных отличия: фрагмент жестко закодирован в .xml (я не думаю, что это изменило бы ситуацию, кроме, может быть, нативная сохранность XML-фрагментов для Android отличается от программно добавленных); и эта активность использует Loader и LoaderManager и получает свои данные от Провайдера, который я создал (который собирает данные из моей базы данных SQLite). Если посмотреть на различия между этими двумя действиями, это заставило меня думать, что «вы не обрабатываете данные, которые правильно адаптируют адаптер», и вдохновил меня на использование RetainedFragment для сохранения коллекции List при повороте устройства.

... который побуждает меня думать о том, выяснить, как, как и Android говорит на их Loader странице о LoaderManager:

«Абстрактный класс, связанный с деятельностью или фрагментом для управления одним или более Экземпляры загрузчика. Это помогает приложению управлять более длительными операциями в сочетании с жизненным циклом активности или фрагмента, наиболее часто используемым с помощью CursorLoader, однако приложения могут свободно писать свои собственные загрузчики для загрузки других типов данных ».

Это «загрузка других типов данных» часть, которая имеет меня думать «Могу ли я использовать LoaderManager, чтобы загрузить данные списка по двум причинам я уклоняюсь от этого: 1) то, что я уже, по крайней мере, концептуально , должен работать, 2) то, что я делаю в настоящее время, на самом деле не является «более продолжительной операцией», я не думаю.

Исследование:

  • StackOverflow Fool proof way to handle Fragment on orientation change

    • сохранить состояние фрагмента.
    • Я думаю, что RetainedFragment я использую сохраняет то, что должно быть сохранено. (?)
  • Once for all, how to correctly save instance state of Fragments in back stack?

    • Сохранить фрагменты backstack.

    • Не показано в моем коде, наклеенный ниже, но моя деятельность динамически создает три другие фрагменты, и я использую следующий, если savedInstanceState !=null и состояние этих фрагментов сохраняется без выполнения каких-либо работ в onSaveInstanceState() (это отчасти, почему он чувствует, как мои проблема заключается не в том, чтобы что-то делать в onSaveInstanceState, потому что Android управляет сохранением моего состояния других фрагментов, так что не стоит ли это делать с ListFragment? Кажется, это должно быть).

      if(savedInstanceState.containsKey(AddActivity_Frag1.F1_TAG)){ 
          frag1 = (AddActivity_Frag1)getFragmentManager().getFragment(savedInstanceState, AddActivity_Frag1.F1_TAG); 
      } 
      
  • Understanding Fragment's setRetainInstance(boolean)

  • Многие StackOverflow вопросы, касающиеся моего запроса, кажется, в основном о том, как сохранить положение прокрутки ListFragment с изменением ориентации, но мне не нужно делать что (хотя я прочитал их, ища советы, которые могут помочь).

  • Android Fragments

  • Android Loaders
  • Android Caching Bitmaps (RetainFragment stuff)

активность - со многими, надеюсь, не связанных между собой вещей, Удалено:

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; 
} 
} 

ответ

1

решаемая. После ввода записей журнала в стратегических местах я обнаружил, что список 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); 
}