FragmentPagerAdapter不能恢复时姿势变化的片段 [英] FragmentPagerAdapter not restoring fragments upon Orientation Change

查看:321
本文介绍了FragmentPagerAdapter不能恢复时姿势变化的片段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:我发现该活动是节省了实例,但保存的数据的片段不补差的活动savedInstanceState。我节省了当前时间在outState,但它不是做它的方式一路上涨,作为活动无关,在其SavedInstanceState的时间和如果将它打印到logcat的时间返回'零'.. ..

我的建筑,建在一个向上计数和倒计时器的应用程序。对于定时器的基本举办活动是它承载了膨胀的code中的两个片段一个FragementPagerAdapter(我不给一个id一个FragmentActivity到XML中的片段,因为它们不将.xml内定义为片段)。一切都很正常,直到改变方向,然后将活动看起来像它失去与旧片段接触,只是选择创建新的。这意味着任何倒数丢失并且选择任何时间也丢失时的配置改变。我需要保持计数去(如果它启动)和当前显示的任何数字....

我知道适配器应该自行处理这些事情,我设置在这两个的OnCreate和OnCreateView的SetRetainInstance(真)。

我把挂钩插入片段code,让我知道每当saveInstanceState不空,因此至少我知道是怎么回事,但似乎该实例始终是NULL,并从头开始创建。 ..always。

一些其他的解决方案中有我重写instantiateItem,但似乎它只是用于获取回调复位,改变别人在清单的设置(皱眉)......其他解决方案看起来对我来说,我有事情设置正确的...但显然,我一路上搞乱的东西了。我只给code为FragmentActivity,FragementPagerAdapter,有点的片段code,因为我不想垃圾邮件与code,它可能不是问题的帖子。

该FragmentActivity

 公共类Timer_Main扩展FragmentActivity {
    ViewPager寻呼机;
    Timer_Pager mAdapter;    公共无效的onCreate(捆绑savedInstanceState){
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.timer_pager);        mAdapter =新Timer_Pager(在此,getSupportFragmentManager());        寻呼机=(ViewPager)findViewById(R.id.timer_pager_display);
        pager.setAdapter(mAdapter);    }    公共静态字符串的getTitle(上下文ctxt,INT位置){
                // TODO自动生成方法存根
        返回null;
    }    @覆盖
    保护无效的onSaveInstanceState(捆绑outState){
        super.onSaveInstanceState(outState);
        outState.putInt(pageItem,pager.getCurrentItem());    }    @覆盖
    保护无效onRestoreInstanceState(捆绑savedInstanceState){
        super.onRestoreInstanceState(savedInstanceState);    }
}

该FragementPagerAdapter

 公共类Timer_Pager扩展FragmentPagerAdapter {
    上下文ctxt = NULL;
    公共Timer_Pager(上下文ctxt,FragmentManager FM){
        超(FM);
        this.ctxt = ctxt;
        }
    @覆盖
    公共片段的getItem(INT位置){        开关(位置){
        情况下0:{
            返回(Countdown_Fragment.newInstance());
        }
        情况1:
            返回(Countup_Fragment.newInstance());
        }
        返回null;
    }    @覆盖
    公共字符串getPageTitle(INT位置){        开关(位置){
        情况下0:
            返回(的String.format(ctxt.getString(R.string.Countdown_label)));
        情况1:
            返回(的String.format(ctxt.getString(R.string.Countup_label)));
        }
        返回null;
    }    @覆盖
    公众诠释的getCount(){
        //做只有三页,现在...一个倒计时,一个用于计数进位,一个用于Tabata的。
        返回2;
    }    私有静态字符串makeFragmentName(INT viewId,INT位置)
    {
         回归机器人:切换:+ viewId +:+位置;
    }
}

该片段中有更多的code,所以我会推在短短的应该是什么问题的核心....如果需要更多的,我会在拼接吧。

 公共类Countdown_Fragment扩展片段实现OnClickListener {
    日历CountdownTime = Calendar.getInstance();
    静态AutoResizeTextView电视;
    静态的ImageButton HoursUp;
    静态的ImageButton HoursDown;
    静态的ImageButton MinutesUp;
    静态的ImageButton MinutesDown;
    静态的ImageButton SecondsUp;
    静态的ImageButton SecondsDown;
    静态的ImageButton复位;
    静态的ImageButton StartPausecount;
    静态的ImageButton Stopcount;
    静态布尔Arewecounting = FALSE;
    静态布尔Arewecountingdown = FALSE;
    静态AutoResizeTextView ThreetwooneGO;
    静态MyCountdownTimer Countdown_Timer_Activity;
    ThreetwooneCount Start_countdown;
    静态龙MillstoPass;    静态的newInstance Countdown_Fragment(){        Countdown_Fragment FRAG =新Countdown_Fragment();
        返回(FRAG);
    }    @覆盖
    公共无效的onSaveInstanceState(捆绑outState){        outState.putString(CurrentTimer(字符串)tv.getText());
        Log.v(身份,保存的片段状态+ tv.getText());
        super.onSaveInstanceState(outState);
    }    @覆盖
    公共无效onAttach(活动活动){
        super.onAttach(活动);    }    @覆盖
    公共无效的onCreate(捆绑savedInstanceState){
        setRetainInstance(真);
        super.onCreate(savedInstanceState);
    }    @覆盖
    公共查看onCreateView(LayoutInflater充气器,容器的ViewGroup,
            捆绑savedInstanceState){            setRetainInstance(真);
            查看结果= inflater.inflate(R.layout.countdown_timer_layout,
                集装箱,FALSE);
        电视=(AutoResizeTextView)result.findViewById(R.id.timer_textview);
        tv.setOnClickListener(新View.OnClickListener(){
            公共无效的onClick(查看为arg0){
                新TimePickerDialog(getActivity(),叔,0,0,真).show();
            }
        });
        如果(savedInstanceState!= NULL){
            Log.v(身份,片段不为空);
            tv.setText(savedInstanceState.getString(CurrentTimer
                    tv.toString()));
        }其他{
            Log.v(身份,片段为空);
        // tv.setText(00:00:00);        }
        tv.resizeText();        ThreetwooneGO =(AutoResizeTextView)结果
                .findViewById(R.id.timer_countdown_text);        HoursUp =(的ImageButton)result.findViewById(R.id.hours_up);
        HoursDown =(的ImageButton)result.findViewById(R.id.hours_down);
        MinutesUp =(的ImageButton)result.findViewById(R.id.minutes_up);
        MinutesDown =(的ImageButton)result.findViewById(R.id.minutes_down);
        SecondsUp =(的ImageButton)result.findViewById(R.id.seconds_up);
        SecondsDown =(的ImageButton)result.findViewById(R.id.seconds_down);
        复位=(的ImageButton)result.findViewById(R.id.reset);
        StartPausecount =(ImageButton的)结果
                .findViewById(R.id.startpausecount);
        Stopcount =(的ImageButton)result.findViewById(R.id.stopcount);        HoursUp.setOnClickListener(本);
        HoursDown.setOnClickListener(本);
        SecondsUp.setOnClickListener(本);
        SecondsDown.setOnClickListener(本);
        MinutesUp.setOnClickListener(本);
        MinutesDown.setOnClickListener(本);
        Reset.setOnClickListener(本);
        StartPausecount.setOnClickListener(本);
        Stopcount.setOnClickListener(本);        返回(结果);
    }    公共无效chooseTime(视图v){
        新TimePickerDialog(getActivity(),叔,0,0,真).show();
    }    TimePickerDialog.OnTimeSetListener T =新TimePickerDialog.OnTimeSetListener(){
        公共无效onTimeSet(TimePicker观点,诠释HOUROFDAY,分整型){
            CountdownTime.set(Calendar.HOUR_OF_DAY,HOUROFDAY);
            CountdownTime.set(Calendar.MINUTE,分钟);
            CountdownTime.set(Calendar.SECOND,0);
            CountdownTime.set(Calendar.MILLISECOND,0);            updateLabel();
        }
    };    私人无效updateLabel(){        串entiretime;
        entiretime =的String.format(%TT,CountdownTime);
        tv.setText(entiretime);
    }

下面是我的计时器,我使用倒计时......

 公共类MyCountdownTimer {私人长期millisInFuture;
私人长期countDownInterval;
Countdown_Fragment ctxt;
AutoResizeTextView Timer_edit;
私人挥发性布尔IsStopped = FALSE;公共MyCountdownTimer(长pMillisInFuture,长pCountDownInterval,
        AutoResizeTextView Artv){
    this.millisInFuture = pMillisInFuture;
    this.countDownInterval = pCountDownInterval;    Timer_edit = Artv;}公共无效启动(){    最后的处理程序处理程序=新的处理程序();
    最终的可运行计数器=新的Runnable(){        公共无效的run(){
            如果(IsStopped == FALSE){
                如果(millisInFuture&下; = 0){
                    Countdown_Fragment.done_counting();
                }其他{
                    长秒= millisInFuture / 1000;
                    Timer_edit.setText(的String.format(%02D:%02D:%02D
                            秒/ 3600,(仲丁基%3600)/ 60,(仲丁基%60)));
                    millisInFuture - = countDownInterval;
                    handler.postDelayed(这一点,countDownInterval);
                }
            }
        }
    };    handler.postDelayed(计数器,countDownInterval);
}公共无效取消(){
    IsStopped = TRUE;
    Timer_edit = NULL;
    Log.v(身份,主定时器取消);
    // this.ctxt = NULL;}
众长whatisourcount(){
    回报(millisInFuture);
}
}


解决方案

事实证明,该RetainInstance是导致自身和ViewPager /适配器/ FragmentManager做自己的东西之间​​的冲突。删除它造成的寻呼机正确重建片段,包括TextView的我,在它之前没有。我也开始在OnCreateView,那是什么地方总是空与设置为True RetainInstance之前,免费获赠捆绑。

我不得不删除RetainInstance和利用的onSaveInstanceState和OnCreateView在片段的当前状态,通过它被摧毁之前,然后在OnCreateView重新创建它的碎片复位到其状态就被毁掉了。

我希望Runnable接口,我用做倒计时能够生存下去,否则我将能够重新装上,但我无法找到一个方法。我不得不保存毫秒当前计数,并通过回片段,继续停止的地方。它不是什么大不了的事,但我很好奇,看看你是否能忠实地重新连接所有这些事情。可运行确实还继续配置更改后,但它不会再更新UI上任何东西,所以我尝试取消回调和空它,当我的onSaveInstanceState里面

我还在读书的项目,我只需要使用RetainInstance对附有一个AsyncTask的或其他类似的项目,否则....项目,只是code之内重建它。

EDIT: I found out that the Activity is saving the instance, but the Fragments saved data is not making it up to the Activity savedInstanceState. I'm saving the current time in the outState, but its not making its way all the way up, as the activity has nothing in its SavedInstanceState for the time and returns 'null' for the time if I print it to the logcat....

I am building an application that has the a countup and countdown timer built in. The basic hosting activity for the timers is a FragmentActivity which hosts a FragementPagerAdapter that inflates two fragments within the code (I do not give an id to the fragments within the XML as they are not defined as fragments within the .xml). Everything works great until an orientation change and then the activity looks like it looses contact with the old fragments and just chooses to create new ones. This means that any current countdown is lost and any time chosen is also lost upon configuration change. I will need to keep the count going (if its started) and any numbers currently displayed....

I know that the Adapter is supposed to handle these things on its own, and I'm setting the SetRetainInstance(true) in both the OnCreate and OnCreateView.

I put in hooks into the Fragment code to let me know whenever the saveInstanceState is NOT null so at least I know what is going on, but it seems like the instance is always NULL, and it creates from scratch...always.

Some other solutions have me overriding the instantiateItem, but it seems that its is only used for getting callbacks reset, others changing a setting in the Manifest(frowned upon).... Other solutions look to me like I have things setup right...but obviously, I'm messing something up along the way. I'm only giving code for the FragmentActivity, FragementPagerAdapter, and a bit of the Fragment code as I don't want to spam the post with code that may not be the issue.

The FragmentActivity

public class Timer_Main extends FragmentActivity {
    ViewPager pager;
    Timer_Pager mAdapter;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.timer_pager);

        mAdapter = new Timer_Pager(this, getSupportFragmentManager());

        pager = (ViewPager) findViewById(R.id.timer_pager_display);
        pager.setAdapter(mAdapter);

    }

    public static String getTitle(Context ctxt, int position) {
                // TODO Auto-generated method stub
        return null;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("pageItem", pager.getCurrentItem());

    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

    }
}    

The FragementPagerAdapter

public class Timer_Pager extends FragmentPagerAdapter{
    Context ctxt = null;
    public Timer_Pager(Context ctxt, FragmentManager fm) {
        super(fm);
        this.ctxt = ctxt;
        }


    @Override
    public Fragment getItem(int position) {

        switch (position) {
        case 0: {
            return(Countdown_Fragment.newInstance());
        }
        case 1:
            return(Countup_Fragment.newInstance());
        }
        return null;
    }

    @Override
    public String getPageTitle(int position) {

        switch (position) {
        case 0: 
            return(String.format(ctxt.getString(R.string.Countdown_label)));
        case 1: 
            return(String.format(ctxt.getString(R.string.Countup_label)));
        }
        return null;
    }

    @Override
    public int getCount() {
        // doing only three pages, right now...one for countdown, one for countup,         and one for Tabata.
        return 2;
    }

    private static String makeFragmentName(int viewId, int position)
    {
         return "android:switcher:" + viewId + ":" + position;
    }
}    

The Fragment has a bit more code in it, so I'll push in just what should be the core of the problem....if more is needed, I'll splice it in.

public class Countdown_Fragment extends Fragment implements OnClickListener {
    Calendar CountdownTime = Calendar.getInstance();
    static AutoResizeTextView tv;
    static ImageButton HoursUp;
    static ImageButton HoursDown;
    static ImageButton MinutesUp;
    static ImageButton MinutesDown;
    static ImageButton SecondsUp;
    static ImageButton SecondsDown;
    static ImageButton Reset;
    static ImageButton StartPausecount;
    static ImageButton Stopcount;
    static Boolean Arewecounting = false;
    static Boolean Arewecountingdown = false;
    static AutoResizeTextView ThreetwooneGO;
    static MyCountdownTimer Countdown_Timer_Activity;
    ThreetwooneCount Start_countdown;
    static Long MillstoPass;

    static Countdown_Fragment newInstance() {

        Countdown_Fragment frag = new Countdown_Fragment();
        return (frag);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {

        outState.putString("CurrentTimer", (String) tv.getText());
        Log.v("status", "saved fragment state" + tv.getText());
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        setRetainInstance(true);
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

            setRetainInstance(true);
            View result = inflater.inflate(R.layout.countdown_timer_layout,
                container, false);
        tv = (AutoResizeTextView) result.findViewById(R.id.timer_textview);
        tv.setOnClickListener(new View.OnClickListener() {
            public void onClick(View arg0) {
                new TimePickerDialog(getActivity(), t, 0, 0, true).show();
            }
        });
        if (savedInstanceState != null) {
            Log.v("status", "fragment is NOT empty");
            tv.setText(savedInstanceState.getString("CurrentTimer",
                    tv.toString()));
        } else {
            Log.v("status", "fragment is empty");
        //  tv.setText("00:00:00");

        }
        tv.resizeText();

        ThreetwooneGO = (AutoResizeTextView) result
                .findViewById(R.id.timer_countdown_text);

        HoursUp = (ImageButton) result.findViewById(R.id.hours_up);
        HoursDown = (ImageButton) result.findViewById(R.id.hours_down);
        MinutesUp = (ImageButton) result.findViewById(R.id.minutes_up);
        MinutesDown = (ImageButton) result.findViewById(R.id.minutes_down);
        SecondsUp = (ImageButton) result.findViewById(R.id.seconds_up);
        SecondsDown = (ImageButton) result.findViewById(R.id.seconds_down);
        Reset = (ImageButton) result.findViewById(R.id.reset);
        StartPausecount = (ImageButton) result
                .findViewById(R.id.startpausecount);
        Stopcount = (ImageButton) result.findViewById(R.id.stopcount);

        HoursUp.setOnClickListener(this);
        HoursDown.setOnClickListener(this);
        SecondsUp.setOnClickListener(this);
        SecondsDown.setOnClickListener(this);
        MinutesUp.setOnClickListener(this);
        MinutesDown.setOnClickListener(this);
        Reset.setOnClickListener(this);
        StartPausecount.setOnClickListener(this);
        Stopcount.setOnClickListener(this);

        return (result);
    }

    public void chooseTime(View v) {
        new TimePickerDialog(getActivity(), t, 0, 0, true).show();
    }

    TimePickerDialog.OnTimeSetListener t = new TimePickerDialog.OnTimeSetListener() {
        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            CountdownTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
            CountdownTime.set(Calendar.MINUTE, minute);
            CountdownTime.set(Calendar.SECOND, 0);
            CountdownTime.set(Calendar.MILLISECOND, 0);

            updateLabel();
        }
    };

    private void updateLabel() {

        String entiretime;
        entiretime = String.format("%tT", CountdownTime);
        tv.setText(entiretime);
    }

Here is my timer that I'm using for Countdown....

public class MyCountdownTimer {

private long millisInFuture;
private long countDownInterval;
Countdown_Fragment ctxt;
AutoResizeTextView Timer_edit;
private volatile boolean IsStopped = false;

public MyCountdownTimer(long pMillisInFuture, long pCountDownInterval,
        AutoResizeTextView Artv) {
    this.millisInFuture = pMillisInFuture;
    this.countDownInterval = pCountDownInterval;

    Timer_edit = Artv;

}

public void Start() {

    final Handler handler = new Handler();
    final Runnable counter = new Runnable() {

        public void run() {
            if (IsStopped == false) {
                if (millisInFuture <= 0) {
                    Countdown_Fragment.done_counting();
                } else {
                    long sec = millisInFuture / 1000;
                    Timer_edit.setText(String.format("%02d:%02d:%02d",
                            sec / 3600, (sec % 3600) / 60, (sec % 60)));
                    millisInFuture -= countDownInterval;
                    handler.postDelayed(this, countDownInterval);
                }
            }
        }
    };

    handler.postDelayed(counter, countDownInterval);
}

public void Cancel() {
    IsStopped = true;
    Timer_edit = null;
    Log.v("status", "Main Timer cancelled");
    // this.ctxt = null;

}
public long whatisourcount(){
    return(millisInFuture);
}
}

解决方案

As it turns out, the RetainInstance was causing a conflict between itself and the ViewPager/Adapter/FragmentManager doing their things . Removing it caused the Pager to properly rebuilt the Fragment, including the TextView I had, where it did not before. I also start to recieve a Bundle in the OnCreateView, where that was always null before with the RetainInstance set to True.

I had to remove the RetainInstance and utilize the OnSaveInstanceState and OnCreateView to pass in the current status of the Fragment before it was destroyed, and then re-create it in OnCreateView to reset the Fragment to its state before it was destroyed.

I was hoping that the Runnable that I was using to do the countdown would survive, or I would be able to reattach it, but I couldn't find a way. I had to save the current count in Milliseconds, and pass back to the Fragment to continue where it left off. Its not that big of a deal, but I am curious to see if you can truely re-attach all those things. The Runnable DOES still continue after the config change, but it doesn't update anything on the UI anymore, so I try to cancel the callbacks and null it when I'm inside OnSaveInstanceState.

I'm also reading items where I only need to use RetainInstance for items that have a AsyncTask attached or another similar item....otherwise, just rebuild it within the code.

这篇关于FragmentPagerAdapter不能恢复时姿势变化的片段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆