gpt4 book ai didi

android - 如何通过方向更改保存自定义 ListFragment 状态

转载 作者:行者123 更新时间:2023-11-30 02:41:15 25 4
gpt4 key购买 nike

我正在更彻底地希望这个问题实际上会更容易理解。

Activity 目的 :允许用户从图库中选择图像;在 ListFragment 中显示图像的缩略图以及用户提供的图像;当用户完成保存每个图像的 uri 和标题,以及用户给这个图像集合的名称。

问题 :当设备旋转时,FragmentList 会丢失用户已经选择的所有图像和标题,即列表的所有行都丢失了。

尝试解决问题 :

  • 实现了 RetainedFragment 以在设备旋转时保存 List 集合。以前我没有这样做并想“啊,适配器在创建时被提供一个空白列表集合。我将保存列表的状态,然后当调用 Activity 的 onCreate 时,我可以将保留的列表提供给适配器构造函数,它会工作的。”但它没有。
  • 然后我想,“当然不行,你还没有通知适配器有变化!”所以我把 adapter.notifyDataSetChanged()在 onCreate.这没有用。
  • 然后我移动了adapter.notifyDataSetChanged()onStart认为我可能需要在 Activity 生命周期的后期通知适配器。没用。

  • 注意:我在同一个应用程序中有另一个 Activity ,它使用相同的自定义 ListViewFragment,并且 ListFragment 的状态随着设备方向的变化而保留。该 Activity 有两个主要区别: fragment 被硬编码到 .xml 中(我认为这不会产生影响,除了可能 Android 的 .xml fragment 的 native 保存与以编程方式添加的 fragment 不同);并且该 Activity 使用 Loader 和 LoaderManager 并从我构建的 Provider 获取其数据(它从我的 SQLite 数据库收集数据)。看看这两个 Activity 之间的差异是什么让我想到“你没有以某种方式适本地处理数据馈送适配器”并启发我在设备旋转时使用 RetainedFragment 来保存 List 集合。

    ...这促使我考虑弄清楚如何去做,正如 Android 在他们的 Loader 页面上关于 LoaderManager 所说的那样:

    "An abstract class associated with an Activity or Fragment for managing one or more Loader instances. This helps an application manage longer-running operations in conjunction with the Activity or Fragment lifecycle; the most common use of this is with a CursorLoader, however applications are free to write their own loaders for loading other types of data."



    正是“加载其他类型的数据”部分让我思考“我可以使用 LoaderManager 来加载列表数据吗?我对此感到害羞的两个原因:1)我已经拥有的,至少在概念上应该可以工作;2 ) 我现在所做的根本不是“长期运行的操作”,我不认为。

    研究 :
  • StackOverflow Fool proof way to handle Fragment on orientation change
  • 保存 fragment 的状态。
  • 我认为我使用的 RetainedFragment 保存了需要保存的内容。(?)
  • Once for all, how to correctly save instance state of Fragments in back stack?
  • 保存 backstack fragment 。
  • 未在下面粘贴的代码中显示,但我的 Activity 动态创建了三个其他 fragment ,如果 savedInstanceState !=null,我使用以下内容并且这些 fragment 的状态无需在 onSaveInstanceState() 中做任何工作即可保存(这就是为什么感觉我的问题不在于在 onSaveInstanceState 中做某事的部分原因,因为 Android 处理保存我的其他 fragment 状态,所以它不应该也使用 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)

  • Activity - 删除了许多希望不相关的东西:
    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);
    }
    }

    列表 fragment :
    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 状态相关的更多信息:

    如果您通过以下方式添加 fragment :
        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);
    }

    关于android - 如何通过方向更改保存自定义 ListFragment 状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25730163/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com