【问题标题】:how to save listview items/adapter in fragment and restore from back stack如何在片段中保存列表视图项目/适配器并从后堆栈恢复
【发布时间】:2015-07-23 11:19:05
【问题描述】:

我已尝试搜索此问题,但找不到任何解决方案。 我在片段中保存 ListView 项目时遇到问题。情况是我有几个片段

假设 FRAGMENT A、FRAGMENT B 和 FRAGMENT C,并且 FRAGMENT A 有一个 listview,listview的item可以在用户滚动的时候改变 (就像社交媒体应用程序中的时间线一样)。当我改变片段 A、我使用了 addToBackStack(null) 方法。

问题是,如果按下返回按钮,如何保存列表视图并恢复它?

onSaveInstanceState() 永远不会被调用,因为 Activity 主机永远不会被杀死,只使用这个来更改片段:

public void changeFragment(Fragment fragment, ColorDrawable colorDrawable){
    Fragment newFrag = fragment;
    ColorDrawable newColor = colorDrawable;

    String backStateName = fragment.getClass().getName();
    FragmentManager manager = getSupportFragmentManager();
    boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
    if (!fragmentPopped){ //fragment not in back stack, create it.
        FragmentTransaction ft = manager.beginTransaction();
        ft.replace(R.id.content_fragment, fragment);
        ft.addToBackStack(backStateName);
        ft.commit();
    }

    //to change the actionbar color
    getSupportActionBar().setBackgroundDrawable(newColor);
}

这是我尝试保存和恢复列表视图的代码:

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //try to save listview scroll
        index = myList.getFirstVisiblePosition();
        View v = myList.getChildAt(0);
        top = (v == null) ? 0 : (v.getTop() - myList.getPaddingTop());
        index = 1;
        //try to save listview items
        savedAdapter = adapter;
        fromBackStack = true;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        myList.setSelectionFromTop(index, top);
        myList.setAdapter(savedAdapter);
    }

这里是我到目前为止所做的代码:

    package com.ngapainya.ngapainya.fragment;


import android.app.Activity;
import android.graphics.drawable.ColorDrawable;
import android.net.ParseException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;

import com.ngapainya.ngapainya.R;
import com.ngapainya.ngapainya.activity.volunteer.ContainerActivity;
import com.ngapainya.ngapainya.adapter.HomeAdapter;
import com.ngapainya.ngapainya.fragment.child.DetailPostFragment;
import com.ngapainya.ngapainya.model.Home;

import java.util.ArrayList;

/**
 * A simple {@link Fragment} subclass.
 */
public class HomeFragment extends Fragment implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener{
    private FragmentActivity myContext;
    private View myFragmentView;
    private ArrayList<Home> filelist;
    private ListView myList;
    private HomeAdapter adapter;
    private SwipeRefreshLayout swipeRefreshLayout;

    //try save instance listview
    boolean fromBackStack = false;
    int index;
    int top;
    HomeAdapter savedAdapter;

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //try to save listview scroll
        index = myList.getFirstVisiblePosition();
        View v = myList.getChildAt(0);
        top = (v == null) ? 0 : (v.getTop() - myList.getPaddingTop());
        index = 1;
        //try to save listview items
        savedAdapter = adapter;
        fromBackStack = true;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        myList.setSelectionFromTop(index, top);
        myList.setAdapter(savedAdapter);
    }

    @Override
    public void onAttach(Activity activity) {
        myContext = (FragmentActivity) activity;
        super.onAttach(activity);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        myFragmentView = inflater.inflate(R.layout.fragment_home, container, false);

        filelist = new ArrayList<Home>();

        //dummy data
        filelist.add(new Home("post", 1)); //post image
        filelist.add(new Home("post", 2)); //post location
        filelist.add(new Home("post", 3)); //post url
        filelist.add(new Home("post", 4)); //become friend with
        filelist.add(new Home("post", 0)); //post status

        swipeRefreshLayout = (SwipeRefreshLayout) myFragmentView.findViewById(R.id.swipe_refresh_layout);
        swipeRefreshLayout.setOnRefreshListener(this);
        /**
         * Showing Swipe Refresh animation on activity create
         * As animation won't start on onCreate, post runnable is used
         */
        swipeRefreshLayout.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        swipeRefreshLayout.setRefreshing(true);
                                        //do something
                                        swipeRefreshLayout.setRefreshing(false);
                                    }
                                }
        );

        myList = (ListView) myFragmentView.findViewById(R.id.list_home);
        //myList.setAdapter(new HomeAdapter(myContext, filelist));
        adapter = new HomeAdapter(myContext, filelist);
        myList.setOnItemClickListener(this);

        if(!fromBackStack) {
            new RemoteDataTask().execute();
        }

        return myFragmentView;
    }

    /**
     * This method is called when swipe refresh is pulled down
     */
    @Override
    public void onRefresh() {
        //do something
        myList.setAdapter(adapter);
        ArrayList<Home> new_filelist = new ArrayList<>();
        new_filelist.add(new Home("Post", 4));

        filelist.addAll(0, new_filelist);
        adapter.notifyDataSetChanged();
        swipeRefreshLayout.setRefreshing(false);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Home tmp = (Home) parent.getItemAtPosition(position);
        DetailPostFragment detailPostFragment = new DetailPostFragment();
        Bundle args = new Bundle();
        switch (tmp.type) {
            case 0:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 1:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 2:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 3:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 4:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
        }
    }

    private class RemoteDataTask extends AsyncTask<String, Void, ArrayList<Home>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected ArrayList<Home> doInBackground(String... urls) {
            try {
                ArrayList<Home> new_filelist = new ArrayList<Home>();
                new_filelist.add(new Home("post", 3)); //post url
                new_filelist.add(new Home("post", 4)); //become friend with
                new_filelist.add(new Home("post", 0)); //post status
                filelist.addAll(new_filelist);
            } catch (ParseException e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }
            return filelist;
        }

        @Override
        protected void onPostExecute(ArrayList<Home> organization) {
            myList.setAdapter(new HomeAdapter(myContext, filelist));
            // Create an OnScrollListener
            myList.setOnScrollListener(new AbsListView.OnScrollListener() {

                @Override
                public void onScrollStateChanged(AbsListView view,
                                                 int scrollState) { // TODO Auto-generated method stub
                    int threshold = 1;
                    int count = myList.getCount();

                    if (scrollState == SCROLL_STATE_IDLE) {
                        if (myList.getLastVisiblePosition() >= count
                                - threshold) {
                            // Execute LoadMoreDataTask AsyncTask
                            new LoadMoreDataTask().execute();
                        }
                    }
                }

                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                                     int visibleItemCount, int totalItemCount) {
                    // TODO Auto-generated method stub

                }

            });
        }
    }

    public class LoadMoreDataTask extends AsyncTask<String, Void, ArrayList<Home>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected ArrayList<Home> doInBackground(String... urls) {
            try {
                // web service request
                ArrayList<Home> new_filelist = new ArrayList<Home>();
                new_filelist.add(new Home("post", 0)); //post status
                if(new_filelist  != null && new_filelist.size() > 0 ){
                    filelist.addAll(new_filelist);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return filelist;
        }

        @Override
        protected void onPostExecute(ArrayList<Home> organization) {
            int position = myList.getLastVisiblePosition();
            adapter = new HomeAdapter(myContext, filelist);
            myList.setAdapter(adapter);
            adapter.notifyDataSetChanged();
            myList.setSelectionFromTop(position, 0);
        }
    }

}

希望有人能帮帮我,谢谢。

更新的代码 到目前为止,这是我的代码,当我尝试保存列表视图项及其适配器时它可以工作,也许这有点棘手。如果有人有任何想法将不胜感激。

package com.ngapainya.ngapainya.fragment;
import android.app.Activity;
import android.graphics.drawable.ColorDrawable;
import android.net.ParseException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;

import com.ngapainya.ngapainya.R;
import com.ngapainya.ngapainya.activity.volunteer.ContainerActivity;
import com.ngapainya.ngapainya.adapter.HomeAdapter;
import com.ngapainya.ngapainya.fragment.child.DetailPostFragment;
import com.ngapainya.ngapainya.model.Home;

import java.util.ArrayList;

/**
 * A simple {@link Fragment} subclass.
 */
public class HomeFragment extends Fragment implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener{
    private FragmentActivity myContext;
    private View myFragmentView;
    private ArrayList<Home> filelist;
    private ListView myList;
    private HomeAdapter adapter;
    private SwipeRefreshLayout swipeRefreshLayout;

    /*
    * these variables used to restore the listview
    * when calling the fragment from backstack
    * */
    private boolean fromBackStack = false;
    private HomeAdapter savedAdapter;
    private ArrayList<Home> savedFilelist;

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        savedAdapter = adapter;
        savedFilelist = filelist;
        fromBackStack = true;
        Log.e("onDestroyView", "onDestroyView");
    }

    @Override
    public void onAttach(Activity activity) {
        myContext = (FragmentActivity) activity;
        super.onAttach(activity);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        myFragmentView = inflater.inflate(R.layout.fragment_home, container, false);

        Log.e("onCreateView", "onCreateView");

        swipeRefreshLayout = (SwipeRefreshLayout) myFragmentView.findViewById(R.id.swipe_refresh_layout);
        swipeRefreshLayout.setOnRefreshListener(this);
        /**
         * Showing Swipe Refresh animation on activity create
         * As animation won't start on onCreate, post runnable is used
         */
        /*swipeRefreshLayout.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        swipeRefreshLayout.setRefreshing(true);
                                        //do something
                                        swipeRefreshLayout.setRefreshing(false);
                                    }
                                }
        );*/

        myList = (ListView) myFragmentView.findViewById(R.id.list_home);
        //myList.setAdapter(new HomeAdapter(myContext, filelist));

        myList.setOnItemClickListener(this);

        filelist = new ArrayList<Home>();
        adapter = new HomeAdapter(myContext, filelist);

        if(fromBackStack) {
            filelist = savedFilelist;
            adapter = savedAdapter;
        }

        new RemoteDataTask().execute();
        return myFragmentView;
    }

    /**
     * This method is called when swipe refresh is pulled down
     */
    @Override
    public void onRefresh() {
        ArrayList<Home> new_filelist = new ArrayList<>();
        new_filelist.add(new Home("Post", 4));

        filelist.addAll(0, new_filelist);
        adapter.notifyDataSetChanged();
        swipeRefreshLayout.setRefreshing(false);

        Log.e("onRefresh", "onRefresh");
    }

    /*
    * This method is called whenever the listview item is clicked
    * */

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Home tmp = (Home) parent.getItemAtPosition(position);
        DetailPostFragment detailPostFragment = new DetailPostFragment();
        Bundle args = new Bundle();
        switch (tmp.type) {
            case 0:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 1:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 2:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 3:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
            case 4:
                args.putInt("postType", tmp.type);
                detailPostFragment.setArguments(args);
                ((ContainerActivity) getActivity()).changeFragment(detailPostFragment, new ColorDrawable(getResources().getColor(R.color.ColorPrimary)));
                break;
        }
    }

    /*
    * This method is called in onCreateView method
    * Used to load data from server and populate them into
    * the listview
    * */

    private class RemoteDataTask extends AsyncTask<String, Void, ArrayList<Home>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected ArrayList<Home> doInBackground(String... urls) {
            try {
                ArrayList<Home> new_filelist = new ArrayList<Home>();
                //dummy data
                new_filelist.add(new Home("post", 1)); //post image
                new_filelist.add(new Home("post", 2)); //post location
                new_filelist.add(new Home("post", 3)); //post url
                new_filelist.add(new Home("post", 4)); //become friend with
                new_filelist.add(new Home("post", 0)); //post status
                new_filelist.add(new Home("post", 3)); //post url
                new_filelist.add(new Home("post", 4)); //become friend with
                new_filelist.add(new Home("post", 0)); //post status
                filelist.addAll(new_filelist);
            } catch (ParseException e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }
            return filelist;
        }

        @Override
        protected void onPostExecute(ArrayList<Home> organization) {
            myList.setAdapter(adapter);
            // Create an OnScrollListener
            myList.setOnScrollListener(new AbsListView.OnScrollListener() {

                @Override
                public void onScrollStateChanged(AbsListView view,
                                                 int scrollState) { // TODO Auto-generated method stub
                    int threshold = 1;
                    int count = myList.getCount();

                    if (scrollState == SCROLL_STATE_IDLE) {
                        if (myList.getLastVisiblePosition() >= count
                                - threshold) {
                            // Execute LoadMoreDataTask AsyncTask
                            new LoadMoreDataTask().execute();
                        }
                    }
                }

                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                                     int visibleItemCount, int totalItemCount) {
                    // TODO Auto-generated method stub

                }

            });
        }
    }

    /*
    * This method is called when the listview pulled down
    * it will trigger this method to load more items from server
    * */

    public class LoadMoreDataTask extends AsyncTask<String, Void, ArrayList<Home>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected ArrayList<Home> doInBackground(String... urls) {
            try {
                // web service request
                ArrayList<Home> new_filelist = new ArrayList<Home>();
                new_filelist.add(new Home("post", 0)); //post status
                new_filelist.add(new Home("post", 1));
                if(new_filelist  != null && new_filelist.size() > 0 ){
                    filelist.addAll(new_filelist);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return filelist;
        }

        @Override
        protected void onPostExecute(ArrayList<Home> organization) {
            int position = myList.getLastVisiblePosition();
            //adapter = new HomeAdapter(myContext, filelist);
            myList.setAdapter(adapter);
            adapter.notifyDataSetChanged();
            myList.setSelectionFromTop(position, 0);
        }
    }

}

【问题讨论】:

    标签: android listview android-fragments fragment fragment-backstack


    【解决方案1】:

    终于明白了…… 我不知道这是否是最好的解决方案,但到目前为止它对我有用。 我只需要一个方法和几个变量,像这样。

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        savedAdapter = adapter;
        savedFilelist = filelist;
        fromBackStack = true;
        Log.e("onDestroyView", "onDestroyView");
    }
    

    谢谢。

    【讨论】:

    • 您必须在 onSaveInstanceState() 而不是 onDestroy() 中保存状态(如果需要)。关于您的适配器,您需要在 onCreate() 中对其进行初始化,可选择使用先前保存的状态填充它。当视图被销毁并重新创建时,它将被保留。一旦你在 ListView 上设置了初始化的适配器,滚动位置就会自动恢复。
    • @BladeCoder 问题是,我的片段没有被破坏而是分离,所以这就是为什么永远不会调用 onSaveInstanceState() 的原因。如果我可以使用 onSaveInstanceState() 而不是 onDestroy() 方法,那可能是最好的解决方案。您知道先生如何在这种情况下调用 onSaveInstanceState 吗?
    • 如果您的片段视图刚刚被销毁并重新创建,则不需要调用它。重新创建视图时,您的所有实例变量仍将被初始化,因此您只需重新附加您在 onCreate() 中初始化的现有适配器。
    • @BladeCoder 请检查我更新的问题和代码。非常感谢您的回复先生
    猜你喜欢
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-06
    相关资源
    最近更新 更多