我的应用程序有一个带有1或2页的仪表板(根据用户配置文件)。
当应用程序第一次启动时,它只显示3个按钮,并且当信息到达时,它可以添加更多3个按钮(总共6个)。这些按钮有一个特定的顺序,应该保留:
为了产生这种效果,我有几个占位符,其中添加了片段。每当一个新按钮到达时(按钮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:很抱歉代码很长。