目前android流行的包含多个功能的网络应用的布局中,有一个类似于windws属性页的布局得到广泛应用,其效果大致类似于多窗口浏览器的标签页,多个activity通过不同的标签页在同一屏幕中显示和切换。这一效果大致是通过Tabhost和一些其他控件结合实现,当然也可以不通过Tabhost,使用ViewPager加一些button也可以实现大致的效果。
1.使用TabHost+ActivityGroup实现,ActivityGroup已经不再使用,这里使用别人写好的例子。参考:http://wazai.net/2114
承上篇文章Android TabHost Without TabActivity,在MainActivity中的TabHost加入頁籤。
1 2 3 | mHost.addTab(mHost.newTabSpec(getString(R.string.tab_two_name)) .setIndicator(getString(R.string.tab_two_name)) .setContent(new Intent(this, ActivityGroup1.class))); |
小蛙在測試了上面兩篇文章的方法後發現,必須要在ActivityGroup1中直接先載入Activity1(也就是ActivityGroup僅用來"操作"這些Activity,並沒有實質的內容呈現,如果讓ActivityGroup在這邊有自己的內容呈現,會造成Back Stack運作錯誤,也有可能是小蛙實作上出了問題,如果照著上面兩篇文章試不出來的網友,不妨參照小蛙的做法),以下是ActivityGroup1的程式碼:
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 | public class ActivityGroup1 extends ActivityGroup{ /** 設定成 static 讓其他的子 Activity 可以存取 */ public static ActivityGroup1 group; /** Back Stack */ private ArrayList<View> history; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.history = new ArrayList<View>(); group = this; // ActivityGroup1 只是一個外框,在這個外框中載入其他要用的 Activity // 如果沒有這個外框會發生錯誤 View view = getLocalActivityManager() .startActivity("Activity1", new Intent(ActivityGroup1.this, Activity1.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)) .getDecorView(); // 馬上載入真正要執行的 Activity replaceView(view); } /** * 在 ActivityGroup 中切換 Activity * @param v */ public void replaceView(View v) { // 可在這插入換頁動畫 history.add(v); setContentView(v); } /** * 當使用者按下 back 的時候,把之前存起來的 stack 撈回來顯示 */ public void back() { // 原本的範例是寫 > 0,但會發生錯誤 if(history.size() > 1) { history.remove(history.size()-1); View v = history.get(history.size()-1); // 可在這插入換頁動畫 setContentView(v); }else { // back stack 沒有其他頁面可顯示,直接結束 finish(); } } /** * 複寫聆聽按下 back 事件,呼叫 back() */ // 由於我的股票精算師是使用1.6+,因此採用onKeyDown來監聽 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: back(); break; } return true; }} |
至於在Activity1中的設定如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Activity1 extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity1); Button button = (Button) findViewById(R.id.button1); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 如果沒有這個外框會發生錯誤 View view = ActivityGroup1.group.getLocalActivityManager().startActivity("Activity2", new Intent(Activity1.this, Activity2.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView(); // 載入真正要執行的 Activity ActivityGroup1.group.replaceView(view); } });} |
基本上到這邊就已經可以正常運作囉!有什麼問題可以留言在這邊,小蛙會盡可能的協助解決。
2.使用LocalActivityManager+TabHost,LocalActivityManager也是一个新版本已经不再使用,可见这一经典布局渊源流长,这里借用别人的代码说明,摘自:http://www.cnblogs.com/mybkn/articles/2507857.html
2012-05-07 更新)接續Android TabHost中切換Activity記錄了使用ActivityGroup達到在TabHost中切換Activity的方法,也在Android Screen Orientation Event螢幕方向處理+Acitivity Liftcycle記錄了當螢幕方向改變時的處理,這篇小蛙繼續記錄用FragmentActivity取代ActivityGroup,透過FragmentActivity內建的BackStack來管理倒退歷程。
MainTabActivity.java : 主要的Tabhost Activity。
public class MainTabActivity extends Activity { private TabHost mHost; // 在Activity中使用Tabhost必須要有LocalActivityManager物件來設定 LocalActivityManager lam; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // main layout採用預設的Tabhost mHost = (TabHost) findViewById(android.R.id.tabhost); lam = new LocalActivityManager(MainTabActivity.this, false); lam.dispatchCreate(savedInstanceState); mHost.setup(lam); mHost.addTab(mHost.newTabSpec("Tab1").setIndicator("Tab1").setContent(new Intent(MainTabActivity.this, FragmentActivity1.class))); mHost.addTab(mHost.newTabSpec("Tab2").setIndicator("Tab2").setContent(new Intent(MainTabActivity.this, FragmentActivity2.class))); } @Override protected void onPause() { // 漏掉這行一定出錯 lam.dispatchPause(isFinishing()); super.onPause(); } @Override protected void onResume() { // 漏掉這行一定出錯 lam.dispatchResume(); super.onResume(); } }
FragmentActivity1.java : 第一個Tab中用來管理Fragment的FragmentActivity。(2012-05-07更新)經由Jay留言後,小蛙詳細測了一下,發現FragmentActivity主畫面中的Button是沒辦法消失的(因為FragmentActivity的目的關係),因此改成這樣,讓FragmentActivity純粹當成容器,主要的內容還是以Fragment為主。(這個方法不是唯一,但是一個可行的方法,應該也有更好的做法!)
public class FragmentActivity1 extends FragmentActivity { public static FragmentManager fm; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_activity_1); fm = getSupportFragmentManager(); // 只當容器,主要內容已Fragment呈現 initFragment(new Fragment1()); } // 切換Fragment public static void changeFragment(Fragment f){ changeFragment(f, false); } // 初始化Fragment(FragmentActivity中呼叫) public static void initFragment(Fragment f){ changeFragment(f, true); } private static void changeFragment(Fragment f, boolean init){ FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.simple_fragment, f); if(!init) ft.addToBackStack(null); ft.commit(); } }
Fragment1.java : 真正使用到的Fragment。
public class Fragment1 extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_1, container, false); Button tv = (Button)v.findViewById(R.id.button2); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 直接呼叫FragmentActivity1的靜態方法來做切換 FragmentActivity1.changeFragment(new Fragment2()); } }); return v; } }
fragment_acitivity_1.xml : FragmentActivity layout。(2012-05-07修改) FragmentActivity只用來當容器,而不真正呈現內容(僅把Fragment內容載入)。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:id="@+id/simple_fragment"> </LinearLayout>
fragment1.xml:Fragment layout,FragmentActivity載入的真正內容。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout>
最後別忘了在AndroidManifest.xml中加入android:configChanges="orientation"設定。這樣就成功的使用在Activity中使用Tabhost並且透過FragmentActivity來管理Fragment囉!
3.TabHost+TabActivity实现,同样在新版本中不再支持。自己写了个实例研究。
public class addall extends TabActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_tool);
Resources res = getResources(); // Resource object to get Drawables
TabHost tabHost = getTabHost(); // The activity TabHost
TabHost.TabSpec spec; // Resusable TabSpec for each tab
Intent intent; // Reusable Intent for each tab
// Create an Intent to launch an Activity for the tab (to be reused)
intent = new Intent().setClass(this, AppAppActivity.class);
// Initialize a TabSpec for each tab and add it to the TabHost
View good = (View) LayoutInflater.from(this).inflate(R.layout.tabmini, null);
TextView text1 = (TextView) good.findViewById(R.id.tab_label);
text1.setText(R.string.total);
spec = tabHost.newTabSpec("good");
//spec.setIndicator(null,res.getDrawable(R.drawable.ic_tab_artists));
spec.setIndicator(good);
spec.setContent(intent);
tabHost.addTab(spec);
// Do the same for the other tabs
intent = new Intent().setClass(this, AppToolActivity.class);
View tool = (View) LayoutInflater.from(this).inflate(R.layout.tabmini, null);
TextView text2 = (TextView) tool.findViewById(R.id.tab_label);
text2.setText(R.string.tools);
spec = tabHost.newTabSpec("tools");
//spec.setIndicator(null,res.getDrawable(R.drawable.ic_tab_artists));
spec.setIndicator(tool);
spec.setContent(intent);
tabHost.addTab(spec);
tabHost.setCurrentTab(0);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="5dp" >
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true"
android:divider="@null"
android:dividerPadding="0dp"
android:tabStripEnabled="false"
android:showDividers="none|none"
/>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="0dp"
/>
</LinearLayout>
</TabHost><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:background="@drawable/topbar_red"
android:gravity="bottom"
android:orientation="vertical"
android:paddingLeft="5dip"
android:paddingRight="5dip" >
<TextView
android:id="@+id/tab_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:background="#00000000"
android:gravity="center_horizontal|top"
android:textSize="@dimen/Medium_text_size"
android:textColor="#ffffffff"
android:textStyle="bold" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:src="@drawable/tab_arrow" />
</LinearLayout>4。TabHost+FragmentActivity,这个是目前官方建议的方式
public class MainActivity extends FragmentActivity {
TabHost tHost;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tHost = (TabHost) findViewById(android.R.id.tabhost);
tHost.setup();
/** Setting tabchangelistener for the tab */
tHost.setOnTabChangedListener(tabChangeListener);
/** Defining tab builder for Andriod tab */
TabHost.TabSpec tSpecAndroid = tHost.newTabSpec("Autognosis");
tSpecAndroid.setIndicator("快速自诊",
getResources().getDrawable(R.drawable.android));
tSpecAndroid.setContent(new DummyTabContent(getBaseContext()));
tHost.addTab(tSpecAndroid);
/** Defining tab builder for Apple tab */
TabHost.TabSpec tSpecApple = tHost.newTabSpec("Doctor");
tSpecApple.setIndicator("求医问药",
getResources().getDrawable(R.drawable.apple));
tSpecApple.setContent(new DummyTabContent(getBaseContext()));
tHost.addTab(tSpecApple);
TabHost.TabSpec tSpecAndroid1 = tHost.newTabSpec("News");
tSpecAndroid1.setIndicator("论坛新闻",
getResources().getDrawable(R.drawable.android));
tSpecAndroid1.setContent(new DummyTabContent(getBaseContext()));
tHost.addTab(tSpecAndroid1);
TabHost.TabSpec tSpecApple1 = tHost.newTabSpec("Center");
tSpecApple1.setIndicator("我的中心",
getResources().getDrawable(R.drawable.apple));
tSpecApple1.setContent(new DummyTabContent(getBaseContext()));
tHost.addTab(tSpecApple1);
}
@Override
public void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();
}
/**
* Defining Tab Change Listener event. This is invoked when tab is
* changed
*/
private TabHost.OnTabChangeListener tabChangeListener = new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
AutognosisFragment autognosisFragment = (AutognosisFragment) fm
.findFragmentByTag("Autognosis");
DoctorFragment doctorFragment = (DoctorFragment) fm
.findFragmentByTag("Doctor");
NewsFragment newsFragment = (NewsFragment) fm
.findFragmentByTag("News");
CenterFragment centerFragment = (CenterFragment) fm
.findFragmentByTag("Center");
FragmentTransaction ft = fm.beginTransaction();
/** Detaches the applefragment if exists */
if (autognosisFragment != null)
ft.detach(autognosisFragment);
/** Detaches the androidfragment if exists */
if (doctorFragment != null)
ft.detach(doctorFragment);
/** Detaches the applefragment if exists */
if (newsFragment != null)
ft.detach(newsFragment);
/** Detaches the applefragment if exists */
if (centerFragment != null)
ft.detach(centerFragment);
/** If current tab is android */
if (tabId.equalsIgnoreCase("Autognosis")) {
if (autognosisFragment == null) {
/**
* Create AndroidFragment and adding to fragmenttransaction
*/
ft.add(R.id.realtabcontent, new AutognosisFragment(),
"Autognosis");
} else {
/**
* Bring to the front, if already exists in the
* fragmenttransaction
*/
ft.attach(autognosisFragment);
}
} else if (tabId.equalsIgnoreCase("Doctor")) {
if (doctorFragment == null) {
/**
* Create AndroidFragment and adding to fragmenttransaction
*/
ft.add(R.id.realtabcontent, new DoctorFragment(),
"Doctor");
} else {
/**
* Bring to the front, if already exists in the
* fragmenttransaction
*/
ft.attach(doctorFragment);
}
}
else if (tabId.equalsIgnoreCase("News")) {
if (newsFragment == null) {
/**
* Create AndroidFragment and adding to fragmenttransaction
*/
ft.add(R.id.realtabcontent, new NewsFragment(),
"News");
} else {
/**
* Bring to the front, if already exists in the
* fragmenttransaction
*/
ft.attach(newsFragment);
}
}else if (tabId.equalsIgnoreCase("Center")) {
if (centerFragment == null) {
/**
* Create AndroidFragment and adding to fragmenttransaction
*/
ft.add(R.id.realtabcontent, new CenterFragment(),
"Center");
} else {
/**
* Bring to the front, if already exists in the
* fragmenttransaction
*/
ft.attach(centerFragment);
}
}
else {
}
ft.commit();
}
};
//@SuppressLint("ShowToast")
public void BtnOnClick(View view) {
// TODO Auto-generated method stub
switch (view.getId()) {
case R.id.button1:
case R.id.button2:
default:
Toast.makeText(MainActivity.this, "等待网络数据", Toast.LENGTH_LONG);
break;
}
}public class AutognosisFragment extends Fragment {
private static final String TAG = "Activity";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.autognosis, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public interface onToast{
public void onToast();
}
@SuppressLint("ShowToast")
public void BtnOnClick(View view) {
// TODO Auto-generated method stub
onToast ot;
switch (view.getId()) {
case R.id.button1:
case R.id.button2:
default:
Toast.makeText(getActivity(), "等待网络数据", Toast.LENGTH_LONG);
break;
}
}
}
public class DummyTabContent implements TabContentFactory{
private Context mContext;
public DummyTabContent(Context context){
mContext = context;
}
@Override
public View createTabContent(String tag) {
View v = new View(mContext);
return v;
}
public void BtnOnClick(View view) {
// TODO Auto-generated method stub
switch (view.getId()) {
case R.id.button1:
case R.id.button2:
default:
Toast.makeText(mContext, "等待网络数据", Toast.LENGTH_LONG);
break;
}
}
}
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<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" />
<TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0" />
</LinearLayout>
</TabHost><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="快速自诊" />
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BtnOnClick"
android:text="小儿感冒" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BtnOnClick"
android:text="颈椎不适" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BtnOnClick"
android:text="失眠" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BtnOnClick"
android:text="便秘" />
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="BtnOnClick"
android:text="效果评价" />
</LinearLayout>开始实现
创建FragmentActivity,总的代码如下:
setupTab*展示了如何获取ActionBar,如何创建和添加Tab组件到ActionBar中。
注意setTabListener这个接口,我们是必须设的,否则要报错,在看一下TabListener的实现,我们只在onTabSelected中写了一句代码,就是要求ViewPager显示出对应位置的Fragment。
oncreate 中的NAVIGATION_MODE_TABS定义了ActionBar的模式,后面两名是隐藏Tilte bar。
ViewPager是一个容器,用来放Fragment,我们需要在FragmentActivity中添加它,我加在layout文件中的:
TestViewPager是ViewPager的一个子类,实现代码如下:
再看一下getFragmentManager()和getSupportFragmentManager()这两个接口,它们的区别是,前者调用Android框架的FragmentManger,后者是获取Android扩展库中的V4的Support FragmentManager,着为独立的APP只能用后者,原因android.jar包中没有前者的实现。
我们再看看TestViewPagerAdapter的代码:
其实这个类就是关联ViewPager和Fragment,注意这些Index。
可能有的同学还是想看一下Fragment的实现:
android的界面技术还是很丰富的,一个重要的证明就是同一显示效果如果你愿意的话,常常能找到几十种实现的方法,即使是相同的类,相同的逻辑算法,不同的人使用,有时也会出现不同的实现方法。研究这些不同的实现方法,有时可以提升我们界面设计的素养,毕竟从可用,健壮,维护,优美的角度来看,同时考虑到andorid屏幕众多,版本升级频繁,硬件配置的差异,选择最好的界面实现方法也不算是孔已已!
大家好,今天我为大家分享TabHost中怎样修改TabWidget样式。在很多界面美观的应用中很多都用到了TabHost,但他们要比系统默认的要漂亮得多。先看几张图:

京东商城底部菜单栏

新浪微博底部菜单栏
好了,看到这些漂亮的菜单栏是不是很惊讶,你可能会说用Button就可以实现啊 ,可是用Button的话控制显示的内容很麻烦,不如用TabHost控制效率更高。很想知道用TabHost是怎么实现的吧,下面就来研究如何实现这种漂亮的TabHost。先看一下效果图:

界面比较简单,要想做得漂亮换几张图片就可以了。
第一步:先在布局(这里用了main.xml创建时自动生成的)里面放上TabHost ,只要将TabHost控件托至屏幕中就可:
这里我们已经把LinearLayout和TextView去掉了,并将“xmlns:android="……" ”添加大TabHost里了,这里要注意我们将TabHost的id定义为自己定义的id比不用android规定的id="@android:id/tabhost"。
第二步:创建显示此TabWidget的布局tabmini.xml:
第三步:创建一个selector在drawable里面 命名tabmini.xml,用来点击TabHost的一个tab时TextView的变化:
第四步:在Activity里实现TabHost:
这里我们用findViewById创建了TabHost,这样的话我们就需要在添加tab时调用TabHost的setup()方法;这里我们添加内容时添加的是布局,我们完全可以换成自己创建的Activity。
好了,让我们来看看运行效果吧:

好了,我们自定义的TabHost算是结束了。不过看到Activity里的代码很多都是重复的我们可以这样把他们简化:
android的Tabhost使用,布布扣,bubuko.com
原文:http://blog.csdn.net/cnbloger/article/details/21383139