(ActionBar) Tabs + Pager + detail Fragments inside

2020-02-16 20:11发布

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.

4条回答
我命由我不由天
2楼-- · 2020-02-16 21:00

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楼-- · 2020-02-16 21:04

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.

查看更多
太酷不给撩
4楼-- · 2020-02-16 21:08

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

查看更多
欢心
5楼-- · 2020-02-16 21:12

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.

查看更多
登录 后发表回答