【问题标题】:Can't save fragment state in Android while configuration is changed更改配置时无法在 Android 中保存片段状态
【发布时间】:2016-08-11 12:43:23
【问题描述】:

这种类型的问题在 SO 中被问了很多次,我已经尝试了所有这些,但无法达到我真正想要的。在我的应用程序中我有Fragment,在那个片段中我有一个recycler 视图。 recyclerview 将被我从 API 获得的数据所操纵。

我想要的是,当旋转发生变化时,应用不会再次调用 API。为了实现这一点,我知道我必须将数据放入onSaveInstanceState,如果我想保存一个复杂的对象,那么该类必须实现Parcelable。我已经完成了推荐的所有操作,但无法实现我想要的。

我在这里分享我的代码,我创建了一个简化的示例。

SimpleFragment.java 的代码

public class SimpleFragment extends Fragment {

    RecyclerView recyclerView;
    ArrayList<TestModel> testModelList = new ArrayList<TestModel>() ;
    SimpleAdapter simpleAdapter;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.simple_fragment, container, false);
        recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler);

        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(mLayoutManager);

        simpleAdapter = new SimpleAdapter(getActivity());

        recyclerView.setAdapter(simpleAdapter);


        if(savedInstanceState != null){
            //if this fragment starts after a rotation or configuration change, load the existing model from a parcelable
            testModelList = savedInstanceState.getParcelableArrayList("key");
            // I have done necessary debug , after rotation , it comes here ,
            // but suddenly savedInstanceState becomes null and getTestModelList() get called


        }else {
            //if this fragment starts for the first time, load the list of model
           getTestModelList();
        }

        simpleAdapter.setModel(testModelList);

        return rootView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelableArrayList("key",testModelList);
    }

    public void getTestModelList(){
        for (int i=0;i<10;i++){
            testModelList.add(new TestModel(i,"test"+i));
        }
        Toast.makeText(getActivity(),"Called",Toast.LENGTH_SHORT).show();
    }
}

SimpleFragment 的布局文件:simple_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:background="@android:color/black"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

这是我的数据模型的代码TestModel.java

public class TestModel implements Parcelable {

    String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public TestModel(int number, String text) {
        this.text = text;
    }


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.text);
    }

    protected TestModel(Parcel in) {
        this.text = in.readString();
    }

    public static final Parcelable.Creator<TestModel> CREATOR
            = new Parcelable.Creator<TestModel>() {
        public TestModel createFromParcel(Parcel in) {
            return new TestModel(in);
        }

        public TestModel[] newArray(int size) {
            return new TestModel[size];
        }
    };
}

我的适配器类:SimpleAdapter.java

public class SimpleAdapter extends RecyclerView.Adapter<SimpleAdapter.ViewHolder> {

    Context context;
    ArrayList<TestModel> testModels;

    public SimpleAdapter(Context context) {
        this.context = context;
    }

    public void setModel(ArrayList<TestModel> model){
        this.testModels = model;
        notifyDataSetChanged();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.single_row, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.list_item.setText(testModels.get(position).getText());
    }

    @Override
    public int getItemCount() {
        return testModels.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        TextView list_item;
        public ViewHolder(View itemView) {
            super(itemView);
            list_item = (TextView) itemView.findViewById(R.id.list_item);
        }
    }
}

适配器的单行布局代码single_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:id="@+id/list_item"
        android:textColor="@android:color/white"/>

</LinearLayout>

这些是我所做的,如果再次调用 SimpleFragment 中的方向 getTestModelList() 方法,我想避免这种情况;在实际应用场景中,此方法将执行一些 API 调用。我想保存ArrayList&lt;TestModel&gt; testModelList 的状态。

【问题讨论】:

    标签: android android-fragments parcelable android-savedstate


    【解决方案1】:

    您需要保存活动状态并在配置前检查它

    遵循此代码

    public class MainActivity extends AppCompatActivity{
    
    
        private HomeFragment homeFragment;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            FragmentManager fm = getSupportFragmentManager();
    
    
               if (savedInstanceState == null) {
                homeFragment = new HomeFragment();
    
    
                ((fm.beginTransaction()).replace(R.id.mainframe, homeFragment)).commit();
            }
    }}}
    

    【讨论】:

      【解决方案2】:

      您可以像这样在 Fragment onCreate 方法中保存 Fragment 实例以更改方向以保存实例:

      @Override
          public void onCreate(@Nullable Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
               // Set Instance true
              this.setRetainInstance(true);
          }
      

      【讨论】:

      • 谢谢,试过了,没用,改变方向时调用getTestModelList()函数。
      • 无需调用getTestModelList() 方法...当您的实例设置为true 时,它会在您更改方向时保持您的状态RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(mLayoutManager); simpleAdapter = new SimpleAdapter(getActivity()); recyclerView.setAdapter(simpleAdapter); getTestModelList(); simpleAdapter.setModel(testModelList); 这样做
      • 我没有显式调用,这个方法是自动调用的
      • 你试过我在评论中提到的以下代码吗?
      • 在清单android:configChanges="orientation|screenSize"中的片段活动中设置这样的
      【解决方案3】:

      只要方向改变,Android 就会重新创建您的 Activity。这背后的原因是您可能需要更改布局,但您仍然可以使用 onConfigurationChanged()onConfigurationChanged()Fragment 我建议您添加

      android:configChanges="orientation|keyboardHidden|screenSize"

      在您的清单文件中(请参阅 thisthis)。这将停止 Activity on Confifuration 更改的重新创建。

      【讨论】:

      • 谢谢,我知道这一点,但是如果我想对横向使用单独的布局怎么办?在这种方法中,单独的布局不会加载。
      • 就像我说的你仍然可以通过onConfigurationChanged() 回调来实现。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-04
      相关资源
      最近更新 更多