[출처] http://android.codeandmagic.org/2011/07/android-tabs-with-fragments/
I’ve been recently using the Android Compatibility Library in a project where I found myself in the need for a tabbed layout, in which each of the tab’s children would be a fragment. The usual way to do this is for the activity you need the tabs in to extend TabActivity. However, if you want to use fragments in your activity, you must extend android.support.v4.app.FragmentActivity instead. Since Java doesn’t allow multiple class inheritance, you find yourself in a bit of dilemma.
Well, there’s a simple solution for making this work. What you need is basically to use a TabHost to hold both the TabWidget (which are the actual tab labels) and their content. Since the TabHost is nothing more than a View, you can place that anywhere, including in the root of a fragment defined in XML, like the one below (tabs_fragment.xml). I am hoping the comments in the code are self-explanatory enough.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <? xml version = "1.0" encoding = "utf-8" ?> < TabHost android:id = "@android:id/tabhost" android:layout_width = "fill_parent" android:layout_height = "fill_parent" android:background = "#EFEFEF" > < LinearLayout android:orientation = "vertical" android:layout_width = "fill_parent" android:layout_height = "fill_parent" > < TabWidget android:id = "@android:id/tabs" android:layout_width = "fill_parent" android:layout_height = "wrap_content" /> < FrameLayout android:id = "@android:id/tabcontent" android:layout_width = "fill_parent" android:layout_height = "fill_parent" > < FrameLayout android:id = "@+id/tab_1" android:layout_width = "fill_parent" android:layout_height = "fill_parent" /> < FrameLayout android:id = "@+id/tab_2" android:layout_width = "fill_parent" android:layout_height = "fill_parent" /> </ FrameLayout > </ LinearLayout > </ TabHost > |
Don’t forget to use the correct ids (@android:id/tabhost, @android:id/tabs and@android:id/tabcontent) because the TabHost expects exactly these in order to retrieve the references for each component of the view. If you want to use a custom style for your tabs, it is easy to do so by creating a tab_selector.xml in /res/drawables in which you can use different colors or backgrounds for each tab state. You can find an example in the attached zip.
The equivalent code for actually creating the tabs is in TabsFragment.java below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | package org.codeandmagic.android; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TabHost; import android.widget.TabHost.OnTabChangeListener; import android.widget.TabHost.TabSpec; import android.widget.TextView; public class TabsFragment extends Fragment implements OnTabChangeListener { private static final String TAG = "FragmentTabs" ; public static final String TAB_WORDS = "words" ; public static final String TAB_NUMBERS = "numbers" ; private View mRoot; private TabHost mTabHost; private int mCurrentTab; @Override public void onAttach(Activity activity) { super .onAttach(activity); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mRoot = inflater.inflate(R.layout.tabs_fragment, null ); mTabHost = (TabHost) mRoot.findViewById(android.R.id.tabhost); setupTabs(); return mRoot; } @Override public void onActivityCreated(Bundle savedInstanceState) { super .onActivityCreated(savedInstanceState); setRetainInstance( true ); mTabHost.setOnTabChangedListener( this ); mTabHost.setCurrentTab(mCurrentTab); // manually start loading stuff in the first tab updateTab(TAB_WORDS, R.id.tab_1); } private void setupTabs() { mTabHost.setup(); // you must call this before adding your tabs! mTabHost.addTab(newTab(TAB_WORDS, R.string.tab_words, R.id.tab_1)); mTabHost.addTab(newTab(TAB_NUMBERS, R.string.tab_numbers, R.id.tab_2)); } private TabSpec newTab(String tag, int labelId, int tabContentId) { Log.d(TAG, "buildTab(): tag=" + tag); View indicator = LayoutInflater.from(getActivity()).inflate( R.layout.tab, (ViewGroup) mRoot.findViewById(android.R.id.tabs), false ); ((TextView) indicator.findViewById(R.id.text)).setText(labelId); TabSpec tabSpec = mTabHost.newTabSpec(tag); tabSpec.setIndicator(indicator); tabSpec.setContent(tabContentId); return tabSpec; } @Override public void onTabChanged(String tabId) { Log.d(TAG, "onTabChanged(): tabId=" + tabId); if (TAB_WORDS.equals(tabId)) { updateTab(tabId, R.id.tab_1); mCurrentTab = 0 ; return ; } if (TAB_NUMBERS.equals(tabId)) { updateTab(tabId, R.id.tab_2); mCurrentTab = 1 ; return ; } } private void updateTab(String tabId, int placeholder) { FragmentManager fm = getFragmentManager(); if (fm.findFragmentByTag(tabId) == null ) { fm.beginTransaction() .replace(placeholder, new MyListFragment(tabId), tabId) .commit(); } } } |
Now that you have the content of your fragment in place, you need to create the tab’s children (which are fragments, like the title says). You can subclass the android.support.v4.app.Fragment (I used a ListFragment in this example). You can also use loaders if your fragment needs to do some background work. In the example below, I use an AsyncTaskLoader to simulate a time-consuming operation in background, at the end of which some words or numbers are added into the list’s adapter and displayed inside a tab. You also need a LoaderCallback object (or your fragment can directly implement this interface) in order to communicate with the LoaderManager to create your loader and also know when its job is done.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | package org.codeandmagic.android; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; public class MyListFragment extends ListFragment implements LoaderCallbacks<Void> { private static final String TAG = "FragmentTabs" ; private String mTag; private MyAdapter mAdapter; private ArrayList<String> mItems; private LayoutInflater mInflater; private int mTotal; private int mPosition; private static final String[] WORDS = { "Lorem" , "ipsum" , "dolor" , "sit" , "amet" , "consectetur" , "adipiscing" , "elit" , "Fusce" , "pharetra" , "luctus" , "sodales" }; private static final String[] NUMBERS = { "I" , "II" , "III" , "IV" , "V" , "VI" , "VII" , "VIII" , "IX" , "X" , "XI" , "XII" , "XIII" , "XIV" , "XV" }; private static final int SLEEP = 1000 ; private final int wordBarColor = R.color.word_bar; private final int numberBarColor = R.color.number_bar; public MyListFragment() { } public MyListFragment(String tag) { mTag = tag; mTotal = TabsFragment.TAB_WORDS.equals(mTag) ? WORDS.length : NUMBERS.length; Log.d(TAG, "Constructor: tag=" + tag); } @Override public void onActivityCreated(Bundle savedInstanceState) { super .onActivityCreated(savedInstanceState); // this is really important in order to save the state across screen // configuration changes for example setRetainInstance( true ); mInflater = LayoutInflater.from(getActivity()); // you only need to instantiate these the first time your fragment is // created; then, the method above will do the rest if (mAdapter == null ) { mItems = new ArrayList<String>(); mAdapter = new MyAdapter(getActivity(), mItems); } getListView().setAdapter(mAdapter); // initiate the loader to do the background work getLoaderManager().initLoader( 0 , null , this ); } @Override public Loader<Void> onCreateLoader( int id, Bundle args) { AsyncTaskLoader<Void> loader = new AsyncTaskLoader<Void>(getActivity()) { @Override public Void loadInBackground() { try { // simulate some time consuming operation going on in the // background Thread.sleep(SLEEP); } catch (InterruptedException e) { } return null ; } }; // somehow the AsyncTaskLoader doesn't want to start its job without // calling this method loader.forceLoad(); return loader; } @Override public void onLoadFinished(Loader<Void> loader, Void result) { // add the new item and let the adapter know in order to refresh the // views mItems.add(TabsFragment.TAB_WORDS.equals(mTag) ? WORDS[mPosition] : NUMBERS[mPosition]); mAdapter.notifyDataSetChanged(); // advance in your list with one step mPosition++; if (mPosition < mTotal - 1 ) { getLoaderManager().restartLoader( 0 , null , this ); Log.d(TAG, "onLoadFinished(): loading next..." ); } else { Log.d(TAG, "onLoadFinished(): done loading!" ); } } @Override public void onLoaderReset(Loader<Void> loader) { } private class MyAdapter extends ArrayAdapter<String> { public MyAdapter(Context context, List<String> objects) { super (context, R.layout.list_item, R.id.text, objects); } @Override public View getView( int position, View convertView, ViewGroup parent) { View view = convertView; Wrapper wrapper; if (view == null ) { view = mInflater.inflate(R.layout.list_item, null ); wrapper = new Wrapper(view); view.setTag(wrapper); } else { wrapper = (Wrapper) view.getTag(); } wrapper.getTextView().setText(getItem(position)); wrapper.getBar().setBackgroundColor( mTag == TabsFragment.TAB_WORDS ? getResources().getColor( wordBarColor) : getResources().getColor( numberBarColor)); return view; } } // use an wrapper (or view holder) object to limit calling the // findViewById() method, which parses the entire structure of your // XML in search for the ID of your view private class Wrapper { private final View mRoot; private TextView mText; private View mBar; public Wrapper(View root) { mRoot = root; } public TextView getTextView() { if (mText == null ) { mText = (TextView) mRoot.findViewById(R.id.text); } return mText; } public View getBar() { if (mBar == null ) { mBar = mRoot.findViewById(R.id.bar); } return mBar; } } } |
Now, all you need to do is create a FragmentActivity to accommodate your tabs fragment. You can either instantiate the fragment programmatically using the methods in the FragmentManager or by putting it directly in XML, like below (main.xml).
1 2 3 4 5 6 7 8 9 10 11 12 13 | <? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout android:orientation = "vertical" android:layout_width = "fill_parent" android:layout_height = "fill_parent" > < fragment class = "org.codeandmagic.android.TabsFragment" android:id = "@+id/tabs_fragment" android:layout_width = "fill_parent" android:layout_height = "fill_parent" /> </ LinearLayout > |
With that, your job is pretty much done. You can see in the screenshot the result and also can download the project that’s available in the attached zip. Enjoy!
[출처] http://android.codeandmagic.org/2011/07/android-tabs-with-fragments/
'Language > Android' 카테고리의 다른 글
Collect preferences failed, Collect preferences failed, class java/lang/AutoCloseable not found in D:\Android\sdk\platforms\android-19\android.jar (3) | 2013.12.26 |
---|---|
모바일 웹에서 앱 호출 (0) | 2013.12.16 |
<key> is not translated in <language> (0) | 2013.07.25 |
[스크랩] 안드로이드 이클립스에서 Proguard(프로가드) 사용하기 (0) | 2013.07.25 |
ADT Bundle (0) | 2013.07.01 |