簡體   English   中英

如何通過方向更改保存自定義ListFragment狀態

[英]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) )我不認為目前根本不是一個“長時間運行的操作”。

研究方向

活動-刪除了許多(希望與之無關)的內容:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM