在通过 ActionBar 添加的 Fragment 上调用 addToBackStack()

Posted

技术标签:

【中文标题】在通过 ActionBar 添加的 Fragment 上调用 addToBackStack()【英文标题】:Calling addToBackStack() on a Fragment that is added via ActionBar 【发布时间】:2012-10-04 08:49:31 【问题描述】:

所以基本上,我有一个简单的 Fragment,它使用 AsyncTask 从 URL 下载位图,然后将其显示在 ImageView 中。

这是片段的代码:

public class TestFragment extends Fragment 

public TestFragment () 

private String pictureUrl = "https://fbcdn-sphotos-d-a.akamaihd.net/hphotos-ak-prn1/532762_10150739418088211_1186720820_n.jpg";

private TextView sampleText;
private ImageView sampleImage;

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

    View root = inflater.inflate(R.layout.fragment_view, container, false); 
    String textToSet = "Section Three";

    sampleText = (TextView) root.findViewById(R.id.textView1);
    sampleText.setText(textToSet);

    sampleImage = (ImageView) root.findViewById(R.id.imageView1);
    DownloadImageTask task = new DownloadImageTask(pictureUrl, root, sampleImage.getId());
    task.execute();

    return root;   
       

同一片段显示 3 次,在 ActionBar 内的每个选项卡中显示 3 次。我遇到的问题是,每次用户选择不同的选项卡时,前一个选项卡都会被破坏,当用户导航回原始选项卡时,需要重新下载内容。我想确保消耗最少的资源(保存数据和有价值的开销处理)。

这是 Fragment Container 活动:

public class LayoutContainer extends Activity

@Override
public void onCreate(Bundle savedInstanceState) 

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layout_container);      

    if (savedInstanceState != null) 
        return;
    

    final ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    Tab tab = actionBar.newTab()
            .setText(R.string.title_section1)
            .setTabListener(new TabListener<TestFragment>(
                    this, "one", TestFragment.class));
    actionBar.addTab(tab);

    tab = actionBar.newTab()
            .setText(R.string.title_section2)
            .setTabListener(new TabListener<TestFragment>(
                    this, "two", TestFragment.class));
    actionBar.addTab(tab);

    tab = actionBar.newTab()
            .setText(R.string.title_section3)
            .setTabListener(new TabListener<TestFragment>(
                    this, "three", TestFragment.class));
    actionBar.addTab(tab);



public static class TabListener<T extends Fragment> implements ActionBar.TabListener 

    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(Activity activity, String tag, Class<T> clz) 
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    

    public void onTabSelected(Tab tab, FragmentTransaction ft) 
        if (mFragment == null) 
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
        
        ft.replace(android.R.id.content, mFragment, mTag);
    

    public void onTabUnselected(Tab tab, FragmentTransaction ft) 
        ft.addToBackStack(null);
        ft.commit();
    

    public void onTabReselected(Tab tab, FragmentTransaction ft) 
    

我目前已将其设置为在 OnTabSelected() 方法上调用 ft.replace(),但我之前曾尝试使用 ft.add() 。我当前在 OnTabUnselected() 中的 ft.addToBackStack() 调用也是如此,我最初将其设置为简单地调用 ft.detach()但这与我描述的问题相同。它以这种方式加载,但每次都必须下载图像。

现在,我尝试调用 addToBackStack(),但应用程序强制关闭,说明无法将 FragmentTransaction 添加到后台堆栈。我过去也尝试过离线缓存图像,为了避免数据消耗,应用程序仍然需要从本地 URI 解析图像并使用处理能力。

我可以尝试任何想法吗?

仅供参考,是的,我知道同一个片段在所有三个选项卡上加载同一个图像。这只是一个测试项目,旨在帮助我更好地了解 Fragments 的生命周期。

【问题讨论】:

【参考方案1】:

所以经过大量阅读后,我意识到重要的代码块不是在您的 TabListener 上完成,而是实际上在您使用的每个 Fragment 上完成。

当导航离开片段时,片段将被分离,但这并不意味着我不能使用 onSaveInstanceState(...) 保留它的状态。

这里是在分离后保留 Fragment 状态所需的简单实现。

public class TimelineFragment extends ListFragment
...

public TimelineFragment () 

public void onSaveInstanceState(Bundle outState)
    getActivity().getFragmentManager().putFragment(outState, TAG, this);        


public void onRetoreInstanceState(Bundle inState)
    getActivity().getFragmentManager().getFragment(inState, TAG);

...

我们通过调用 putFragment() 来保存 Fragment,并通过调用 getFragment() 来恢复它。非常简单,我从来没有想过。

【讨论】:

【参考方案2】:

当用户需要来自网络的图像时我会做什么我首先检查我是否已缓存它或图像是否在 SD 中。如果在 SD 或缓存中找不到图像,我会下载图像,将其存储到 SD 并添加到缓存中。

通常是这样的:

    Bitmap cacheBitmap = Database.getImageFromMemory(exercise.image, getActivity());

    if(cacheBitmap != null) 
        image.setImageBitmap(cacheBitmap);
        progressBar.setVisibility(View.GONE);
    
    Bitmap localBitmap = ImageDownloader.decodeSampledBitmap(exercise.image, width, height, getActivity());

    if(localBitmap != null) 
        image.setImageBitmap(localBitmap);
        progressBar.setVisibility(View.GONE);
        Database.addBitmapToMemoryCache(exercise.image, localBitmap, getActivity());
     else 
        ImageDownloader imageDownloader = new ImageDownloader(this.getActivity().getApplicationContext(), image, progressBar, width, height);
        imageDownloader.execute(exercise.image);
    

现在这不是所有代码,但它让您了解如何处理它。如果您需要更多代码,请随时提出,但您似乎走在了正确的轨道上。

【讨论】:

虽然你在回答我想到的一个问题,但你并没有真正回答线程的问题,即在调用 onDetach() 时如何保留特定片段的实例状态。跨度>

以上是关于在通过 ActionBar 添加的 Fragment 上调用 addToBackStack()的主要内容,如果未能解决你的问题,请参考以下文章

如何在代码中将子菜单项添加到 ActionBar 操作?

ActionBar上没有应用程序图标

如何在ActionBar(Android)中添加按钮?

如何在Android中将ActionBar添加到Activity

安卓studio 怎么设置actionbar显示

关于ActionBar 左侧添加完返回后 点击无效的问题