顶级,我正在尝试实现两个选项卡,将单个片段与每个选项卡关联,并在相应选择每个选项卡时显示片段.
目前,我有两个问题(但我相信它们是相关的……)
1)每个标签的片段彼此重叠.这可能与不正确的片段附加/分离有关.
2)在某处创建了第三个神秘碎片,并与其他碎片重叠
在模拟器(和物理设备)上,您可以看到在选择任一选项卡时有两个片段重叠
当选择tab1时,片段1和未知片段重叠.
当选择tab2时,片段1和片段2重叠.
(tab1重叠)http://s8.postimg.org/kv81yz745/tab1_overlapping.png
(tab2重叠)http://s8.postimg.org/3tf7wvs91/tab2_overlapping.png
在这里,为了演示/清晰的目的,我将每个片段中的文本分开.
这些屏幕截图的链接在我的评论/回复中. (上传超过2个链接的声誉不够……)
活动布局(fragment_tabs.xml)
<TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TabWidget android:id="@android:id/tabs" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0"/> <FrameLayout android:id="@+id/realtabcontent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout> </TabHost>
源代码
public class TabbedInfoHome extends SherlockFragmentActivity { TabHost mTabHost; TabManager mTabManager; static String tag1name = "simple1"; static String tag2name = "simple2"; static String tab1string = "You are looking at fragment 1"; static String tab2string = "You are looking at fragment 2"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); if (savedInstanceState == null) { // Do first time initialization -- add initial fragment. Fragment frag1 = CountingFragment.newInstance(tab1string); Fragment frag2 = CountingFragment.newInstance(tab2string); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(R.id.realtabcontent,frag1,tag1name); ft.add(R.id.realtabcontent,frag2,tag2name); ft.commit(); } else { mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); } mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); mTabManager = new TabManager(this,mTabHost,R.id.realtabcontent); mTabManager.addTab(mTabHost.newTabSpec(tag1name) .setIndicator(tag1name),TabbedInfoHome.CountingFragment.class,null); mTabManager.addTab(mTabHost.newTabSpec(tag2name) .setIndicator(tag2name),null); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("tab",mTabHost.getCurrentTabTag()); } public static class CountingFragment extends SherlockFragment { String displayString; String FRAGMENT_TAG = this.getClass().getSimpleName(); /** * Create a new instance of CountingFragment,providing "num" * as an argument. */ static CountingFragment newInstance(String toDisplay) { CountingFragment f = new CountingFragment(); Bundle args = new Bundle(); args.putString("string",toDisplay); f.setArguments(args); return f; } /* When creating,retrieve this instance's number from its arguments. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); displayString = getArguments() != null ? getArguments().getString("string") : "no string was passed in!"; } /* The Fragment's UI is just a simple text view showing its * instance number. */ @Override public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) { View v = inflater.inflate(R.layout.hello_world,container,false); View tv = v.findViewById(R.id.text); boolean separateStrings = false; /* the overlapping is hard to decipher,so * lets illustrate how both fragments are appearing */ if(separateStrings) { String temp; /* b/c I only created TWO instances of the CountingFragments object,* there should only be TWO "displayStrings" to consider... */ if( (displayString.compareTo(tab1string) == 0) ) { /* user clicked tab 1 */ temp = "\n\n\n\n" + displayString; } else if( (displayString.compareTo(tab2string) == 0) ) { /* user clicked tab2 */ temp = "\n\n\n\n\n\n\n" + displayString; } else { /* unknown CountingFragment instance */ temp = "What am I doing here..??? "; } ((TextView)tv).setText(temp); } else { /* normal case of the fragment being shown; (but they overlap!) */ ((TextView)tv).setText(displayString); } return v; } } /** * This is a helper class that implements a generic mechanism for * associating fragments with the tabs in a tab host. It relies on a * trick. Normally a tab host has a simple API for supplying a View or * Intent that each tab will show. This is not sufficient for switching * between fragments. So instead we make the content part of the tab host * 0dp high (it is not shown) and the TabManager supplies its own dummy * view to show as the tab content. It listens to changes in tabs,and takes * care of switch to the correct fragment shown in a separate content area * whenever the selected tab changes. */ public static class TabManager implements TabHost.OnTabChangeListener { private final FragmentActivity mActivity; private final TabHost mTabHost; private final int mContainerId; private final HashMap<String,TabInfo> mTabs = new HashMap<String,TabInfo>(); TabInfo mLastTab; static final class TabInfo { private final String tag; private final Class<?> clss; private final Bundle args; private Fragment fragment; TabInfo(String _tag,Class<?> _class,Bundle _args) { tag = _tag; clss = _class; args = _args; } } static class DummyTabFactory implements TabHost.TabContentFactory { private final Context mContext; public DummyTabFactory(Context context) { mContext = context; } @Override public View createTabContent(String tag) { View v = new View(mContext); v.setMinimumWidth(0); v.setMinimumHeight(0); return v; } } public TabManager(FragmentActivity activity,TabHost tabHost,int containerId) { mActivity = activity; mTabHost = tabHost; mContainerId = containerId; mTabHost.setOnTabChangedListener(this); } public void addTab(TabHost.TabSpec tabSpec,Class<?> clss,Bundle args) { tabSpec.setContent(new DummyTabFactory(mActivity)); String tag = tabSpec.getTag(); TabInfo info = new TabInfo(tag,clss,args); // Check to see if we already have a fragment for this tab,probably // from a prevIoUsly saved state. If so,deactivate it,because our // initial state is that a tab isn't shown. info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag); if (info.fragment != null ) { // && !info.fragment.isDetached()) { Log.d("addingTab","we already have a fragment for this tab. tabInfo.fragment.id: " + info.fragment.getId()); FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); ft.detach(info.fragment); ft.commit(); mActivity.getSupportFragmentManager().executePendingTransactions(); } // associate the tabSpec tag with a particular TabInfo object mTabs.put(tag,info); mTabHost.addTab(tabSpec); } @Override public void onTabChanged(String tabId) { TabInfo newTab = mTabs.get(tabId); if (mLastTab != newTab) { FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); if (mLastTab != null) { if (mLastTab.fragment != null) { ft.detach(mLastTab.fragment); } } if (newTab != null) { if (newTab.fragment == null) { newTab.fragment = Fragment.instantiate(mActivity,newTab.clss.getName(),newTab.args); ft.add(mContainerId,newTab.fragment,newTab.tag); } else { ft.attach(newTab.fragment); } } mLastTab = newTab; ft.commit(); mActivity.getSupportFragmentManager().executePendingTransactions(); } } }
解决方法
在我的TabbedInfoHome类的onCreate方法中,当第一次创建片段的新实例(savedInstanceState == null)时,我使用this.getSupportFragmentManager()强制执行FragmentTransaction中的挂起事务.executePendingTransactions()
这个解释是基于http://developer.android.com/reference/android/app/FragmentManager.html#executePendingTransactions()的文档
After a
FragmentTransaction
is committed withFragmentTransaction.commit()
,it is scheduled to be executed asynchronously > on the process’s main thread. If you want to immediately executing any such pending operations,you can call this function.
我仍然有一个悬而未决的问题是,缺少即时待决交易的执行情况如何体现在原始问题中看到的行为中.
换句话说…… this.getSupportFragmentManager().executePendingTransactions()的LACK如何解释重叠的片段?
编辑的代码如下;一行补充
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); if (savedInstanceState == null) { // Do first time initialization -- add initial fragment. Fragment frag1 = CountingFragment.newInstance(tab1string); Fragment frag2 = CountingFragment.newInstance(tab2string); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(R.id.realtabcontent,tag1name); ft.add(R.id.realtabcontent,tag2name); ft.commit(); this.getSupportFragmentManager().executePendingTransactions(); // <----- This is the key } else { mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); } mTabHost = (TabHost)findViewById(android.R.id.tabhost); mTabHost.setup(); mTabManager = new TabManager(this,R.id.realtabcontent); mTabManager.addTab(mTabHost.newTabSpec(tag1name) .setIndicator(tag1name),null); mTabManager.addTab(mTabHost.newTabSpec(tag2name) .setIndicator(tag2name),null); }