【问题标题】:How to load Fragment only once and save its state?如何仅加载一次 Fragment 并保存其状态?
【发布时间】:2013-12-20 08:28:16
【问题描述】:

我有一个 mainActivity 从中调用片段列表。

现在,在这个列表中,我有一个名为 MyAccount 的片段,它使用 web 服务显示帐户详细信息列表。

我想要的是我只想加载 myAccount 详细信息一次(第一次)片段加载,而不是每次加载。(现在正在发生)。此外,我想在操作栏中设置一个刷新按钮,所以单击该刷新按钮/图像时,将加载并显示帐户详细信息。

这是我的实现::

主要活动中的getFragement() ::

private List<Fragment> getFragments()
    {
        List<Fragment> fList = new ArrayList<Fragment>();


            fList.add(Fragment.instantiate(this,Fragment1.class.getName()));


            fList.add(Fragment.instantiate(this,Fragment2.class.getName()));



            fList.add(Fragment.instantiate(this,Fragment3.class.getName()));



        fList.add(MyAccountFragment.newInstance(this,"My Account"));
        fList.add(ReportFragment.newInstance(this,"Reports"));
        fList.add(SettingsListFragment.newInstance(this,"Settings"));

        return fList;
    }

调用适配器 ::

actionbartabmenuAdapter = new ActionBarTabMenuAdapter(getSupportFragmentManager(), fragments,this);
awesomePager.setAdapter(actionbartabmenuAdapter);

适配器 ::

private class ActionBarTabMenuAdapter extends FragmentPagerAdapter  {
        Activity context;
        Context ctx;
        private List<Fragment> fragments;

        public ActionBarTabMenuAdapter(FragmentManager fm, List<Fragment> fragments,Context ctx,)
        {
            super(fm);
            this.context=(Activity) ctx;

            this.fragments = fragments;
        }

        @Override
        public int getCount() 
        {
            return fragments.size();
        }
        @Override
        public Fragment getItem(int position) {

            System.out.println("position of fragment--"+position);
            return this.fragments.get(position);
        }
    }

onTabSelected/Reselected 方法 ::

@Override
    public void onTabReselected(Tab tabposition, FragmentTransaction fragmentposition) {
        System.out.println("Tab Reselected method");

    }

    @Override
    public void onTabSelected(Tab tabposition, FragmentTransaction fragmentposition) {

        awesomePager.setCurrentItem(tabposition.getPosition());
    }

    @Override
    public void onTabUnselected(Tab tabposition, FragmentTransaction fragmentposition) {
        System.out.println("Tab unselected method");

    }

MyAccountFragment ::

public class MyAccountFragment extends SherlockFragment{

    static MyAccountFragment f ;
    ListView list_subscriberInfo;
    ProgressDialog pd = null;
    MyAccountInfo myAccountInfo = new MyAccountInfo();
    private MyApplication appContext;
    static Context context;

    public static MyAccountFragment newInstance(Activity ctx,String string)
    {
        f = new MyAccountFragment();
        context=ctx;
        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSherlockActivity().supportInvalidateOptionsMenu(); // now onCreateOptionsMenu(...) is called again
        this.setHasOptionsMenu(true);
    }

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

        View v = inflater.inflate(R.layout.my_account, container, false);
        list_subscriberInfo = (ListView)v.findViewById(R.id.list_subscriberInfo);

        return v;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) 
    {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_new, menu);
    }

    public void onPrepareOptionsMenu(Menu menu) 
    {
        super.onPrepareOptionsMenu(menu);
    }
    public void setUserVisibleHint(boolean isVisibleToUser) 
    {
        super.setUserVisibleHint(isVisibleToUser);
        pd=new ProgressDialog(context);
        if (isVisibleToUser) 
        {
            pd =ProgressDialog.show(context,null, "Please Wait..", true);
            pd.setContentView(R.layout.progress);
            Thread thread = new Thread(getPersonalInfo);
            thread.start();
        }
        else
        {
            if(pd.isShowing())
            {
                pd.dismiss();
            }
        }
    }
}

请注意,我在 myAccountFragment 中设置了 setUserVisibleHint() 以仅在该片段可见性为真时才显示帐户信息。

我知道我的问题的解决方案是 setOffScreenLimit(),但我不知道在哪里/如何设置我的实现。

但是如何同时使用这两种方法意味着只有在片段可见并且不每次都加载片段时才能看到我的帐户。?仅当在操作栏中单击刷新按钮时才加载(第二次)。

希望我清楚。

提前谢谢...

编辑::

应用 LuksProg 的逻辑...

MyContainerActivity ::

MyAccountFragment mSavedAccount;

public void cacheAccount(MyAccountFragment account) {
        mSavedAccount = account;
    }

    public Object getCachedAccount() {
        return mSavedAccount;
    }

MyAccountFragment ::

@Override
    public void onActivityCreated(Bundle savedInstanceState) 
    {
        super.onActivityCreated(savedInstanceState);
        containerActivity = (ButtonPayActivity) getActivity();
        pd=new ProgressDialog(context);

        if (containerActivity.getCachedAccount() != null)
        {
            //Do nothing
            if(pd.isShowing())
            {
                pd.dismiss();
            }
        }
        else
        {
            // the data isn't available so this must be a fresh start
            // start the thread, remember to use act.cacheAccount() when the thread finishes
            pd =ProgressDialog.show(context,null, "Please Wait..", true);
            pd.setContentView(R.layout.progress);
            Thread thread = new Thread(getPersonalInfo);
            thread.start();
        }
    }

编辑 2 ::

应用进度对话框的逻辑后,我遇到了以下问题。

我所做的是显示并取消进度对话框,但是在我移至帐户的下一个选项卡后,我的应用程序崩溃了...

awesomePager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {

            @Override
            public void onPageSelected(int position)
            {
                //supportInvalidateOptionsMenu();
                actionBar.setSelectedNavigationItem(position);
                 if (position == 4)
                 {
                        MyAccountFragment df = (MyAccountFragment) actionbartabmenuAdapter.getItem(position);
                    if (df.getThread() != null && df.getThread().isAlive()) 
                    {
                        df.showDialog();
                    }
                    else
                    {
                        df.cancelDialog();
                    }
                }
             }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                super.onPageScrollStateChanged(state);
            }
        });

myAccountFragment ::

@Override
    public void onActivityCreated(Bundle savedInstanceState) 
    {
        super.onActivityCreated(savedInstanceState);
        containerActivity = (ButtonPayActivity) getActivity();
        pd=new ProgressDialog(context);

        if (containerActivity.getCachedAccount() != null)
        {

            if(pd.isShowing())
            {
                pd.dismiss();
            }
        }
        else
        {

            pd =ProgressDialog.show(context,null, "Please Wait..", true);
            pd.setContentView(R.layout.progress);
            thread = new Thread(getPersonalInfo);
            thread.start();
        }
    }

    public void showDialog() 
    {
        if (pd != null) 
        {
            pd.show(); // the dialog will be started from the OnPageChangeListener only
        }
    }

    public void cancelDialog() 
    {
        if (pd != null) 
        {
            pd.dismiss();
        }
    }

    public Thread getThread() 
    {
        return thread;
    }

Logcat ::

12-24 20:31:20.076: E/AndroidRuntime(15596): FATAL EXCEPTION: main
12-24 20:31:20.076: E/AndroidRuntime(15596): java.lang.ClassCastException: com.example.app.home.ReportFragment
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.example.app.ContainerActivity$1.onPageSelected(ContainerActivity.java:151)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:438)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:405)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:386)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.example.app.ContainerActivity.onTabSelected(ContainerActivity)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.actionbarsherlock.internal.app.ActionBarImpl.selectTab(ActionBarImpl.java:531)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.actionbarsherlock.internal.app.ActionBarImpl$TabImpl.select(ActionBarImpl.java:912)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.actionbarsherlock.internal.widget.ScrollingTabContainerView$TabClickListener.onClick(ScrollingTabContainerView.java:504)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.view.View.performClick(View.java:2485)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.view.View$PerformClick.run(View.java:9080)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.os.Handler.handleCallback(Handler.java:587)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.os.Handler.dispatchMessage(Handler.java:92)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.os.Looper.loop(Looper.java:130)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at android.app.ActivityThread.main(ActivityThread.java:3683)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at java.lang.reflect.Method.invokeNative(Native Method)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at java.lang.reflect.Method.invoke(Method.java:507)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-24 20:31:20.076: E/AndroidRuntime(15596):    at dalvik.system.NativeStart.main(Native Method)

问题仍然存在,只是出于好奇,我在我的代码中应用了以下内容,但没有成功。 现在我在 myAccount Fragment 获得可见性之前获得了进度对话框,因此我将onActivityCreated() 应用于setUserVisibleHint() 并保持其余部分不变。

但是我的应用程序也崩溃了,我得到了以下日志..

Logcat ::

12-24 20:22:26.125: E/AndroidRuntime(14861): FATAL EXCEPTION: main
12-24 20:22:26.125: E/AndroidRuntime(14861): java.lang.NullPointerException
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.example.app.home.MyAccountFragment.setUserVisibleHint(MyAccountFragment.java:183)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.support.v4.app.FragmentPagerAdapter.instantiateItem(FragmentPagerAdapter.java:102)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:649)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.support.v4.view.ViewPager.populate(ViewPager.java:783)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:433)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:405)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:386)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.example.app.ContainerActivity.onTabSelected(ContainerActivity.java:385)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.actionbarsherlock.internal.app.ActionBarImpl.selectTab(ActionBarImpl.java:531)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.actionbarsherlock.internal.app.ActionBarImpl$TabImpl.select(ActionBarImpl.java:912)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.actionbarsherlock.internal.widget.ScrollingTabContainerView$TabClickListener.onClick(ScrollingTabContainerView.java:504)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.view.View.performClick(View.java:2485)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.view.View$PerformClick.run(View.java:9080)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.os.Handler.handleCallback(Handler.java:587)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.os.Handler.dispatchMessage(Handler.java:92)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.os.Looper.loop(Looper.java:130)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at android.app.ActivityThread.main(ActivityThread.java:3683)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at java.lang.reflect.Method.invokeNative(Native Method)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at java.lang.reflect.Method.invoke(Method.java:507)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
12-24 20:22:26.125: E/AndroidRuntime(14861):    at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

  • 您的应用是否包含 ABS+ 选项卡+ 多片段?对吗?
  • @Sharmilee 是的,你完全正确......

标签: android android-viewpager fragment


【解决方案1】:

我只想加载 myAccount 详细信息一次(第一次)片段 已加载,不是每次都加载。(现在正在发生)。

如果您只想加载一次数据,那么您需要在第一次检索数据时将其缓存在活动/片段级别。因此,您需要在包含片段的Activity 中实现两个方法来设置和获取数据:

public void cacheAccount(Object account) {
    mSavedAccount = account;
}

public Object getCachedAccount() {
    return mSavedAccount;
}

然后在MyAccountFragment 片段中,您将实现以下逻辑:

  • 使用一种生命周期方法来放置用于检索数据的代码(所以,不要使用setUserVisibleHint()),例如onActivityCreated()
  • 在那里,您需要使用上述方法首先查看数据是否不可用(如果之前已加载片段并保存数据)。如果数据可用,则直接使用它,不要启动检索线程,否则启动线程
  • 如果线程已启动,则当它完成时,您需要设置数据并将其保存在活动中,以便将来使用 MyAccountFragment 片段时可用

模拟实现:

// in the onActivityCreated() method:
// cast the activity
YourActivity act = (YourActivity) getActivity();
// check if the data isn't by any chance already available
if (act.getCachedAccount() != null) {
     // the data is already available so use it
} else {
     // the data isn't available so this must be a fresh start
     // start the thread, remember to use act.cacheAccount() when the thread finishes
}

另外,如果您想在配置更改中保存数据,您可能需要使用带有上述方法的片段,该片段设置为保留配置(为此调用setRetainInstance(true))。

此外,我想在操作栏中设置一个刷新按钮,所以点击 该刷新按钮/图像的帐户详细信息将被加载和 显示。

这应该不是问题,只要您记得在检索线程完成时调用activity.cacheAccount(),以便您拥有可供将来使用的数据。

我知道我的问题的解决方案是 setOffScreenLimit() 但我不知道 知道在哪里/如何在我的实现中设置它。

setOffScreenLimit() 将增加适配器保留在内存中的片段数。根据您的片段的重量,它可能会起作用,但通常您应该避免使用更大的值(在您的情况下,您基本上会将所有ViewPager 的片段保存在内存中)。

请注意,您当前对ViewPager 的适配器的实现(传递给它一个预先构建的片段列表)在某些情况下会失败,因为您构建的片段列表不会包含片段ViewPager 实际使用的。

编辑 实施:

private SparseArray<Fragment> getFragments() {
     // I used a SparseArray but a list will also do
     SparseArray<Fragment> fList = new SparseArray<Fragment>();
     fList.put(Fragment.instantiate(this,Fragment1.class.getName()));
     fList.put(Fragment.instantiate(this,Fragment2.class.getName()));
     fList.put(Fragment.instantiate(this,Fragment3.class.getName()));
     fList.put(MyAccountFragment.newInstance(this,"My Account"));
     fList.put(ReportFragment.newInstance(this,"Reports"));
     fList.put(SettingsListFragment.newInstance(this,"Settings"));
     return fList;
}

// the adapter
private class ActionBarTabMenuAdapter extends FragmentPagerAdapter  {
        Activity context;
        Context ctx;
        private SparseArray<Fragment> fragments;

        public ActionBarTabMenuAdapter(FragmentManager fm, SparseArray<Fragment> fragments,Context ctx) {
            super(fm);
            this.context=(Activity) ctx;
            this.fragments = fragments;
        }

        @Override
        public int getCount() {
            return fragments.size();
        }

        @Override
        public Fragment getItem(int position) {
            System.out.println("position of fragment--"+position);
            return this.fragments.get(position);
        }

        @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        fragments.put(position, null);
        super.destroyItem(container, position, object);
    } 

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment f = (Fragment) super.instantiateItem(container, position);
        fragments.put(position, f);
        return f;
    }
    }

第二次编辑: 仅当您位于 MyAccountFragment 片段时才显示对话框:

 mViewPager.setOnPageChangeListener(new OnPageChangeListener() {

@Override
public void onPageSelected(int position) {
    if (position == 3) {
                MyAccountFragment df = (MyAccountFragment) adapter.getItem(position);
            if (df.getThread() != null && df.getThread().isAlive()) {
                df.showDialog();
            } else {
                df.cancelDialog();
            }
        }
     }

// rest of the methods
 }

这些方法都在MyAccountFragment中实现:

  public void showDialog() {
        if (pd != null) {
            pd.show(); // the dialog will be started from the OnPageChangeListener only
        }
    }

    public void cancelDialog() {
        if (pd != null) {
            pd.dismiss(); // remeber to call this when the thread finishes
        }
    }

    public Thread getThread() {
        return mThread; // this will be the reference to the started thread
    }

【讨论】:

  • 正如你所说,我当前的 viewpager 实现在某些情况下可能会失败。所以你能在那部分纠正我吗?
  • @AndroidLearner 我已经用一个例子编辑了我的答案。您需要保持引用同步。
  • fragments.delete(position);fragments.put(position, f); 显示错误 The method put(int, Fragment) is undefined for the type List&lt;Fragment&gt;
  • @AndroidLearner 我使用了SparseArray,它有一个put() 方法,而List 没有。如果您想使用List,请使用其set(position, fragment) 方法。
  • 我已经在我的代码中设置了你的逻辑。请检查编辑。我现在面临的问题是,每当我点击 myaccount 的上一个或下一个片段时,它都会向我显示进度对话框。如何解决那个?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 2019-06-13
  • 2013-12-06
  • 2011-10-13
相关资源
最近更新 更多