zoukankan      html  css  js  c++  java
  • Android 动态Tab分页效果实现

        当前项目使用的是TabHost+Activity进行分页,目前要做个报表功能,需要在一个Tab页内进行Activity的切换。比方说我有4个Tab页分别为Tab1,Tab2,Tab3,Tab4,现在的需求是需要将Tab1内的Activity动态切换。找了很多资料最终使用了ActivityGroup解决了问题,在这过程中顺便尝试了一下使用Fragment+FragmentActivity+TabHost和Fragment+FragmentActivity+ActionBar试图淘汰掉旧版的ActivityGroup和TabHost,但是发现如果要使用Fragment,我需要修改已有的各个Tab页内的Activity,工作量较大,而使用ActionBar还有着许多版本兼容性问题,并且ActionBar不支持多层嵌套,最终放弃。现在写一下我个人实验用的TabHost+ActivityGroup、Fragment+FragmentActivity+TabHost和Fragment+FragmentActivity+ActionBar的Demo,供参考。

       首先是TabHost+ActivityGroup。在最初的想法中,我是使用通过清空TabHost现有内容,再添加新内容来实现动态加载Tab页效果,具体代码不说了,举个例子:我有4个Tab页分别为Tab1、Tab2、Tab3和Tab4,存放4个Activity分别为Tab1Activity、Tab2Activity、Tab3Activity和Tab4Activity,现在我需要将Tab1位置的Tab1Activity变成Tab5Activity,我的做法是先全部清空,然后重新添加Tab1、Tab2、Tab3和Tab4,在添加的时候,将Tab1指向的Activity变成Tab5Activity。这种方法无疑是非常蠢得,而且在这一切换之后,会发现Ta5Activity的生命周期出现异常,每次在Tab1和其他Tab之间切换时,Ta5Activity都会被destroy掉重新create。这个问题我至今没找到原因。

       然后找到了ActivityGroup。ActivityGroup也是继承至Activity类,可以看成是一个Actvity的容器类。 以上面的例子为例,当我们把ActivityGroup放入Tab1内时,我们就可以通过管理ActivityGroup来达到动态切换Activity的效果,贴上ActivityGroup的代码:

    public class ActivityGroup1 extends ActivityGroup {
        private ScrollView container = null;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.layout);
            //存放Activity的容器
            container = (ScrollView) findViewById(R.id.containerBody);
             // 模块1
            ImageView btnModule1 = (ImageView) findViewById(R.id.btnModule1);
            btnModule1.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    //清空容器内现有内容
                    container.removeAllViews();
                    //加载activity
                    container.addView(getLocalActivityManager().startActivity(
                            "Module1",
                            new Intent(ActivityGroup1.this, Tab1Activity.class)
                                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
                            .getDecorView());
                }
            });
    
            // 模块2
            ImageView btnModule2 = (ImageView) findViewById(R.id.btnModule2);
            btnModule2.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    container.removeAllViews();
                    container.addView(getLocalActivityManager().startActivity(
                            "Module2",
                            new Intent(ActivityGroup1.this, Tab2Activity.class)
                                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
                            .getDecorView());
                }
            });
    
            // 模块3
            ImageView btnModule3 = (ImageView) findViewById(R.id.btnModule3);
            btnModule3.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    container.removeAllViews();
                    container.addView(getLocalActivityManager().startActivity(
                            "Module3",
                            new Intent(ActivityGroup1.this, Tab3Activity.class)
                                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
                            .getDecorView());
                }
            });
        }
    }

    layout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent" android:orientation="vertical"
        android:layout_height="fill_parent">
        <LinearLayout android:gravity="center_horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
            <TextView android:id="@+id/cust_title" android:textColor="@android:color/white"
                android:textSize="28sp" android:text="模块1" android:layout_width="wrap_content"
                android:layout_height="wrap_content"></TextView>
        </LinearLayout>
        <!-- 中间动态加载View -->
        <ScrollView android:measureAllChildren="true" android:id="@+id/containerBody"
            android:layout_weight="1" android:layout_height="fill_parent"
            android:layout_width="fill_parent">
        </ScrollView>
        <LinearLayout android:background="@android:color/black"
            android:layout_gravity="bottom" android:orientation="horizontal"
            android:layout_width="fill_parent" android:layout_height="wrap_content">
            <!-- 功能模块按钮1 -->
            <ImageView android:id="@+id/btnModule1" android:src="@android:drawable/ic_dialog_dialer"
                android:layout_marginLeft="7dp" android:layout_marginTop="3dp"
                android:layout_marginBottom="3dp" android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <!-- 功能模块按钮2 -->
            <ImageView android:id="@+id/btnModule2" android:src="@android:drawable/ic_dialog_info"
                android:layout_marginLeft="7dp" android:layout_marginTop="3dp"
                android:layout_marginBottom="3dp" android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <!-- 功能模块按钮3 -->
            <ImageView android:id="@+id/btnModule3" android:src="@android:drawable/ic_dialog_alert"
                android:layout_marginLeft="7dp" android:layout_marginTop="3dp"
                android:layout_marginBottom="3dp" android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>

    在TabHost中的调用方式和普通的activity一样

    TabSpec tabSpec=mtabHost.newTabSpec("1").setIndicator("TAB1").setContent(
                    new Intent(this, FragmentActivity1.class));
            mtabHost.addTab(tabSpec);

    这样通过点击三个按钮就能达到动态切换Activity的效果了。

    顺便说下,当ActivityGroup内嵌套Activity时,Activity的生命周期跟TabHost内嵌套Activity一样。具体的百度。

         较新的版本中TabHost和ActivityGroup都已经被淘汰了,取而代之的是FragmentActivity+Fragment。但我很疑惑Fragment是否真能完全取代ActivityGroup的效果。就拿上述功能来说,我要想使用FragmentActivity替换ActivityGroup,那么我现有的Activity就必须重写,改装成Fragment。这种情况下,我在其他地方要使用这些Activity该怎么办呢?我没找到如何使用Fragment装载Activity的办法,所以暂时就没法用FragmentActivity替代ActivityGroup了。贴下使用FragmentActivity+Fragment+TabHost的实现代码:

    Fragment1Activity:

    public class FragmentActivity1 extends FragmentActivity{
        public static FragmentManager fm;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.fragment_acitivity_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();
        }
    }

    这里的FragmentActivity1并不显示具体内容,只做Fragment得容器使用,具体显示内容通过调用changeFragment方法动态加载。

    Fragment1和Fragment2的代码如下,点击按钮两者互相切换:

    public class Fragment1 extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.fragment1, 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;
        }
    }
    public class Fragment2 extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.fragment2, 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 Fragment1());
                }
            });
            return v;
        }
    }

    同样,在TabHost添加中跟普通的Activity一样:

    TabSpec tabSpec=mtabHost.newTabSpec("1").setIndicator("TAB1").setContent(
                    new Intent(this, FragmentActivity1.class));
            mtabHost.addTab(tabSpec);

    这三者的layout都很简单,就不写了。对比ActivityGroup方法发现,其实两者实现思路是一样的,都是先用一个“容器”(ActivityGroup和FragmentActivity)占住Tab1的位置,然后每次相应时,通过改变“容器”内的元素来达到动态改变的效果,区别就是ActivityGroup存放的是Activity,而FragmentActivity存放的是Fragment。(本人目前已经知道可以将Fragment嵌套到Activity中,是否有方法可以将Activity嵌套到Fragment中呢?那么就可以达到两个方法的完全兼容了,而不用像我我担心的,需要改造目前已有的Activity成Fragment来达到使用FragmentActivity替换ActivityGroup的效果)

        最后说下ActionBar+FragmentActivity+Fragment来替换掉TabHost。在使用过程中发现这个ActionBar限制有点多,于是舍弃了,贴下实现代码。

    public class FragmentActivity1 extends FragmentActivity implements ActionBar.TabListener{
        public static FragmentManager fm;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.fragment_acitivity_1);
            fm = getSupportFragmentManager();
            ActionBar actionBar=this.getActionBar();
            this.getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);  
            this.getActionBar().setDisplayShowTitleEnabled(false);  
            this.getActionBar().setDisplayShowHomeEnabled(false);  
            setBar("Tab 1");
            setBar("Tab 2"); 
            setBar("Tab 3"); 
            setBar("Tab 4"); 
            // 只當容器,主要內容已Fragment呈現
            initFragment(new Fragment1());
        }
        /**
         * 
         */
        private void setBar(String s) {
            Tab tab = this.getActionBar().newTab();  
            tab.setContentDescription(s);  
            tab.setText(s); 
            tab.setTabListener(this);
            getActionBar().addTab(tab);
        }
        // 切換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();
        }
        @Override
        public void onTabReselected(Tab arg0, android.app.FragmentTransaction arg1) {
            // TODO Auto-generated method stub
            
        }
        @Override
        public void onTabSelected(Tab arg0, android.app.FragmentTransaction arg1) {
            // TODO Auto-generated method stub
            if(arg0.getText().equals("Tab 1"))
            {
                changeFragment(new Fragment1());
            }
            else {
                changeFragment(new Fragment2());
            }
        }
        @Override
        public void onTabUnselected(Tab arg0, android.app.FragmentTransaction arg1) {
            // TODO Auto-generated method stub
            
        }
    }

    在minSdkVersion设置为11后就能调用getActionBar()方法了,可以给它添加tab,然后重点是为ActionBar.TabListener写点击Tab时要实现的功能,这里我进行了Fragment的切换,以达到TabHost相同的效果。要注意的是如果该FragmentActivity是嵌套在其他Activity中(比方说TabHost)时,getAction将会返回null,即无法进行分页,其他情况下也有可能返回null,限制还是比较多的。

  • 相关阅读:
    洛谷八连测R7 nzhtl1477-我回来了
    String中的equals方法原理分析
    Java线程
    Spring配置日志级别报红:Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'logging.level' to java.util.Map<java.lang.String
    # SpringMVC跨服务器上传文件出现的问题
    使用Maven创建Spring-web项目的基本流程
    Maven的下载与安装(环境变量的配置)
    eNSP的安装(附链接)
    数据库分页操作
    Sql语句占位符?的使用
  • 原文地址:https://www.cnblogs.com/linjzong/p/3299155.html
Copyright © 2011-2022 走看看