多次旋转时出现碎片异常

时间:2012-07-12 15:08:35

标签: android android-fragments

我的应用程序有一个带有1或2页的仪表板(根据用户配置文件)。

当应用程序第一次启动时,它只显示3个按钮,并且当信息到达时,它可以添加更多3个按钮(总共6个)。这些按钮有一个特定的顺序,应该保留:

  • 按钮1是强制性的,应该是第一个;
  • 按钮2是强制性的,应该是第二个;
  • 按钮6是强制性的,应该是最后一个(当有3,4,5或6时)
  • 按钮3到5不是强制性的,但应按其编号顺序显示。

为了产生这种效果,我有几个占位符,其中添加了片段。每当一个新按钮到达时(按钮3到5),它就会进入Button_6的旧位置,向前移动一个位置。

由于复杂的用户界面和行为,每个按钮都是一个片段。

应用程序可以是纵向和横向,纵向有4个按钮,横向有3个按钮。每当旋转应用程序时,活动都会调用refreshDashboard方法来重绘UI。

代码:

public class DashboardBuilder
{
    private final static String TAG_BUTTON_1 = "BUTTON_1";
    private final static String TAG_BUTTON_2 = "BUTTON_2";
    private final static String TAG_BUTTON_3 = "BUTTON_3";
    private final static String TAG_BUTTON_4 = "BUTTON_4";
    private final static String TAG_BUTTON_5 = "BUTTON_5";
    private final static String TAG_BUTTON_6 = "BUTTON_6";

    private int width = 0;
    private int height = 0;
    private int pages = 0;

    private int buttonsPerPage = 4;
    private int orientation = Configuration.ORIENTATION_PORTRAIT;

    public final String TAG = getClass().getSimpleName();

    public DashboardBuilder(DashboardPaginationActivity dashboard)
    {      
            FragmentManager.enableDebugLogging(false);   
    }

    //This is called each time there is a rotation. 
    public void rebuildDashboard(DashboardPaginationActivity dashboard, int orientation)
    {
        switch (orientation)
        {
            case Configuration.ORIENTATION_LANDSCAPE:
            case Configuration.ORIENTATION_SQUARE:
                buttonsPerPage = 3;
                break;
            default:
                buttonsPerPage = 4;
                break;
        }

        this.width = dashboard.getWidth();
        this.height = dashboard.getHeight();

        // Orientation changed
        if (this.orientation != orientation)
        {
            this.orientation = orientation;
            pages = 0;
        }
        rebuildDashboard(dashboard);
    }

    private synchronized void rebuildDashboard(DashboardPaginationActivity dashboard)
    {
        final Application application = (Application) dashboard.getApplication();
        final ArrayList<FragmentReference> fragmentsReferences = application.getFragmentsReferences();

        if (fragmentsReferences.size() == 0)
        {
            try
            {
                final String[] tags = new String[] { TAG_BUTTON_1, TAG_BUTTON_2, TAG_BUTTON_3, TAG_BUTTON_4, TAG_BUTTON_5, TAG_BUTTON_6 };
                for (String tag : tags)
                {
                    Fragment fragment = dashboard.getSupportFragmentManager().findFragmentByTag(tag);
                    if (fragment != null)
                    {
                        LOG.debug(LOG.TAG_ADAPTER, this, "rebuildDashboard : delete tag -> " + tag, null);
                        fragmentsReferences.add(new FragmentReference(tag, fragment.getId()));
                    }
                }

                final ViewGroup pages = (ViewGroup) dashboard.findViewById(R.id.dashboard_pages);
                pages.removeAllViews();

                final ViewGroup pagination = (ViewGroup) dashboard.findViewById(R.id.pagination);
                pagination.removeAllViews();
            }
            catch (Exception e)
            {
            }

            // Definitely a first run
            if (fragmentsReferences.size() == 0)
            {
                // add mandatory buttons + refresh layout
                fragmentsReferences.add(new FragmentReference(TAG_BUTTON_1, -1));
                fragmentsReferences.add(new FragmentReference(TAG_BUTTON_2, -1));
                fragmentsReferences.add(new FragmentReference(TAG_BUTTON_6, -1));
            }

            refreshDashboard(dashboard, false);
        }
        else
        {
            // refresh layout
            refreshDashboard(dashboard, true);
        }

    }

    public void refreshDashboard(DashboardPaginationActivity dashboard, boolean exitOnNewButton)
    {
        final Application application = (Application) dashboard.getApplication();
        final ArrayList<FragmentReference> fragmentsReferences = application.getFragmentsReferences();

        int buttonsCounter = fragmentsReferences.size();

        int pagesCounter = (buttonsCounter / (buttonsPerPage + 1)) + 1;
        for (int i = pages; i < pagesCounter; i++)
        {
            final View pageInflator = createPage(dashboard, i);
            if (pageInflator != null && i > 0)
            {
                swapIDs(i, pageInflator);
            }
        }

        final FragmentManager manager = dashboard.getSupportFragmentManager();
        final FragmentTransaction transaction = manager.beginTransaction();
        for (int i = fragmentsReferences.size() - 1; i >= 0; i--)
        {
            Fragment fragment = null;
            if (TAG_BUTTON_1.equalsIgnoreCase(fragmentsReferences.get(i).getTag()))
            {
                fragment = manager.findFragmentByTag(TAG_BUTTON_1);
                if (fragment != null)
                {
                    transaction.remove(fragment);
                }
                ButtonOneFragment button = new ButtonOneFragment();
                button.setRetainInstance(false);
                transaction.replace(getPlaceHolder(i), button, TAG_BUTTON_1);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

                fragmentsReferences.get(i).setPlaceHolder(getPlaceHolder(i));
            }
            else if (TAG_BUTTON_2.equalsIgnoreCase(fragmentsReferences.get(i).getTag()))
            {
                fragment = manager.findFragmentByTag(TAG_BUTTON_2);
                if (fragment != null)
                {
                    transaction.remove(fragment);
                }
                ButtonTwoFragment button = new ButtonTwoFragment();
                button.setRetainInstance(false);
                transaction.replace(getPlaceHolder(i), button, TAG_BUTTON_2);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

                fragmentsReferences.get(i).setPlaceHolder(getPlaceHolder(i));

            }
            else if (TAG_BUTTON_3.equalsIgnoreCase(fragmentsReferences.get(i).getTag()))
            {

                fragment = manager.findFragmentByTag(TAG_BUTTON_3);
                if (fragment != null)
                {
                    transaction.remove(fragment);
                }
                ButtonThreeFragment button = new ButtonThreeFragment();
                button.setRetainInstance(false);
                transaction.replace(getPlaceHolder(i), button, TAG_BUTTON_3);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                fragmentsReferences.get(i).setPlaceHolder(getPlaceHolder(i));

            }
            else if (TAG_BUTTON_4.equalsIgnoreCase(fragmentsReferences.get(i).getTag()))
            {
                fragment = manager.findFragmentByTag(TAG_BUTTON_4);
                if (fragment != null)
                {
                    transaction.remove(fragment);
                }
                ButtonFourFragment button = new ButtonFourFragment();
                button.setRetainInstance(false);
                transaction.replace(getPlaceHolder(i), button, TAG_BUTTON_4);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                fragmentsReferences.get(i).setPlaceHolder(getPlaceHolder(i));
            }
            else if (TAG_BUTTON_5.equalsIgnoreCase(fragmentsReferences.get(i).getTag()))
            {
                fragment = manager.findFragmentByTag(TAG_BUTTON_5);
                if (fragment != null)
                {
                    transaction.remove(fragment);
                }
                ButtonFiveFragment button = new ButtonFiveFragment();
                button.setRetainInstance(false);
                transaction.replace(getPlaceHolder(i), button, TAG_BUTTON_5);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                fragmentsReferences.get(i).setPlaceHolder(getPlaceHolder(i));
            }
            else if (TAG_BUTTON_6.equalsIgnoreCase(fragmentsReferences.get(i).getTag()))
            {
                fragment = manager.findFragmentByTag(TAG_BUTTON_6);
                if (fragment != null)
                {
                    transaction.remove(fragment);
                }
                ButtonSixFragment button = new ButtonSixFragment();
                button.setRetainInstance(false);
                transaction.replace(getPlaceHolder(i), button, TAG_BUTTON_6);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                fragmentsReferences.get(i).setPlaceHolder(getPlaceHolder(i));
            }

            if (exitOnNewButton)
            {
                // on first new button found
                if (fragmentsReferences.get(i).getPlaceHolder() < 0) break;
            }
        }

        buttonsCounter = fragmentsReferences.size();
        pagesCounter = (buttonsCounter / (buttonsPerPage + 1)) + 1;

        for (int i = pages; i > pagesCounter; i--)
        {
            // scroll to first if removing
            dashboard.changePage(0);
            removePage(dashboard, i - 1);
        }

        pages = pagesCounter;
        dashboard.setTotalScreens(pages);

        try
        {
            // we suspect that this is only required when a remove happens
            // keep it here in all cases to be on the safe site...
            transaction.commit();
            manager.executePendingTransactions();
        }
        catch (Exception e)
        {
            // ignore exception
            LOG.error(LOG.TAG_FRAGMENT, this, "refreshDashboard", e.getMessage());
            // e.printStackTrace();
        }
    }

    private View createPage(DashboardPaginationActivity dashboard, int index)
    {
        final View pageHolder = dashboard.getLayoutInflater().inflate(R.layout.dashboard_page, null);
        pageHolder.setLayoutParams(new LinearLayout.LayoutParams(getHorizontalScrollWidth(dashboard), getHorizontalScrollHeight(dashboard)));

        View existingView = null;

        final View page = pageHolder.findViewById(R.id.page);
        switch (index)
        {
            case 0:
                if (dashboard.findViewById(R.id.page1) == null)
                {
                    page.setId(R.id.page1);
                }
                existingView = dashboard.findViewById(R.id.page1);
                break;
            case 1:
                if (dashboard.findViewById(R.id.page2) == null)
                {
                    page.setId(R.id.page2);
                }
                existingView = dashboard.findViewById(R.id.page2);
                break;
            default:
                break;
        }

        if (existingView != null)
        {
            return null;
        }

        // add page holder to dashboard

        ViewGroup pages = ((ViewGroup) dashboard.findViewById(R.id.dashboard_pages));
        pages.addView(pageHolder);

        return pageHolder;
    }

    private void removePage(DashboardPaginationActivity dashboard, int index)
    {
        if (((ViewGroup) dashboard.findViewById(R.id.dashboard_pages)).getChildCount() > index)
        {
            ((ViewGroup) dashboard.findViewById(R.id.dashboard_pages)).removeViewAt(index);
        }
    }

          private void swapIDs(int index, View pageInflator)
    {
        if (orientation == Configuration.ORIENTATION_LANDSCAPE || orientation == Configuration.ORIENTATION_SQUARE)
        {
            swapLandscapeIDs(index, pageInflator);
        }
        else
        {
            swapPortraitIDs(index, pageInflator);
        }
    }

    private Integer getFragmentPlaceHolder(DashboardPaginationActivity dashboard, String tag)
    {
        if (tag != null)
        {
            final Application application = (Application) dashboard.getApplication();
            final ArrayList<FragmentReference> fragmentsReferences = application.getFragmentsReferences();

            for (int i = 0; i < fragmentsReferences.size(); i++)
            {
                final String fragmentTag = fragmentsReferences.get(i).getTag();
                if (tag.equalsIgnoreCase(fragmentTag))
                {
                    return fragmentsReferences.get(i).getPlaceHolder();
                }
            }
        }
        return null;
    }

    private Integer addFragmentReference(DashboardPaginationActivity dashboard, String tag)
    {
        int index = 0;
        if (tag != null)
        {
            final Application application = (Application) dashboard.getApplication();
            final ArrayList<FragmentReference> fragmentsReferences = application.getFragmentsReferences();

            if (tag.equalsIgnoreCase(TAG_BUTTON_1))
            {
                fragmentsReferences.add(0, new FragmentReference(TAG_BUTTON_1, -1));
                index = 0;
            }

            if (tag.equalsIgnoreCase(TAG_BUTTON_2))
            {
                fragmentsReferences.add(1, new FragmentReference(TAG_BUTTON_2, -1));
                index = 1;
            }

            if (tag.equalsIgnoreCase(TAG_BUTTON_3))
            {
                fragmentsReferences.add(2, new FragmentReference(TAG_BUTTON_3, -1));
                index = 2;
            }
            else if (tag.equalsIgnoreCase(TAG_BUTTON_4))
            {
                if (TAG_BUTTON_3.equalsIgnoreCase(fragmentsReferences.get(2).getTag()))
                {
                    fragmentsReferences.add(3, new FragmentReference(TAG_BUTTON_4, -1));
                    index = 3;
                }
                else
                {
                    fragmentsReferences.add(2, new FragmentReference(TAG_BUTTON_4, -1));
                    index = 2;
                }
            }
            else if (tag.equalsIgnoreCase(TAG_BUTTON_5))
            {
                if (fragmentsReferences.size() == 5 && TAG_BUTTON_4.equalsIgnoreCase(fragmentsReferences.get(3).getTag()))
                {
                    fragmentsReferences.add(4, new FragmentReference(TAG_BUTTON_5, -1));
                    index = 4;
                }
                else if (fragmentsReferences.size() == 4 && (TAG_BUTTON_3.equalsIgnoreCase(fragmentsReferences.get(2).getTag()) || TAG_BUTTON_4.equalsIgnoreCase(fragmentsReferences.get(2).getTag())))
                {
                    fragmentsReferences.add(3, new FragmentReference(TAG_BUTTON_5, -1));
                    index = 3;
                }
                else
                {
                    fragmentsReferences.add(2, new FragmentReference(TAG_BUTTON_5, -1));
                    index = 2;
                }
            }
        }
        return index;
    }

    private void removeFragmentReference(DashboardPaginationActivity dashboard, String tag)
    {
        if (tag != null)
        {
            final Application application = (Application) dashboard.getApplication();
            final ArrayList<FragmentReference> fragmentsReferences = application.getFragmentsReferences();

            // start at 2 means that button 1 and 2 will never be removed
            for (int i = 0; i < fragmentsReferences.size(); i++)
            {
                final String fragmentTag = fragmentsReferences.get(i).getTag();
                if (tag.equalsIgnoreCase(fragmentTag))
                {
                    fragmentsReferences.remove(i);
                    break;
                }
            }

            final FragmentManager manager = dashboard.getSupportFragmentManager();
            final Fragment fragment = manager.findFragmentByTag(tag);
            if (fragment != null)
            {
                final FragmentTransaction transaction = manager.beginTransaction();

                // remove store references and other resources
                // ((ButtonFragment) fragment).cleanup();

                transaction.remove(fragment);
                transaction.commit();

                try
                {
                    // we suspect that this is only required when a remove happens
                    // keep it here in all cases to be on the safe site...
                    manager.executePendingTransactions();
                }
                catch (Exception e)
                {
                    // ignore exception
                    LOG.debug(LOG.TAG_FRAGMENT, this, "removeFragmentReference", null);
                }
            }
        }
    }

    public boolean buttonExists(DashboardPaginationActivity dashboard, String tag)
    {
        if (tag == null) return false;

        final Application application = (Application) dashboard.getApplication();
        final ArrayList<FragmentReference> fragmentsReferences = application.getFragmentsReferences();

        for (int i = 0; i < fragmentsReferences.size(); i++)
        {
            final String fragmentTag = fragmentsReferences.get(i).getTag();
            if (tag.equalsIgnoreCase(fragmentTag))
            {
                return true;
            }
        }
        return false;
    }

    private int getPlaceHolder(int index)
    {
        switch (index)
        {
            case 0:
                return R.id.placeHolder01;
            case 1:
                return R.id.placeHolder02;
            case 2:
                return R.id.placeHolder03;
            case 3:
                return R.id.placeHolder04;
            case 4:
                return R.id.placeHolder05;
            case 5:
                return R.id.placeHolder06;
            case 6:
                return R.id.placeHolder07;
            case 7:
                return R.id.placeHolder08;
            case 8:
                return R.id.placeHolder09;
            case 9:
                return R.id.placeHolder10;
            case 10:
                return R.id.placeHolder11;
            case 11:
                return R.id.placeHolder12;
            default:
                return -1;
        }
    }

    private void swapPortraitIDs(int index, View pageInflated)
    {
        switch (index)
        {
            case 1:
                pageInflated.findViewById(R.id.placeHolder01).setId(R.id.placeHolder05);
                pageInflated.findViewById(R.id.placeHolder02).setId(R.id.placeHolder06);
                pageInflated.findViewById(R.id.placeHolder03).setId(R.id.placeHolder07);
                pageInflated.findViewById(R.id.placeHolder04).setId(R.id.placeHolder08);
                break;
            case 2:
                pageInflated.findViewById(R.id.placeHolder01).setId(R.id.placeHolder09);
                pageInflated.findViewById(R.id.placeHolder02).setId(R.id.placeHolder10);
                pageInflated.findViewById(R.id.placeHolder03).setId(R.id.placeHolder11);
                pageInflated.findViewById(R.id.placeHolder04).setId(R.id.placeHolder12);
                break;
            default:
                LOG.error(LOG.TAG_ACTIVITY, activity, "swapPortraitIDs", "Invalid page number: " + index);
                break;
        }

    }

    private void swapLandscapeIDs(int index, View pageInflated)
    {
        switch (index)
        {
            case 1:
                pageInflated.findViewById(R.id.placeHolder01).setId(R.id.placeHolder04);
                pageInflated.findViewById(R.id.placeHolder02).setId(R.id.placeHolder05);
                pageInflated.findViewById(R.id.placeHolder03).setId(R.id.placeHolder06);
                break;
            case 2:
                pageInflated.findViewById(R.id.placeHolder01).setId(R.id.placeHolder07);
                pageInflated.findViewById(R.id.placeHolder02).setId(R.id.placeHolder08);
                pageInflated.findViewById(R.id.placeHolder03).setId(R.id.placeHolder09);
                break;
            default:
                LOG.error(LOG.TAG_ACTIVITY, activity, "swapLandscapeIDs", "Invalid page number: " + index);
                break;
        }
    }


    public int[] getPagesIds(AbstractActivity activity)
    {
        switch (pages)
        {
            case 1:
                return new int[] { R.id.page1 };
            case 2:
                return new int[] { R.id.page1, R.id.page2 };
            default:
                LOG.error(LOG.TAG_ACTIVITY, activity, "getPagesIds", "Invalid page number: " + pages);
                return new int[0];
        }
    }

}

我不得不接受manager.executePendingTransactions();使用try catch因为它会“随机”抛出java.lang.IllegalStateException:如果在很短的时间内发生大量旋转,则会递归进入executePendingTransactions。

我认为这是由于Fragments Manager的异步特性造成的。

我已经“解决”了那些尝试捕获的问题..但我想知道是否有更正确的方法来解决它和/或建议。

在Try catchs之前的StackTrace:

19:04:47.402 E/AndroidRuntime( 1352): FATAL EXCEPTION: main 
 19:04:47.402 E/AndroidRuntime( 1352): java.lang.IllegalStateException: Recursive entry to executePendingTransactions 
 19:04:47.402 E/AndroidRuntime( 1352): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1388) 
 19:04:47.402 E/AndroidRuntime( 1352): at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:420) 
 19:04:47.402 E/AndroidRuntime( 1352): at android.os.Handler.handleCallback(Handler.java:587) 
 19:04:47.402 E/AndroidRuntime( 1352): at android.os.Handler.dispatchMessage(Handler.java:92) 
 19:04:47.402 E/AndroidRuntime( 1352): at android.os.Looper.loop(Looper.java:123) 
 19:04:47.402 E/AndroidRuntime( 1352): at android.app.ActivityThread.main(ActivityThread.java:3683) 
 19:04:47.402 E/AndroidRuntime( 1352): at java.lang.reflect.Method.invokeNative(Native Method) 
 19:04:47.402 E/AndroidRuntime( 1352): at java.lang.reflect.Method.invoke(Method.java:507) 
 19:04:47.402 E/AndroidRuntime( 1352): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 
 19:04:47.402 E/AndroidRuntime( 1352): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 
 19:04:47.402 E/AndroidRuntime( 1352): at dalvik.system.NativeStart.main(Native Method) 
 19:04:47.406 W/ActivityManager(  107):   Force finishing activity com.dashboard.tests/.activity.MainActivity
 19:04:47.402 E/AndroidRuntime( 1352): FATAL EXCEPTION: main 
 19:04:47.402 E/AndroidRuntime( 1352): java.lang.IllegalStateException: Recursive entry to executePendingTransactions 
 19:04:47.402 E/AndroidRuntime( 1352): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1388) 
 19:04:47.402 E/AndroidRuntime( 1352): at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:420)

根据Android Developer Office Hours(http://www.google.com/moderator/#15/e=1ac28e&t=1ac28e.51)的要求,以下是Android Crew可以查看的代码。

由于

Ps:很抱歉代码很长。

0 个答案:

没有答案