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

前言

使用FragmentTabHost实现顶部选项卡(居中且宽度非全屏)展现。

备注:该Demo主要是演示FragmentTabHost的一些设置和部分功能,实际中需要参考其他Demo。

效果图

代码分析

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

2、未使用自定义的FragmentTabHost;【建议使用自定义的FragmentTabHost,见《FragmentTabHostUnderLineDemo【FragmentTabHost带下划线】》】

3、演示更新文字颜色和背景;

4、切换回来后无法保持打开的网页,始终实现网址的首页。原因是FragmentTabHost切换时执行的是attach/detach,而不是show/hide。而atach触发的执行顺序:

attach()->onCreateView()->onActivityCreated()->onStart()->onResume()

而Demo中将webview.loadUrl()执行放到了onActivityCreated()方法中了。

解决方案:建议采用自定义的FragmentTabHost。【补充:还可以使用demo的fragment中onCreateView方法中的方案,两种方案各有利弊。】

使用步骤

一、项目组织结构图

注意事项:

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

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

二、导入步骤

将选项卡子项布局文件tab_top_item.xml文件复制到项目中

<?xml version="1.0" encoding="utf-8"?>
<!-- 顶部选项卡布局 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/tab_bg_normal_top"
android:gravity="center" > <!-- android:checkMark="?android:attr/listChoiceIndicatorMultiple"代表多选
android:checkMark="?android:attr/listChoiceIndicatorSingle" 代表单选
该属性不添加的话,不会显示方框或者圆点
--> <!-- android:drawableTop的属性值使用drawable目录下的selector选择器 -->
<!-- android:tag="tag1"用于checkedTextview的索引 --> <!-- 选项卡的内容(图片+文字)类似RadioButton -->
<!--android:textAlignment="center" 文本居中-->
<CheckedTextView
android:id="@+id/toptab_checkedTextView"
android:tag="tag1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text=""
android:textSize="@dimen/tab_larger_text_size"
android:textColor="@color/tab_text_normal_top"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:textAlignment="center"/>
</RelativeLayout>

tab_top_item

将selector文件复制到项目中【后续可根据实际情况更换】

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

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color> <!-- *********************************顶部选项卡区域********************************* -->
<!-- 顶部选项卡底部背景色 -->
<color name="tab_bg_normal_top">#00ffffff</color>
<color name="tab_bg_selected_top">#0075AA</color>
<!-- 顶部选项卡文本颜色 -->
<color name="tab_text_normal_top">#0075AA</color>
<color name="tab_text_selected_top">#ffffff</color> </resources>

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

<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen> <!-- *********************************顶部选项卡区域********************************* -->
<!-- 顶部选项卡文本大小 -->
<dimen name="tab_text_size">14sp</dimen>
<dimen name="tab_medium_text_size">16sp</dimen>
<dimen name="tab_larger_text_size">18sp</dimen>
<dimen name="tab_larger_small_text_size">20sp</dimen>
</resources>

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

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.why.project.fragmenttabhosttopdemo"> <!-- ======================授权访问网络(HttpUtil)========================== -->
<!-- 允许程序打开网络套接字 -->
<uses-permission android:name="android.permission.INTERNET"/> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application> </manifest>

三、使用方法

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

<?xml version="1.0" encoding="utf-8"?>
<!-- 顶部选项卡区域 -->
<android.support.v4.app.FragmentTabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tab_top_ftabhost_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
> <!-- 必须要有LinearLayout,因为FragmentTabHost属于FrameLayout帧布局 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <!-- 选项卡区域 -->
<!--注意:原来的配置:android:layout_height="?attr/actionBarSize"-->
<TabWidget
android:id="@android:id/tabs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="10dp"/> <!-- 碎片切换区域,且其id必须为@android:id/tabcontent -->
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/> </LinearLayout> </android.support.v4.app.FragmentTabHost>

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

 

fragment_web.xml文件布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"> <!-- webview -->
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></WebView> </LinearLayout>

 WebViewFragment

package com.why.project.fragmenttabhosttopdemo.fragment;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient; import com.why.project.fragmenttabhosttopdemo.R; /**
* @Created HaiyuKing
* @Used 首页界面——碎片界面
*/
public class WebViewFragment extends BaseFragment{ private static final String TAG = "WebViewFragment";
/**View实例*/
private View myView; private WebView web_view; /**传递过来的参数*/
private String bundle_param; //重写
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { //使用FragmentTabHost时,Fragment之间切换时每次都会调用onCreateView方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态。
//http://www.cnblogs.com/changkai244/p/4110173.html
if(myView==null){
myView = inflater.inflate(R.layout.fragment_web, container, false);
//接收传参
Bundle bundle = this.getArguments();
bundle_param = bundle.getString("param");
}
//缓存的rootView需要判断是否已经被加过parent, 如果有parent需要从parent删除,要不然会发生这个rootview已经有parent的错误。
ViewGroup parent = (ViewGroup) myView.getParent();
if (parent != null) {
parent.removeView(myView);
} return myView;
} @Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState); //初始化控件以及设置
initView();
//初始化数据
initData();
//初始化控件的点击事件
initEvent();
}
@Override
public void onResume() {
super.onResume();
} @Override
public void onPause() {
super.onPause();
} @Override
public void onDestroy() {
super.onDestroy();
} /**
* 初始化控件
*/
private void initView() {
web_view = (WebView) myView.findViewById(R.id.web_view);
//设置支持js脚本
// web_view.getSettings().setJavaScriptEnabled(true);
web_view.setWebViewClient(new WebViewClient() {
/**
* 重写此方法表明点击网页内的链接由自己处理,而不是新开Android的系统browser中响应该链接。
*/
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
//webView.loadUrl(url);
return false;
}
});
} /**
* 初始化数据
*/
public void initData() {
Log.e("tag","{initData}bundle_param="+bundle_param);
web_view.loadUrl(bundle_param);//加载网页
} /**
* 初始化点击事件
* */
private void initEvent(){
} }

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

package com.why.project.fragmenttabhosttopdemo;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTabHost;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.CheckedTextView;
import android.widget.TabHost;
import android.widget.Toast; import com.why.project.fragmenttabhosttopdemo.fragment.WebViewFragment; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private FragmentTabHost mTopFTabHostLayout; //选项卡子类集合
private ArrayList<TabItem> tabItemList = new ArrayList<TabItem>(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); initTabList();
initFTabHostLayout();
setFTabHostData();
initEvents(); } /**
* 初始化选项卡数据集合
*/
private void initTabList() {
//底部选项卡对应的Fragment类使用的是同一个Fragment,那么需要考虑切换Fragment时避免重复加载UI的问题】
tabItemList.add(new TabItem(this,"已发布",WebViewFragment.class));
tabItemList.add(new TabItem(this,"未发布",WebViewFragment.class));
} /**
* 初始化FragmentTabHost
*/
private void initFTabHostLayout() {
//实例化
mTopFTabHostLayout = (FragmentTabHost) findViewById(R.id.tab_top_ftabhost_layout);
mTopFTabHostLayout.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);//最后一个参数是碎片切换区域的ID值
// 去掉分割线
mTopFTabHostLayout.getTabWidget().setDividerDrawable(null); } /**
* 设置选项卡的内容
*/
private void setFTabHostData() {
//Tab存在于TabWidget内,而TabWidget是存在于TabHost内。与此同时,在TabHost内无需在写一个TabWidget,系统已经内置了一个TabWidget
for (int i = 0; i < tabItemList.size(); i++) {
//实例化一个TabSpec,设置tab的名称和视图
TabHost.TabSpec spec = mTopFTabHostLayout.newTabSpec(tabItemList.get(i).getTabTitle()).setIndicator(tabItemList.get(i).getTabView());
// 添加Fragment
//初始化传参:http://bbs.csdn.net/topics/391059505
Bundle bundle = new Bundle();
if(i == 0 ){
bundle.putString("param", "http://www.baidu.com");
}else{
bundle.putString("param", "http://www.cnblogs.com");
} mTopFTabHostLayout.addTab(spec, tabItemList.get(i).getTabFragment(), bundle);
} //默认选中第一项
mTopFTabHostLayout.setCurrentTab(0);
upDateTab(mTopFTabHostLayout);
} /**
* 更新文字颜色和背景
* http://blog.csdn.net/ray_admin/article/details/69951540
*/
private void upDateTab(FragmentTabHost mTabHost) {
int size = mTabHost.getTabWidget().getChildCount();
for (int i = 0; i < size; i++) {
//CheckedTextView tv = (CheckedTextView) mTabHost.getTabWidget().getChildAt(i).findViewById(toptab_checkedTextView);//常规写法
CheckedTextView tv = (CheckedTextView) tabItemList.get(i).getToptab_checkedTextView();
if (mTabHost.getCurrentTab() == i) {//选中
//修改文字的颜色
tv.setTextColor(this.getResources().getColor(R.color.tab_text_selected_top));
//修改view的背景颜色
if (0 == i) {
mTopFTabHostLayout.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_top_layout_item_shape_left_selected);
} else if (i == size - 1) {
mTopFTabHostLayout.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_top_layout_item_shape_right_selected);
} else {
mTopFTabHostLayout.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_top_layout_item_shape_mid_selected);
}
} else {//不选中
//修改文字的颜色
tv.setTextColor(this.getResources().getColor(R.color.tab_text_normal_top));
//修改view的背景颜色
if (0 == i) {
mTopFTabHostLayout.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_top_layout_item_shape_left_unselected);
} else if (i == size - 1) {
mTopFTabHostLayout.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_top_layout_item_shape_right_unselected);
} else {
mTopFTabHostLayout.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_top_layout_item_shape_mid_unselected);
}
}
}
} private void initEvents() {
//选项卡的切换事件监听
mTopFTabHostLayout.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) { upDateTab(mTopFTabHostLayout); Toast.makeText(MainActivity.this, tabId, Toast.LENGTH_SHORT).show(); }
}); } /**
* 选项卡子项类*/
class TabItem{ private Context mContext; private CheckedTextView toptab_checkedTextView; //底部选项卡对应的文字
private String tabTitle;
//底部选项卡对应的Fragment类
private Class<? extends Fragment> tabFragment; public TabItem(Context mContext, String tabTitle, Class tabFragment){
this.mContext = mContext; this.tabTitle = tabTitle;
this.tabFragment = tabFragment;
} public Class<? extends Fragment> getTabFragment() {
return tabFragment;
} public String getTabTitle() {
return tabTitle;
} /**
* 获取底部选项卡的布局实例并初始化设置*/
private View getTabView() {
//取得布局实例
View toptabitemView = View.inflate(mContext, R.layout.tab_top_item, null); //===========设置CheckedTextView控件的图片和文字==========
toptab_checkedTextView = (CheckedTextView) toptabitemView.findViewById(R.id.toptab_checkedTextView); //设置CheckedTextView的文字
toptab_checkedTextView.setText(tabTitle); return toptabitemView;
} public CheckedTextView getToptab_checkedTextView() {
return toptab_checkedTextView;
}
}
}

混淆配置

参考资料

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/FragmentTabHostTopDemo

FragmentTabHostTopDemo【FragmentTabHost固定宽度且居中】的更多相关文章

  1. div(固定宽度和不固定宽度)居中显示的方法总结

    今天我总结一下css实现div居中的方法,有的是固定宽度的,还有的是不固定宽度的. 1.使用自动外边距实现DIV CSS居中 CSS中首选的让元素水平居中的方法就是使用margin属性—将元素的mar ...

  2. TabTopLayout【自定义顶部选项卡区域(固定宽度且居中)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 自定义顶部选项卡并居中显示.结合显示/隐藏view的方式实现切换功能(正常情况下可能是切换fragment). 效果图 代码分析 T ...

  3. css3学习——一列固定宽度且居中

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  4. FragmentTabHostBottomDemo【FragmentTabHost + Fragment实现底部选项卡】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 使用FragmentTabHost实现底部选项卡效果. 备注:该Demo主要是演示FragmentTabHost的一些设置和部分功能 ...

  5. Android快速开发常用知识点系列目录

    项目构建 Android项目目录结构模板以及简单说明[简单版] Android Studio配置统一管理依赖版本号引用 Android多Module下的Application引用方式 APP开篇 An ...

  6. CSS常见居中讨论

    先来一个常见的案例,把一张图片和下方文字进行居中: 首先处理左右居中,考虑到img是一个行内元素,下方的文字内容也是行内元素,因此直接用text-align即可: <style> .con ...

  7. 实用的60个CSS代码片段

    1.垂直对齐 如果你用CSS,则你会有困惑:我该怎么垂直对齐容器中的元素?现在,利用CSS3的Transform,可以很优雅的解决这个困惑: .verticalcenter{ position: re ...

  8. 60个有用CSS代码片段

    1.垂直对齐 如果你用CSS,则你会有困惑:我该怎么垂直对齐容器中的元素?现在,利用CSS3的Transform,可以很优雅的解决这个困惑: .verticalcenter{ position: re ...

  9. 实战案例:DIV嵌套

    缘于页面结构语义化的考虑,我们应该慎用div和span这两个通用元素,只有当划分页面结构模块时才使用div元素,因为模块本身是没有任何语义的,他仅代表一块独立的结构.如果想对段落内部分内联元素或文本应 ...

随机推荐

  1. Java程序员必备的Intellij插件

    以下是我用过不错的Intellij插件 1. .ignore 地址:https://plugins.jetbrains.com/plugin/7495--ignore 生成各种ignore文件,一键创 ...

  2. bzoj 4501 旅行

    01分数规划+最大权闭合子图 倒拓扑序处理每个节点 $$f[x]=\frac{\sum{f[v]}}{n}+1$$ 二分答案$val$ 只需要判断是否存在$\sum{f[v]}+1-val>0$ ...

  3. bzoj 2005 能量采集 莫比乌斯反演

    我们要求的是∑ni=1∑mj=1(2×gcd(i,j)−1) 化简得2×∑ni=1∑mj=1gcd(i,j)−n×m 所以我们现在只需要求出∑ni=1∑mj=1gcd(i,j)即可 ∑ni=1∑mj= ...

  4. BZOJ_1823_[JSOI2010]满汉全席_2-sat+tarjan

    BZOJ_1823_[JSOI2010]满汉全席_2-sat 题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1823 分析:一道比较容易看出来的 ...

  5. BZOJ_3932_[CQOI2015]任务查询系统_主席树

    BZOJ_3932_[CQOI2015]任务查询系统_主席树 题意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,P ...

  6. BZOJ_1486_[HNOI2009]最小圈_01分数规划

    BZOJ_1486_[HNOI2009]最小圈_01分数规划 Description Input Output Sample Input 4 5 1 2 5 2 3 5 3 1 5 2 4 3 4 1 ...

  7. 分布式团队中沟通引发的问题, itest 解决之道

    导读: 从问题场景和 itest 优雅解决办法及示例2部分来阐述 1.问题场景: 研发团队是分散在几地的分布式团队,经常会因沟通引来一些问题.如下三图是开发觉得测试进度太慢,一番对话之后, 接下来他们 ...

  8. Tensorflow源码解析1 -- 内核架构和源码结构

    1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层.比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android ...

  9. ASP.NET Core在Azure Kubernetes Service中的部署和管理

    目录 ASP.NET Core在Azure Kubernetes Service中的部署和管理 目标 准备工作 注册 Azure 账户 AKS文档 进入Azure门户(控制台) 安装 Azure Cl ...

  10. 面试挂在了 LRU 缓存算法设计上

    好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了.当时做题的时候,自己想的太多了,感觉设计一个 LRU(Least recently used) 缓存 ...