繁体   English   中英

Android自定义视图在方向更改时重新创建两次,第二次没有onRestoreInstanceState

[英]Android custom view is recreated twice on orientation change, the second time without onRestoreInstanceState

我创建了一个自定义视图,它是RelativeLayout的子类。 视图是Fragment集的一部分,用于保留其实例状态。 我想保存状态信息(只有一个布尔值)时的设备的方向变化,所以我实现onSaveInstanceStateonRestoreInstanceState与我的自定义扩展BaseSavedState FragmentActivity都没有实现任何处理保存/恢复实例状态的事情。 当我更改设备方向时,它会保存状态,使用保存的状态重新创建视图,但随后重新创建它而不调用onRestoreInstanceState

事件序列如下所示, showingProgress是我要保存的布尔值:

View#1 (onSaveInstanceState) showingProgress=true

View#2 (ctor) showingProgress=false
View#2 (onFinishInflate)
View#2 (onRestoreInstanceState) showingProgress=true
View#2 (showProgress)
View#2 (onSaveInstanceState) showingProgress=true

View#3 (ctor) showingProgress=false
View#3 (onFinishInflate)

之后,重建视图,但由于从未为View#3调用onRestoreInstanceState ,因此它始终处于初始状态。

为什么要重新创建两次视图? 如何防止第二个视图再次重新创建,或者将保存的状态分别传递给第三个视图?

编辑:

活动的相关部分

private Fragment currentFragment = null;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.single_fragment);
    getSupportActionBar().setDisplayShowTitleEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().show();
    showLoginFragment();
}

public void showLoginFragment()
{
    final FragmentTransaction t = this.getSupportFragmentManager().beginTransaction();
    currentFragment = MyFragment.newInstance();
    t.replace(R.id.layoutRoot, currentFragment);
    t.commit();
}

布局single_fragment.xml

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

MyFragment

public class MyFragment extends RoboFragment
{
    // I'm using roboguice
    @InjectView(R.id.myButton) private ProgressButton myButton;

    public static MyFragment newInstance()
    {
        Bundle arguments = new Bundle();
        // no arguments for now, this comes later
        MyFragment fragment = new MyFragment();
        fragment.setArguments(arguments);
        return fragment;
    } 

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        setRetainInstance(true);
        View result = inflater.inflate(R.layout.my_fragment, container, false);
        return result;
    }
}

布局my_fragment.xml

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

    <com.my.package.ProgressButton
        android:id="@+id/myButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/button_caption" />

</LinearLayout>

最后是ProgressButton的代码

/**
 * A Button with an integrated progress spinner. The button can show the progress spinner while some background process
 * is running to indicate it can't be clicked again.
 */
public class ProgressButton extends RelativeLayout
{
    /** The view holding the button text */
    private TextView textView = null;

    /** The progress spinner */
    private ProgressBar progressSpinner = null;

    private boolean showingProgress = false;

    public ProgressButton(Context context)
    {
        super(context);
        textView = new TextView(context);
        progressSpinner = new ProgressBar(context);
        initView();
    }

    public ProgressButton(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        textView = new TextView(context, attrs);
        progressSpinner = new ProgressBar(context, attrs);
        initView();
    }

    public ProgressButton(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        textView = new TextView(context, attrs, defStyle);
        progressSpinner = new ProgressBar(context, attrs, defStyle);
        initView();
    }

    /**
     * Initializes the view with all its properties.
     */
    @SuppressWarnings("deprecation")
    private void initView()
    {
        // remove the background attributes from progressbar and textview, because they should be transparent
        if (Build.VERSION.SDK_INT >= 16)
        {
            textView.setBackground(null);
            progressSpinner.setBackground(null);
        }
        else
        {
            textView.setBackgroundDrawable(null);
            progressSpinner.setBackgroundDrawable(null);
        }
    }

    @Override
    protected void onFinishInflate()
    {
        super.onFinishInflate();
        if (!isInEditMode())
        {
            progressSpinner.setVisibility(View.INVISIBLE);
        }
        RelativeLayout layout = new RelativeLayout(getContext());
        layout.setId(R.id.progressButtonContainer);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        this.addView(layout, params);

        params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.ALIGN_LEFT);
        params.leftMargin = 10;
        progressSpinner.setClickable(false);
        progressSpinner.setId(R.id.progressButtonProgress);
        layout.addView(progressSpinner, params);

        params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        params.addRule(RelativeLayout.CENTER_IN_PARENT);
        textView.setClickable(false);
        textView.setId(R.id.progressButtonText);
        layout.addView(textView, params);
    }

    /**
     * Disables the button and shows the progress spinner.
     */
    public void showProgress()
    {
        this.setEnabled(false);
        progressSpinner.setVisibility(View.VISIBLE);
        showingProgress = true;
    }

    /**
     * Enables the button and hides the progress spinner.
     */
    public void hideProgress()
    {
        this.setEnabled(true);
        progressSpinner.setVisibility(View.INVISIBLE);
        showingProgress = false;
    }

    @Override
    public Parcelable onSaveInstanceState()
    {
        Parcelable superState = super.onSaveInstanceState();
        return new SavedState(superState, showingProgress);
    }

    @Override
    public void onRestoreInstanceState(Parcelable state)
    {
        SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        showingProgress = savedState.showProgress;
        if(showingProgress)
        {
            showProgress();
        }
        else
        {
            hideProgress();
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    protected void dispatchSaveInstanceState(SparseArray container) 
    {
        super.dispatchFreezeSelfOnly(container);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    protected void dispatchRestoreInstanceState(SparseArray container) 
    {
        super.dispatchThawSelfOnly(container);
    }

    static class SavedState extends BaseSavedState
    {
        boolean showProgress = false;

        SavedState(Parcelable superState, boolean showProgress)
        {
            super(superState);
            this.showProgress = showProgress;
        }

        private SavedState(Parcel in)
        {
            super(in);
            this.showProgress = in.readByte() == 0 ? false : true;
        }

        @Override
        public void writeToParcel(Parcel out, int flags)
        {
            super.writeToParcel(out, flags);
            out.writeByte((byte) (showProgress ? 1 : 0));
        }

        // required field that makes Parcelables from a Parcel
        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
        {
            public SavedState createFromParcel(Parcel in)
            {
                return new SavedState(in);
            }

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

在Daniel Bo的有益评论之后,我很容易得到解决方案。 无论是否存在保存状态,都会在活动中添加片段。 这导致在初始状态下添加View#3。 包装showLoginFragment(); 在以下if循环中的Activities onCreate方法中解决了问题。

if(savedInstanceState == null)
{
    showLoginFragment();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM