版权声明:本文为HaiyuKing原创文章,转载请注明出处!

前言

使用FragmentTabHost实现顶部选项卡(带下划线效果)展现。

效果图

代码分析

1、该Demo中采用的是FragmentTabHost的布局方案之一【命名为常规布局写法】;

2、使用自定义的FragmentTabHost;

3、切换回来后,可以保持打开的网页,而不是显示首页。【因为切换时执行的是show/hide,而不是attach/detach】

使用步骤

一、项目组织结构图

注意事项:

1、  导入类文件后需要change包名以及重新import R文件路径

2、  Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖

二、导入步骤

将自定义的MyFragmentTabHost复制到项目中

  1. package com.why.project.fragmenttabhostunderlinedemo.views.tab;
  2.  
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.os.Bundle;
  6. import android.os.Parcel;
  7. import android.os.Parcelable;
  8. import android.support.annotation.NonNull;
  9. import android.support.annotation.Nullable;
  10. import android.support.v4.app.Fragment;
  11. import android.support.v4.app.FragmentManager;
  12. import android.support.v4.app.FragmentTransaction;
  13. import android.util.AttributeSet;
  14. import android.view.View;
  15. import android.view.ViewGroup;
  16. import android.widget.FrameLayout;
  17. import android.widget.LinearLayout;
  18. import android.widget.TabHost;
  19. import android.widget.TabWidget;
  20.  
  21. import java.util.ArrayList;
  22.  
  23. /**
  24. * Created by HaiyuKing
  25. * Used 仿照FragmentTabHost并更改doTabChanged方法实现切换Fragment的时候不刷新fragment
  26. */
  27.  
  28. public class MyFragmentTabHost extends TabHost
  29. implements TabHost.OnTabChangeListener {
  30. private final ArrayList<MyFragmentTabHost.TabInfo> mTabs = new ArrayList<>();
  31.  
  32. private FrameLayout mRealTabContent;
  33. private Context mContext;
  34. private FragmentManager mFragmentManager;
  35. private int mContainerId;
  36. private TabHost.OnTabChangeListener mOnTabChangeListener;
  37. private MyFragmentTabHost.TabInfo mLastTab;
  38. private boolean mAttached;
  39.  
  40. static final class TabInfo {
  41. final
  42. @NonNull
  43. String tag;
  44. final
  45. @NonNull
  46. Class<?> clss;
  47. final
  48. @Nullable
  49. Bundle args;
  50. Fragment fragment;
  51.  
  52. TabInfo(@NonNull String _tag, @NonNull Class<?> _class, @Nullable Bundle _args) {
  53. tag = _tag;
  54. clss = _class;
  55. args = _args;
  56. }
  57. }
  58.  
  59. static class DummyTabFactory implements TabHost.TabContentFactory {
  60. private final Context mContext;
  61.  
  62. public DummyTabFactory(Context context) {
  63. mContext = context;
  64. }
  65.  
  66. @Override
  67. public View createTabContent(String tag) {
  68. View v = new View(mContext);
  69. v.setMinimumWidth(0);
  70. v.setMinimumHeight(0);
  71. return v;
  72. }
  73. }
  74.  
  75. static class SavedState extends BaseSavedState {
  76. String curTab;
  77.  
  78. SavedState(Parcelable superState) {
  79. super(superState);
  80. }
  81.  
  82. SavedState(Parcel in) {
  83. super(in);
  84. curTab = in.readString();
  85. }
  86.  
  87. @Override
  88. public void writeToParcel(Parcel out, int flags) {
  89. super.writeToParcel(out, flags);
  90. out.writeString(curTab);
  91. }
  92.  
  93. @Override
  94. public String toString() {
  95. return "MyFragmentTabHost.SavedState{"
  96. + Integer.toHexString(System.identityHashCode(this))
  97. + " curTab=" + curTab + "}";
  98. }
  99.  
  100. public static final Parcelable.Creator<MyFragmentTabHost.SavedState> CREATOR
  101. = new Parcelable.Creator<MyFragmentTabHost.SavedState>() {
  102. @Override
  103. public MyFragmentTabHost.SavedState createFromParcel(Parcel in) {
  104. return new MyFragmentTabHost.SavedState(in);
  105. }
  106.  
  107. @Override
  108. public MyFragmentTabHost.SavedState[] newArray(int size) {
  109. return new MyFragmentTabHost.SavedState[size];
  110. }
  111. };
  112. }
  113.  
  114. public MyFragmentTabHost(Context context) {
  115. // Note that we call through to the version that takes an AttributeSet,
  116. // because the simple Context construct can result in a broken object!
  117. super(context, null);
  118. initFragmentTabHost(context, null);
  119. }
  120.  
  121. public MyFragmentTabHost(Context context, AttributeSet attrs) {
  122. super(context, attrs);
  123. initFragmentTabHost(context, attrs);
  124. }
  125.  
  126. private void initFragmentTabHost(Context context, AttributeSet attrs) {
  127. final TypedArray a = context.obtainStyledAttributes(attrs,
  128. new int[]{android.R.attr.inflatedId}, 0, 0);
  129. mContainerId = a.getResourceId(0, 0);
  130. a.recycle();
  131.  
  132. super.setOnTabChangedListener(this);
  133. }
  134.  
  135. private void ensureHierarchy(Context context) {
  136. // If owner hasn't made its own view hierarchy, then as a convenience
  137. // we will construct a standard one here.
  138. if (findViewById(android.R.id.tabs) == null) {
  139. LinearLayout ll = new LinearLayout(context);
  140. ll.setOrientation(LinearLayout.VERTICAL);
  141. addView(ll, new FrameLayout.LayoutParams(
  142. ViewGroup.LayoutParams.MATCH_PARENT,
  143. ViewGroup.LayoutParams.MATCH_PARENT));
  144.  
  145. TabWidget tw = new TabWidget(context);
  146. tw.setId(android.R.id.tabs);
  147. tw.setOrientation(TabWidget.HORIZONTAL);
  148. ll.addView(tw, new LinearLayout.LayoutParams(
  149. ViewGroup.LayoutParams.MATCH_PARENT,
  150. ViewGroup.LayoutParams.WRAP_CONTENT, 0));
  151.  
  152. FrameLayout fl = new FrameLayout(context);
  153. fl.setId(android.R.id.tabcontent);
  154. ll.addView(fl, new LinearLayout.LayoutParams(0, 0, 0));
  155.  
  156. mRealTabContent = fl = new FrameLayout(context);
  157. mRealTabContent.setId(mContainerId);
  158. ll.addView(fl, new LinearLayout.LayoutParams(
  159. LinearLayout.LayoutParams.MATCH_PARENT, 0, 1));
  160. }
  161. }
  162.  
  163. /**
  164. * @deprecated Don't call the original TabHost setup, you must instead
  165. * call {@link #setup(Context, FragmentManager)} or
  166. * {@link #setup(Context, FragmentManager, int)}.
  167. */
  168. @Override
  169. @Deprecated
  170. public void setup() {
  171. throw new IllegalStateException(
  172. "Must call setup() that takes a Context and FragmentManager");
  173. }
  174.  
  175. public void setup(Context context, FragmentManager manager) {
  176. ensureHierarchy(context); // Ensure views required by super.setup()
  177. super.setup();
  178. mContext = context;
  179. mFragmentManager = manager;
  180. ensureContent();
  181. }
  182.  
  183. public void setup(Context context, FragmentManager manager, int containerId) {
  184. ensureHierarchy(context); // Ensure views required by super.setup()
  185. super.setup();
  186. mContext = context;
  187. mFragmentManager = manager;
  188. mContainerId = containerId;
  189. ensureContent();
  190. mRealTabContent.setId(containerId);
  191.  
  192. // We must have an ID to be able to save/restore our state. If
  193. // the owner hasn't set one at this point, we will set it ourselves.
  194. if (getId() == View.NO_ID) {
  195. setId(android.R.id.tabhost);
  196. }
  197. }
  198.  
  199. private void ensureContent() {
  200. if (mRealTabContent == null) {
  201. mRealTabContent = (FrameLayout) findViewById(mContainerId);
  202. if (mRealTabContent == null) {
  203. throw new IllegalStateException(
  204. "No tab content FrameLayout found for id " + mContainerId);
  205. }
  206. }
  207. }
  208.  
  209. @Override
  210. public void setOnTabChangedListener(OnTabChangeListener l) {
  211. mOnTabChangeListener = l;
  212. }
  213.  
  214. public void addTab(@NonNull TabHost.TabSpec tabSpec, @NonNull Class<?> clss,
  215. @Nullable Bundle args) {
  216. tabSpec.setContent(new MyFragmentTabHost.DummyTabFactory(mContext));
  217.  
  218. final String tag = tabSpec.getTag();
  219. final MyFragmentTabHost.TabInfo info = new MyFragmentTabHost.TabInfo(tag, clss, args);
  220.  
  221. if (mAttached) {
  222. // If we are already attached to the window, then check to make
  223. // sure this tab's fragment is inactive if it exists. This shouldn't
  224. // normally happen.
  225. info.fragment = mFragmentManager.findFragmentByTag(tag);
  226. if (info.fragment != null && !info.fragment.isDetached()) {
  227. final FragmentTransaction ft = mFragmentManager.beginTransaction();
  228. ft.detach(info.fragment);
  229. ft.commit();
  230. }
  231. }
  232.  
  233. mTabs.add(info);
  234. addTab(tabSpec);
  235. }
  236.  
  237. @Override
  238. protected void onAttachedToWindow() {
  239. super.onAttachedToWindow();
  240.  
  241. final String currentTag = getCurrentTabTag();
  242.  
  243. // Go through all tabs and make sure their fragments match
  244. // the correct state.
  245. FragmentTransaction ft = null;
  246. for (int i = 0, count = mTabs.size(); i < count; i++) {
  247. final MyFragmentTabHost.TabInfo tab = mTabs.get(i);
  248. tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);
  249. if (tab.fragment != null && !tab.fragment.isDetached()) {
  250. if (tab.tag.equals(currentTag)) {
  251. // The fragment for this tab is already there and
  252. // active, and it is what we really want to have
  253. // as the current tab. Nothing to do.
  254. mLastTab = tab;
  255. } else {
  256. // This fragment was restored in the active state,
  257. // but is not the current tab. Deactivate it.
  258. if (ft == null) {
  259. ft = mFragmentManager.beginTransaction();
  260. }
  261. ft.detach(tab.fragment);
  262. }
  263. }
  264. }
  265.  
  266. // We are now ready to go. Make sure we are switched to the
  267. // correct tab.
  268. mAttached = true;
  269. ft = doTabChanged(currentTag, ft);
  270. if (ft != null) {
  271. ft.commit();
  272. mFragmentManager.executePendingTransactions();
  273. }
  274. }
  275.  
  276. @Override
  277. protected void onDetachedFromWindow() {
  278. super.onDetachedFromWindow();
  279. mAttached = false;
  280. }
  281.  
  282. @Override
  283. protected Parcelable onSaveInstanceState() {
  284. Parcelable superState = super.onSaveInstanceState();
  285. MyFragmentTabHost.SavedState ss = new MyFragmentTabHost.SavedState(superState);
  286. ss.curTab = getCurrentTabTag();
  287. return ss;
  288. }
  289.  
  290. @Override
  291. protected void onRestoreInstanceState(Parcelable state) {
  292. if (!(state instanceof MyFragmentTabHost.SavedState)) {
  293. super.onRestoreInstanceState(state);
  294. return;
  295. }
  296. MyFragmentTabHost.SavedState ss = (MyFragmentTabHost.SavedState) state;
  297. super.onRestoreInstanceState(ss.getSuperState());
  298. setCurrentTabByTag(ss.curTab);
  299. }
  300.  
  301. @Override
  302. public void onTabChanged(String tabId) {
  303. if (mAttached) {
  304. final FragmentTransaction ft = doTabChanged(tabId, null);
  305. if (ft != null) {
  306. ft.commit();
  307. }
  308. }
  309. if (mOnTabChangeListener != null) {
  310. mOnTabChangeListener.onTabChanged(tabId);
  311. }
  312. }
  313.  
  314. @Nullable
  315. private FragmentTransaction doTabChanged(@Nullable String tag,
  316. @Nullable FragmentTransaction ft) {
  317. final MyFragmentTabHost.TabInfo newTab = getTabInfoForTag(tag);
  318. if (mLastTab != newTab) {
  319. if (ft == null) {
  320. ft = mFragmentManager.beginTransaction();
  321. }
  322.  
  323. if (mLastTab != null) {
  324. if (mLastTab.fragment != null) {
  325. // ft.detach(mLastTab.fragment);
  326. ft.hide(mLastTab.fragment);//http://blog.csdn.net/w1054993544/article/details/37658183
  327. }
  328. }
  329.  
  330. if (newTab != null) {
  331. if (newTab.fragment == null) {
  332. newTab.fragment = Fragment.instantiate(mContext,
  333. newTab.clss.getName(), newTab.args);
  334. ft.add(mContainerId, newTab.fragment, newTab.tag);
  335. } else {
  336. // ft.attach(newTab.fragment);
  337. ft.show(newTab.fragment);//http://blog.csdn.net/w1054993544/article/details/37658183
  338. }
  339. }
  340.  
  341. mLastTab = newTab;
  342. }
  343.  
  344. return ft;
  345. }
  346.  
  347. @Nullable
  348. private MyFragmentTabHost.TabInfo getTabInfoForTag(String tabId) {
  349. for (int i = 0, count = mTabs.size(); i < count; i++) {
  350. final MyFragmentTabHost.TabInfo tab = mTabs.get(i);
  351. if (tab.tag.equals(tabId)) {
  352. return tab;
  353. }
  354. }
  355. return null;
  356. }
  357. }

MyFragmentTabHost

代码是复制的系统的FragmentTabHost,只有一小部分和系统不一样的代码:

将tab_top_underline_item.xml文件复制到项目中

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- 带有下划线的顶部选项卡子项的布局文件(选择图片界面) -->
  3. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/toptabLayout"
  5. android:layout_width="wrap_content"
  6. android:layout_height="wrap_content"
  7. android:gravity="center"
  8. android:paddingTop="@dimen/tab_top_underline_padding"
  9. android:paddingLeft="@dimen/tab_top_underline_padding"
  10. android:paddingRight="@dimen/tab_top_underline_padding"
  11. >
  12. <!-- 标题 -->
  13. <TextView
  14. android:id="@+id/top_title"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:gravity="center"
  18. android:text=""
  19. android:textSize="@dimen/tab_top_underline_title_size"
  20. android:textColor="@color/tab_text_normal_top"
  21. android:layout_alignParentTop="true"
  22. android:layout_centerHorizontal="true" />
  23. <!-- 下划线-->
  24. <!-- android:background="@color/tab_underline_selected_top" -->
  25. <View
  26. android:id="@+id/top_underline"
  27. android:layout_width="match_parent"
  28. android:layout_height="@dimen/tab_top_underline_height"
  29. android:background="@color/tab_underline_normal_top"
  30. android:layout_below="@id/top_title"
  31. android:layout_centerHorizontal="true"
  32. android:layout_marginTop="@dimen/tab_top_underline_padding"
  33. />
  34.  
  35. </RelativeLayout>

tab_top_underline_item

在colors.xml文件中添加以下代码:【后续可根据实际情况更改背景颜色、文字颜色值】

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <color name="colorPrimary">#3F51B5</color>
  4. <color name="colorPrimaryDark">#303F9F</color>
  5. <color name="colorAccent">#FF4081</color>
  6.  
  7. <!-- *********************************顶部选项卡区域********************************* -->
  8. <!-- 顶部选项卡下划线背景色 -->
  9. <color name="tab_underline_normal_top">#00ffffff</color>
  10. <color name="tab_underline_selected_top">#FF7700</color>
  11. <!-- 顶部选项卡文本颜色 -->
  12. <color name="tab_text_normal_top">#191919</color>
  13. <color name="tab_text_selected_top">#FF7700</color>
  14.  
  15. </resources>

在dimens.xml文件中添加以下代码:【后续可根据实际情况更改底部选项卡区域的高度值、文字大小值】

  1. <resources>
  2. <!-- Default screen margins, per the Android Design guidelines. -->
  3. <dimen name="activity_horizontal_margin">16dp</dimen>
  4. <dimen name="activity_vertical_margin">16dp</dimen>
  5.  
  6. <!-- *********************************顶部选项卡区域********************************* -->
  7. <!-- 选项卡的内边距 -->
  8. <dimen name="tab_top_underline_padding">10dp</dimen>
  9. <!-- 选项卡标题的文字大小 -->
  10. <dimen name="tab_top_underline_title_size">18sp</dimen>
  11. <!-- 选项卡标题的下划线高度 -->
  12. <dimen name="tab_top_underline_height">3dp</dimen>
  13.  
  14. </resources>

至此,选项卡子项的布局所需的文件已集成到项目中了。

在AndroidManifest.xml文件中添加网络请求的权限【demo中用到的】

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.why.project.fragmenttabhostunderlinedemo">
  4.  
  5. <!-- ======================授权访问网络(HttpUtil)========================== -->
  6. <!-- 允许程序打开网络套接字 -->
  7. <uses-permission android:name="android.permission.INTERNET"/>
  8.  
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@mipmap/ic_launcher"
  12. android:label="@string/app_name"
  13. android:supportsRtl="true"
  14. android:theme="@style/AppTheme">
  15. <activity android:name=".MainActivity">
  16. <intent-filter>
  17. <action android:name="android.intent.action.MAIN"/>
  18.  
  19. <category android:name="android.intent.category.LAUNCHER"/>
  20. </intent-filter>
  21. </activity>
  22. </application>
  23.  
  24. </manifest>

三、使用方法

在Activity布局文件中引用MyFragmentTabHost【注意:TabWidget的android:layout_width="match_parent"】

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- 顶部选项卡区域 -->
  3. <com.why.project.fragmenttabhostunderlinedemo.views.tab.MyFragmentTabHost
  4. xmlns:android="http://schemas.android.com/apk/res/android"
  5. android:id="@+id/tab_top_underline_ftabhost_layout"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent"
  8. >
  9.  
  10. <!-- 必须要有LinearLayout,因为FragmentTabHost属于FrameLayout帧布局 -->
  11. <LinearLayout
  12. android:layout_width="match_parent"
  13. android:layout_height="match_parent"
  14. android:orientation="vertical">
  15.  
  16. <!-- 选项卡区域 -->
  17. <!--注意:原来的配置:android:layout_height="?attr/actionBarSize"-->
  18. <TabWidget
  19. android:id="@android:id/tabs"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content"
  22. android:layout_gravity="top|center_horizontal"/>
  23.  
  24. <!-- 分割线 -->
  25. <View
  26. android:layout_width="match_parent"
  27. android:layout_height="1dp"
  28. android:background="#cfcfcf">
  29. </View>
  30.  
  31. <!-- 碎片切换区域,且其id必须为@android:id/tabcontent -->
  32. <FrameLayout
  33. android:id="@android:id/tabcontent"
  34. android:layout_width="match_parent"
  35. android:layout_height="0dp"
  36. android:layout_weight="1"
  37. />
  38.  
  39. </LinearLayout>
  40.  
  41. </com.why.project.fragmenttabhostunderlinedemo.views.tab.MyFragmentTabHost>

创建需要用到的fragment类和布局文件【后续可根据实际情况更改命名,并且需要重新import R文件】

 

fragment_web.xml文件布局如下

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent">
  6.  
  7. <!-- webview -->
  8. <WebView
  9. android:id="@+id/web_view"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent"></WebView>
  12.  
  13. </LinearLayout>

WebViewFragment

  1. package com.why.project.fragmenttabhostunderlinedemo.fragment;
  2.  
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.webkit.WebView;
  9. import android.webkit.WebViewClient;
  10.  
  11. import com.why.project.fragmenttabhostunderlinedemo.R;
  12.  
  13. /**
  14. * @Created HaiyuKing
  15. * @Used 首页界面——碎片界面
  16. */
  17. public class WebViewFragment extends BaseFragment{
  18.  
  19. private static final String TAG = "WebViewFragment";
  20. /**View实例*/
  21. private View myView;
  22.  
  23. private WebView web_view;
  24.  
  25. /**传递过来的参数*/
  26. private String bundle_param;
  27.  
  28. //重写
  29. public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
  30.  
  31. //使用FragmentTabHost时,Fragment之间切换时每次都会调用onCreateView方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态。
  32. //http://www.cnblogs.com/changkai244/p/4110173.html
  33. if(myView==null){
  34. myView = inflater.inflate(R.layout.fragment_web, container, false);
  35. //接收传参
  36. Bundle bundle = this.getArguments();
  37. bundle_param = bundle.getString("param");
  38. }
  39. //缓存的rootView需要判断是否已经被加过parent, 如果有parent需要从parent删除,要不然会发生这个rootview已经有parent的错误。
  40. ViewGroup parent = (ViewGroup) myView.getParent();
  41. if (parent != null) {
  42. parent.removeView(myView);
  43. }
  44.  
  45. return myView;
  46. }
  47.  
  48. @Override
  49. public void onActivityCreated(Bundle savedInstanceState) {
  50. // TODO Auto-generated method stub
  51. super.onActivityCreated(savedInstanceState);
  52.  
  53. //初始化控件以及设置
  54. initView();
  55. //初始化数据
  56. initData();
  57. //初始化控件的点击事件
  58. initEvent();
  59. }
  60. @Override
  61. public void onResume() {
  62. super.onResume();
  63. }
  64.  
  65. @Override
  66. public void onPause() {
  67. super.onPause();
  68. }
  69.  
  70. @Override
  71. public void onDestroy() {
  72. super.onDestroy();
  73. }
  74.  
  75. /**
  76. * 初始化控件
  77. */
  78. private void initView() {
  79. web_view = (WebView) myView.findViewById(R.id.web_view);
  80. //设置支持js脚本
  81. // web_view.getSettings().setJavaScriptEnabled(true);
  82. web_view.setWebViewClient(new WebViewClient() {
  83. /**
  84. * 重写此方法表明点击网页内的链接由自己处理,而不是新开Android的系统browser中响应该链接。
  85. */
  86. @Override
  87. public boolean shouldOverrideUrlLoading(WebView webView, String url) {
  88. //webView.loadUrl(url);
  89. return false;
  90. }
  91. });
  92. }
  93.  
  94. /**
  95. * 初始化数据
  96. */
  97. public void initData() {
  98. Log.e("tag","{initData}bundle_param="+bundle_param);
  99. web_view.loadUrl(bundle_param);//加载网页
  100. }
  101.  
  102. /**
  103. * 初始化点击事件
  104. * */
  105. private void initEvent(){
  106. }
  107.  
  108. }

在Activity中使用如下【继承FragmentActivity或者其子类】

  1. package com.why.project.fragmenttabhostunderlinedemo;
  2.  
  3. import android.content.Context;
  4. import android.os.Bundle;
  5. import android.support.v4.app.Fragment;
  6. import android.support.v7.app.AppCompatActivity;
  7. import android.view.View;
  8. import android.widget.TabHost;
  9. import android.widget.TextView;
  10. import android.widget.Toast;
  11.  
  12. import com.why.project.fragmenttabhostunderlinedemo.fragment.WebViewFragment;
  13. import com.why.project.fragmenttabhostunderlinedemo.views.tab.MyFragmentTabHost;
  14.  
  15. import java.util.ArrayList;
  16.  
  17. public class MainActivity extends AppCompatActivity {
  18.  
  19. private MyFragmentTabHost mTopUnderlineFTabHostLayout;
  20. //选项卡子类集合
  21. private ArrayList<TabItem> tabItemList = new ArrayList<TabItem>();
  22.  
  23. @Override
  24. protected void onCreate(Bundle savedInstanceState) {
  25. super.onCreate(savedInstanceState);
  26. setContentView(R.layout.activity_main);
  27.  
  28. initTabList();
  29. initFTabHostLayout();
  30. setFTabHostData();
  31. initEvents();
  32. }
  33.  
  34. /**
  35. * 初始化选项卡数据集合
  36. */
  37. private void initTabList() {
  38. //底部选项卡对应的Fragment类使用的是同一个Fragment,那么需要考虑切换Fragment时避免重复加载UI的问题】
  39. tabItemList.add(new TabItem(this,"百度",WebViewFragment.class));
  40. tabItemList.add(new TabItem(this,"博客园",WebViewFragment.class));
  41. tabItemList.add(new TabItem(this,"CSDN",WebViewFragment.class));
  42. }
  43.  
  44. /**
  45. * 初始化FragmentTabHost
  46. */
  47. private void initFTabHostLayout() {
  48. //实例化
  49. mTopUnderlineFTabHostLayout = (MyFragmentTabHost) findViewById(R.id.tab_top_underline_ftabhost_layout);
  50. mTopUnderlineFTabHostLayout.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);//最后一个参数是碎片切换区域的ID值
  51. // 去掉分割线
  52. mTopUnderlineFTabHostLayout.getTabWidget().setDividerDrawable(null);
  53.  
  54. }
  55.  
  56. /**
  57. * 设置选项卡的内容
  58. */
  59. private void setFTabHostData() {
  60. //Tab存在于TabWidget内,而TabWidget是存在于TabHost内。与此同时,在TabHost内无需在写一个TabWidget,系统已经内置了一个TabWidget
  61. for (int i = 0; i < tabItemList.size(); i++) {
  62. //实例化一个TabSpec,设置tab的名称和视图
  63. TabHost.TabSpec spec = mTopUnderlineFTabHostLayout.newTabSpec(tabItemList.get(i).getTabTitle()).setIndicator(tabItemList.get(i).getTabView());
  64. // 添加Fragment
  65. //初始化传参:http://bbs.csdn.net/topics/391059505
  66. Bundle bundle = new Bundle();
  67. if(i == 0 ){
  68. bundle.putString("param", "http://www.baidu.com");
  69. }else if(i == tabItemList.size() - 1){
  70. bundle.putString("param", "http://blog.csdn.net");
  71. }else{
  72. bundle.putString("param", "http://www.cnblogs.com");
  73. }
  74.  
  75. mTopUnderlineFTabHostLayout.addTab(spec, tabItemList.get(i).getTabFragment(), bundle);
  76. }
  77.  
  78. //默认选中第一项
  79. mTopUnderlineFTabHostLayout.setCurrentTab(0);
  80. tabItemList.get(0).setChecked(true);
  81. }
  82.  
  83. private void initEvents() {
  84. //选项卡的切换事件监听
  85. mTopUnderlineFTabHostLayout.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
  86. @Override
  87. public void onTabChanged(String tabId) {
  88. //重置Tab样式
  89. for (int i = 0; i < tabItemList.size(); i++) {
  90. TabItem tabitem = tabItemList.get(i);
  91. if (tabId.equals(tabitem.getTabTitle())) {
  92. tabitem.setChecked(true);
  93. } else {
  94. tabitem.setChecked(false);
  95. }
  96. }
  97.  
  98. Toast.makeText(MainActivity.this, tabId, Toast.LENGTH_SHORT).show();
  99.  
  100. }
  101. });
  102. }
  103.  
  104. /**
  105. * 选项卡子项类*/
  106. class TabItem{
  107.  
  108. private Context mContext;
  109.  
  110. private TextView top_title;
  111. private View top_underline;
  112.  
  113. //底部选项卡对应的文字
  114. private String tabTitle;
  115. //底部选项卡对应的Fragment类
  116. private Class<? extends Fragment> tabFragment;
  117.  
  118. public TabItem(Context mContext, String tabTitle, Class tabFragment){
  119. this.mContext = mContext;
  120.  
  121. this.tabTitle = tabTitle;
  122. this.tabFragment = tabFragment;
  123. }
  124.  
  125. public Class<? extends Fragment> getTabFragment() {
  126. return tabFragment;
  127. }
  128.  
  129. public String getTabTitle() {
  130. return tabTitle;
  131. }
  132.  
  133. /**
  134. * 获取底部选项卡的布局实例并初始化设置*/
  135. private View getTabView() {
  136. //============引用选项卡的各个选项的布局文件=================
  137. View toptabitemView = View.inflate(mContext,R.layout.tab_top_underline_item, null);
  138.  
  139. //===========设置选项卡的文字==========
  140. top_title = (TextView) toptabitemView.findViewById(R.id.top_title);
  141. //设置选项卡的文字
  142. top_title.setText(tabTitle);
  143.  
  144. //===========设置选项卡控件的下划线==========
  145. top_underline = (View) toptabitemView.findViewById(R.id.top_underline);
  146.  
  147. return toptabitemView;
  148. }
  149.  
  150. /**
  151. * 更新文字颜色
  152. */
  153. public void setChecked(boolean isChecked) {
  154. if(tabTitle != null){
  155. if(isChecked){
  156. //修改文字颜色
  157. top_title.setTextColor(getResources().getColor(R.color.tab_text_selected_top));
  158. //修改下划线的颜色
  159. top_underline.setBackgroundColor(getResources().getColor(R.color.tab_underline_selected_top));
  160. }else{
  161. //修改文字颜色
  162. top_title.setTextColor(getResources().getColor(R.color.tab_text_normal_top));
  163. //修改下划线的颜色
  164. top_underline.setBackgroundColor(getResources().getColor(R.color.tab_underline_normal_top));
  165. }
  166. }
  167. }
  168. }
  169. }

混淆配置

参考资料

Android的FragmentTabHost使用(顶部或底部菜单栏)

FragmentTabHost使用方法

Android_ FragmentTabHost切换Fragment时避免重复加载UI

使用FragmentTabHost+TabLayout+ViewPager实现双层嵌套Tab

如何自定义FragmentTabHost中某一个Tab的点击效果

FragmentTabHost布局的使用及优化方式

改变FragmentTabHost选中的文字颜色

fragmenttabhost 传参问题

FragmentTabHost+fragment中获得fragment的对象

fragment中的attach/detach方法说明(网上拷贝,只为作笔记)

FragmentTabHost切换Fragment,与ViewPager切换Fragment时重新onCreateView的问题

Android选项卡动态滑动效果

项目demo下载地址

https://github.com/haiyuKing/FragmentTabHostUnderLineDemo

FragmentTabHostUnderLineDemo【FragmentTabHost带下划线】的更多相关文章

  1. 关于python中带下划线的变量和函数 的意义

    总结: 变量: 1.  前带_的变量:  标明是一个私有变量, 只用于标明, 外部类还是可以访问到这个变量 2.  前带两个_ ,后带两个_ 的变量:  标明是内置变量, 3.  大写加下划线的变量: ...

  2. delphi 仅带下划线的TEdit控件

    在做录入框的时候,很希望有一个只带下划线的文本框,网上介绍的很多,有自己做组件的,须不知Delphi下只需要简单设置几个属性即可达到目的.

  3. geotools导入shp文件到Oracle数据库时表名带下划线的问题解决

    问题: 最近在做利用geotools导入shp文件到Oracle表中,发现一个问题Oracle表名带下划线时导入失败,问题代码行: dsOracle.getFeatureWriterAppend(or ...

  4. [转]关于python中带下划线的变量和函数的意义

    Python 的代码风格由 PEP 8 描述.这个文档描述了 Python 编程风格的方方面面.在遵守这个文档的条件下,不同程序员编写的 Python 代码可以保持最大程度的相似风格.这样就易于阅读, ...

  5. TabTopUnderLineLayout【自定义顶部选项卡(带下划线)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 自定义顶部选项卡布局LinearLayout类,实现带下划线样式的效果. 备注:如果配合Fragment的话,MainActivit ...

  6. TabTopAutoLayout【自定义顶部选项卡区域(带下划线)(动态选项卡数据且可滑动)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 自定义顶部选项卡布局LinearLayout类,实现带下划线且可滑动效果.[实际情况中建议使用RecyclerView] 备注:如果 ...

  7. Python里的单下划线,双下划线,以及前后都带下划线的意义

    Python里的单下划线,双下划线,以及前后都带下划线的意义: 单下划线如:_name 意思是:不能通过from modules import * 导入,如需导入需要:from modules imp ...

  8. 【转】关于python中带下划线的变量和函数 的意义

    http://www.blogjava.net/lincode/archive/2011/02/02/343859.html 总结: 变量: 1.  前带_的变量:  标明是一个私有变量, 只用于标明 ...

  9. 带下划线的 HTTP Header无法获取到可能是因为nginx

    背景:新版本修改了个功能是在老版本的基础上做的,同一个接口,需要兼容老版本,因此让前台在header中封装了 version版本号,client_type 客户端类型,根据这两个字段判断接口要走的逻辑 ...

随机推荐

  1. java-直接选择排序

    直接选择排序是一种简单的排序方法,它每次从当前待排序的区间中选择出最小的元素,把该元素与该区间的第一个元素交换. 第一次从a[0]~a[n-1]中选取最小值,与a0]交换,第二次从a[1]~a[n-1 ...

  2. get.go

    //获取空间文件 ))     resp, err := http.DefaultClient.Do(req)     if err != nil {         return nil, err ...

  3. disk.go

    package disk import "syscall" //空间使用结构体 type DiskStatus struct {     Size uint64     Used ...

  4. BZOJ_3994_[SDOI2015]约数个数和_莫比乌斯反演

    BZOJ_3994_[SDOI2015]约数个数和_莫比乌斯反演 Description  设d(x)为x的约数个数,给定N.M,求   Input 输入文件包含多组测试数据. 第一行,一个整数T,表 ...

  5. 复仇者联盟3热映,我用python爬取影评告诉你它都在讲什么

    Python(发音:英[?pa?θ?n],美[?pa?θɑ:n]),是一种面向对象.直译式电脑编程语言,也是一种功能强大的通用型语言,已经具有近二十年的发展历史,成熟且稳定.它包含了一组完善而且容易理 ...

  6. 实验吧——隐写术之复杂的QR_code

    好久没有更新隐写术方面的题目了,对不起各位小可爱,今天我会多多更新几篇文章,来慰藉你们! 永远爱你们的 ---------新宝宝 1:复杂的QR_code 解题思路:保存图片之后使用在线解码工具,并没 ...

  7. JavaWeb学习总结(转载)

    JavaWeb学习总结(五十三)--Web应用中使用JavaMail发送邮件      JavaWeb学习总结(五十二)--使用JavaMail创建邮件和发送邮件     JavaWeb学习总结(五十 ...

  8. 深入理解数据库磁盘存储(Disk Storage)

    数据库管理系统将数据存储在磁盘.磁带以及其他的裸设备上,虽然这些设备的访问速度相比内存慢很多,但其非易失性和大容量的特点使他们成为数据存储的不二之选. 本文主要讨论大型数据库产品的磁盘存储内部结构,这 ...

  9. 《k8s 源码分析》- Custom Controller 之 Informer

    Custom Controller 之 Informer 概述 架构概览 reflector - List & Watch API Server Reflector 对象 ListAndWat ...

  10. MySQL之父造访腾讯云 为腾讯云数据库开源点赞

    近日,技术大牛 MariaDB 公司创始人兼CTO Michael Widenius(又名Monty).MariaDB 基金会主席 Kaj 来到中国,针对MariaDB与腾讯云的技术合作进行回访.去年 ...