可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
For ActionBarSherlock I would like to have (Action Bar) Tabs + Pager. I use Fragments inside that pager container. I already got the examples of http://actionbarsherlock.com/ working, but I can't manage to get a details fragment inside that pager container when I would click on let's say a listitem in the first fragment.
Is it impossible to have something like this:
Activity with Tabs and pager container
- Fragment A inside pager container under Tab1
- Click on something in Fragment A and show Fragment B in same pager container under Tab1.
Fragment A is then not visible, only Fragment B is visible, but also all the Tabs.
At the moment I think only a new activity (which would hold Fragment B inside it) can be started after clicking something in Fragment A.
回答1:
Here is my solution for the (Tabs + Fragment + ViewPager) it is works for me as i wanted,
hope that works for you as well
here is the xml file
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="5" />
<FrameLayout
android:id="@+id/fragment_details"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="4.3" />
</LinearLayout>
here is the code for MainActivity.java I'll post relevant code only so you'll have to manage it
public class MainActivity extends FragmentActivity implements
DialogInterface.OnDismissListener, TabDataResponder {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
artistTab = getSupportActionBar().newTab().setText(
R.string.tab_name_artist);
albumTab = getSupportActionBar().newTab().setText(
R.string.tab_name_album);
songTab = getSupportActionBar().newTab().setText(
R.string.tab_name_songs);
map = new HashMap<String, Integer>();
mViewPager = (ViewPager) findViewById(R.id.pager);
FrameLayout deatil = (FrameLayout) findViewById(R.id.fragment_details);
mDualPane = (deatil != null) && (deatil.getVisibility() == View.VISIBLE);
mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);
if (savedInstanceState != null) {
flag = true;
index = savedInstanceState.getInt("index");
}
setUpTabView();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("index", getSupportActionBar()
.getSelectedNavigationIndex());
}
private void setUpTabView() {
mTabsAdapter.addTab(artistTab, ArtistFragment.class, null);
mTabsAdapter.addTab(albumTab, AlbumFragment.class, null);
mTabsAdapter.addTab(songTab, SongFragment.class, null);
getSupportActionBar().setSelectedNavigationItem(index);
}
public static class TabsAdapter extends FragmentPagerAdapter implements
ViewPager.OnPageChangeListener, ActionBar.TabListener {
private FragmentActivity mContext;
private ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<String> mTabs = new ArrayList<String>();
private TabDataResponder responder;
public TabsAdapter(FragmentActivity activity, ActionBar actionBar,
ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = actionBar;
mViewPager = pager;
// TabDataResponder is an interface which is implemented in MainActivity
// You can find implementation @ the last
responder = (TabDataResponder) activity;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
//I have used map to save state of the fragment
map.put(SongFragment.TYPE_FRAGMENT.trim(), 0);
map.put(AlbumFragment.TYPE_FRAGMENT.trim(), 0);
map.put(ArtistFragment.TYPE_FRAGMENT.trim(), 0);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
mTabs.add(clss.getName());
// mArgs.add(args);
mActionBar.addTab(tab.setTabListener(this));
notifyDataSetChanged();
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Fragment getItem(int position) {
return Fragment
.instantiate(mContext, mTabs.get(position), /*
* mArgs.get(
* position)
*/null);
}
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Log.i(TAG, "PageSelected....");
mActionBar.setSelectedNavigationItem(position);
}
@Override
public void onPageScrollStateChanged(int state) {
Log.i(TAG, "ScrollSateChanged....");
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
String a = null;
if (mDualPane) {
a = mTabs.get(tab.getPosition());
responder.loadData(a, map.get(a));
}
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
Log.i(TAG, "Tab is released now....");
}
}
@Override
public void onDismiss(DialogInterface dialog) {
setUpTabView();
}
//This interface must be call from fragment class
//@ the time of event you want to show detail
// pass the class name in the type argument using class.getName() method
@Override
public void loadData(String type, int index) {
DetailFragment viewer = (DetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragment_details);
if (mDualPane) {
if (viewer == null || viewer.getShownIndex() != index
|| viewer.getTypeFragment() != type) {
DetailFragment df = DetailFragment.newInstance(index, type);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_details, df)
.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
map.put(type.trim(), index);
}
} else {
Intent intent = new Intent();
intent.setClass(MainActivity.this, DetailActivity.class);
intent.putExtra("index", index);
intent.putExtra("type", type);
startActivity(intent);
}
}
}
and here is how i deal with detail fragment not very efficient but kind of working
public class DetailFragment extends Fragment{
public static DetailFragment newInstance(int index, String TYPE_FRAGMENT) {
DetailFragment f = new DetailFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
args.putString("type", TYPE_FRAGMENT);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
public String getTypeFragment(){
String a = getArguments().getString("type");
return a;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//template is blank layout
View view = inflater.inflate(R.layout.template, container, false);
if(getTypeFragment().equals(ArtistFragment.TYPE_FRAGMENT)){
view = null;
view = inflater.inflate(R.layout.artist_details, container, false);
//....
}
else if(getTypeFragment().equals(AlbumFragment.TYPE_FRAGMENT)){
//do's for album fragment
}
else if(getTypeFragment().equals(SongFragment.TYPE_FRAGMENT)){
//do's for song fragment
}
return view;
}
}
do not save the state of tab in their individual fragment it will conflict, we are already doing it here
回答2:
EDIT:
Cheered too soon. Now the details_container is not a viewpager and I cannot use it to 'swipe tabs'.
Found it! Just had to define two FrameLayouts, with in the first one the ViewPager and in the second the details fragments can be 'loaded'. This is done by adding fragments dynamically and replace them.
First the two FrameLayouts:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fadingEdge="none" >
<FrameLayout
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
<FrameLayout
android:id="@+id/details_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Then replace a fragment dynamically:
// Create new fragment and transaction
Fragment detailsFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment container view with this fragment
// and add the transaction to the back stack
transaction.replace(R.id.details_container, detailsFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Very simple and I don't understand why it took me hours to figure this out..
回答3:
I still did not find a possibility to have a Pager container where fragments should be loaded in and also keep the (ActionBar) Tabs. I have however found a really dirty solution to acomplish this, with starting intens (Main Activity with the Tabs) and finishing the previous ones when the backbutton doesn't need it anymore.
I adapted the code from ABS: Support Demos - Tabs and Pager. But again it's really dirty:
LoaderCursorSupport.CursorLoaderListFragment under Tab2
@Override public void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = new Intent();
intent.setClass(getActivity(), ActionBarTabsPager.class);
intent.putExtra("index", position);
intent.putExtra("fragment", "details");
intent.putExtra("tab", 1);
ActionBarTabsPager.mPreviousActivity = getActivity();
startActivity(intent);
ActionBarTabsPager (Main Activity with Tabs)
public class ActionBarTabsPager extends FragmentActivity {
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
static Activity mPreviousActivity;
static Activity mActivity;
static int mTabPosition = -1;
static Boolean mTabRefreshed = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.actionbar_tabs_pager);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = getSupportActionBar().newTab().setText("Tab 1");
ActionBar.Tab tab2 = getSupportActionBar().newTab().setText("Tab 2");
ActionBar.Tab tab3 = getSupportActionBar().newTab().setText("Tab 3");
ActionBar.Tab tab4 = getSupportActionBar().newTab().setText("Tab 4");
String fragment = "";
try {
Bundle bundle = this.getIntent().getExtras();
fragment = bundle.getString("fragment");
mTabPosition = bundle.getInt("tab");
} catch (Exception ex) {
}
mViewPager = (ViewPager) findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);
mTabsAdapter.addTab(tab1, FragmentStackSupport.CountingFragment.class);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) {
mTabsAdapter.addTab(tab2, FragmentStackSupport.CountingFragment.class);
mTabsAdapter.addTab(tab3, FragmentStackSupport.CountingFragment.class);
mTabsAdapter.addTab(tab4, FragmentStackSupport.CountingFragment.class);
} else {
if (!fragment.contains("details")) {
mTabsAdapter.addTab(tab2, LoaderCursorSupport.CursorLoaderListFragment.class);
} else {
mTabsAdapter.addTab(tab2, ExampleFragment.class);
}
mTabsAdapter.addTab(tab3, LoaderCustomSupport.AppListFragment.class);
mTabsAdapter.addTab(tab4, LoaderThrottleSupport.ThrottledLoaderListFragment.class);
}
if (savedInstanceState != null) {
getSupportActionBar().setSelectedNavigationItem(savedInstanceState.getInt("index"));
}
if (mTabPosition > -1) {
mTabsAdapter.setPrimaryItem(mTabPosition);
mActivity = this;
}
}
Inside this Class there's a TabsAdapter
public static class TabsAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<String> mTabs = new ArrayList<String>();
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mTabPosition > -1 && mTabRefreshed) {
int tabPosition = tab.getPosition();
if (mTabPosition != tabPosition) {
if (mPreviousActivity != null) {
mPreviousActivity.finish();
mTabRefreshed = false;
mPreviousActivity = null;
mTabPosition = -1;
Intent intent = new Intent();
intent.setClass(mContext, ActionBarTabsPager.class);
intent.putExtra("fragment", "home");
intent.putExtra("tab", tabPosition);
mActivity.startActivity(intent);
mActivity.finish();
}
}
}
mViewPager.setCurrentItem(tab.getPosition());
}
Can this be done simpler? Or should I just give up on having Tabs together with fragment history? This was done before Android 3.0 with ActivityGroups and Activities, but it seems this can't be done with fragments.
回答4:
I found the other good example of the same implementation in hear... https://github.com/UweTrottmann/SeriesGuide
In this example under package com.battlelancer.seriesguide.ui
you can find UpcomingRecentActivity.java, and UpcomingFragment.java
and layout upcoming_multipan.xml
this example works for me...
I got one problem while adding different content for detail-fragment the different tabs, it gives me class-cast-exception
so i implemented a common detalFragment class and created separate layout in onCreateView method
but the only one problem i found is layout is not changing on tab switched, may be need to do it by implementing some listener
I'll tell you when i found the answer.