zoukankan      html  css  js  c++  java
  • FragmentTabHostTopDemo【FragmentTabHost固定宽度且居中】

    版权声明:本文为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

  • 相关阅读:
    Best Time to Buy and Sell Stock III
    Valid Palindrome
    Longest Substring Without Repeating Characters
    Copy List with Random Pointer
    Add Two Numbers
    Recover Binary Search Tree
    Anagrams
    ZigZag Conversion
    Merge k Sorted Lists
    Distinct Subsequences
  • 原文地址:https://www.cnblogs.com/whycxb/p/7787965.html
Copyright © 2011-2022 走看看