Android杂谈(7)搞一搞Fragment+官方API底部导航制作

来源:互联网 发布:js时间戳有什么作用 编辑:程序博客网 时间:2024/04/30 03:54

转载请注意:http://blog.csdn.net/wjzj000/article/details/51879798

本菜GitHub上开源了一个小的Android项目,感兴趣的看官大大们可以star下:

https://github.com/zhiaixinyang/MyFirstApp



进入暑假,啥也不想搞。所以就抱着几本书敲敲东西。


首先记录一下Fragment的一些用法和问题:

Fragment的生命周期:

正常的周期顺序是:

onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart -> onResume -> onPause -> onStop -> onDistroyView -> onDistroy -> onDetach

特别来看一下Fragment比Activity多的几个方法的用处。

onAttach(Activity):当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle):创建该Fragment的视图
onActivityCreated(Bundle):当Activity的onCreate方法返回时调用
onDestoryView():与onCreateView想对应,当该Fragment的视图被移除时调用
onDetach():与onAttach相对应,当Fragment与Activity关联被取消时调用

经测试,除了onCreateView方法,其他的所有方法如果被重写了,我们都得调用它的父类此方法

1. 当一个fragment被创建的时候,它会经历以下状态.

  • onAttach()
  • onCreate()
  • onCreateView()
  • onActivityCreated()

2. 当这个fragment对用户可见的时候,它会经历以下状态。

  • onStart()
  • onResume()

3. 当这个fragment进入“后台模式”的时候,它会经历以下状态。

  • onPause()
  • onStop()

4. 当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。

  • onPause()
  • onStop()
  • onDestroyView()
  • onDestroy() 
  • onDetach()

 静态使用 

静态的使用还是比较简单的,只需要在activity的布局中写几个需要用到的<fragment>标签,然后在标签中android:name=""写上自己的Fragment类的全称即可。

而具体的代码在Fragment中写就好。简易的流程就是:继承Fragment,重写onCreateView方法,通过LayoutInflater加载fragment的布局,拿到返回值的View然后return。那么这个Fragment就算写好了。因为我们在activity布局中声明了这个Fragment类,所以在对应的Activity类中我们并不需要做些什么。只需要在onCreate方法中通过setContent把activity的布局文件加载进去就好。其他的业务逻辑在Fragment类中处理。那么这个我们的Fragment其实就可以当做比较复杂的控件来使用了。  

(动态使用方式下文有补充)

不过,今天在敲Fragment的时候,遇到了尴尬的小情况,是关于v4包和app包下的fragment的区别,实际上之前从来没深究过这个问题,今天特地记下来。

首先我是习惯性在fragment类中继承v4下的Fragment类,然后在布局中添加相关fragment标签,可是在运行时报出了这样的错误Binary XML file line #8: Error inflating class fragment并提示说that is not a Fragment

瞎捣鼓了很久才发现是v4和app捣的鬼,当把fragment中的继承换为app包下的Framgent后,程序可以正常运行。

这就促使了我去了解一下这俩个包下的Fragment的区别。

关于这两个Fragment使用<fragment>标签的问题
 (1)app.fragment和v4.fragment都是可以使用<fragment>标签的:
     如果是app.fragment,那么加载fragment的activity则没有什么特殊的地方继承Activity即可。
 (2)当v4.fragment使用<fragment>标签的时候就需要注意:
那么加载fragment的activity必须继承FragmentActivity,否则就会报错。

其他的不同点也有,比如最常见的就是兼容问题.....(本彩笔觉得,兼容这个年代不是问题了吧...)

app包中的fragment,是在3.0之后才有的。

android.support.v4.app.Fragment:可以兼容到1.6的版本。


(2016年8月11补充基础使用)


不得不承认自己真的很菜。关于Fragment的动态加载,一上午都在纠结一个问题。实际上问题出在自身基础不熟练。

在Activity中的layout里动态添加一个Fragment时,layout的里的空间一直写的事<fragment>所以一直报错(这个标签用于静态加载时)。换成<xxxxxLayout>就行了(一般使用FrameLayout,也就是用正常的布局控件,而不是fragment控件。

虽然浪费了很多时间,但是有一个关setArguments(bundle)的收获。

关于动态加载的用法:

FragmentManager fm = getFragmentManager();fm.beginTransaction().add(R.id.test_frag, new FragmentTest()).commit();
这里的R.id.test_frag 其实就是activity布局中的一个布局控件的id。也就是你想让Fragment出现的地方
一般使用FrameLayout。
更多的时候我们使用的是替换方法:
fm.beginTransaction().replace的方式替换Fragment。用法依然同上,第一个参数是id,第二个是Fragment
类。然后就是调用commit提交方法。一个简单的应用,我们可以通过这种方式制作底部导航栏...当然现阶段有很多
解决方案可以做底部导航栏。这里只是一个思路...
(后文有补充,关于使用官方API做底部导航)

关于setArguments(bundle):

这个newInstance是系统提供的静态工厂模式方法。这里的效果是在使用Fragment时通过静态的newInstance方法初始化Fragment,并且传入自己想要传递的值。那么为什么不适用new Fragment的方法在构造方法中传值呢?

public static Fragment newInstance(String text){    Bundle bundle=new Bundle();    bundle.putString(ARGUMENT,text);    FragmentTest fragmentTest=new FragmentTest();    fragmentTest.setArguments(bundle);    return fragmentTest;}

在动态加载的时候通过newInstance(String text)加载Fragment并且传入想更新UI的数据,在onCreateView内可以随意取出。如果通过构造方法传,是拿不到值的,因为源码中Fragment的启动方法是使用的无参构造方法,所以我们在构造方法中传值其实并没有被调用。

提供静态工厂而不是使用默认构造函数的原因:

fragmnet经常会被销毁重新实例化,Android framework只会调用fragment无参的构造函数。在系统自动实例化fragment的过程中,你没有办法干预。一些需要外部传入的参数来决定的初始化就没有办法完成。使用静态工厂方法,将外部传入的参数可以通过Fragment.setArgument保存在它自己身上,这样我们可以在Fragment.onCreate(...)调用的时候将这些参数取出来。


2016年12月6日补充-官方API实现底部导航:

首先是activity的布局文件:

<?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">    <FrameLayout        android:id="@+id/frg_content"        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1"/>    <!--必须写,官方要求,而且id写法必须是@android:id/tabcontent,写法很死,id名都得固定-->    <android.support.v4.app.FragmentTabHost        android:id="@android:id/tabhost"        android:layout_width="match_parent"        android:background="@color/white"        android:layout_height="wrap_content">        <FrameLayout            android:id="@android:id/tabcontent"            android:layout_width="0dp"            android:layout_height="0dp"           />    </android.support.v4.app.FragmentTabHost></LinearLayout>
此处写法比较蛋疼,就得这么写,而且最下面的那个<FrameLayout>没什么卵用,但是不写还不行,而且id还必须那么写...而且宽和高不整成0,它还会把真正的<FrameLayout>给挤掉...

回到Actvity之中:

private FragmentTabHost mTabHost;    private LayoutInflater layoutInflater;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        layoutInflater=LayoutInflater.from(this);        /**         * 这里可以这么理解:         *      TabHost就是底部导航栏的长条集合,而它的内部类TabSpec就是具体的按钮         */        mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);        mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);        //这里构造方法传递的值并不会对实际效果产生影响,其实和下文的TabHost.TabSpec重名        TabHost.TabSpec one=mTabHost.newTabSpec("one");        /**         * R.layout.fth_tabspec_item         * 就是底部按钮的布局文件,自己想要啥样的就整成啥样的。         * 我这里是一个ImageView和一个TextView竖直排列         */        View oneView= layoutInflater.inflate(R.layout.fth_tabspec_item,null);        ImageView iv= (ImageView) oneView.findViewById(R.id.iv_fth);        //这里是一个图片选择selector        iv.setBackgroundResource(R.drawable.tab_icon_select);        TextView tv= (TextView) oneView.findViewById(R.id.tv_fth);        tv.setText("One!");        one.setIndicator(oneView);        TabHost.TabSpec two=mTabHost.newTabSpec("one");        View twoView= layoutInflater.inflate(R.layout.fth_tabspec_item,null);        ImageView iv_= (ImageView) twoView.findViewById(R.id.iv_fth);        iv_.setBackgroundResource(R.drawable.tab_icon_select);        TextView tv_= (TextView) twoView.findViewById(R.id.tv_fth);        tv_.setText("Two!");        two.setIndicator(twoView);        //给导航按钮绑定你所要跳转的Fragment,具体有啥实现效果直接在Fragment中写就好        mTabHost.addTab(one,OneFragment.class, null);        mTabHost.addTab(two,ThreeFragment.class, null);    }

这里边涉及到的相关文件源码:

R.drawable.tab_icon_select:

(相关用法,参看http://blog.csdn.net/wjzj000/article/details/50937031)

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <!-- btn_ok就是一个图片 -->    <item android:state_focused="true" android:drawable="@drawable/btn_ok"/>    <item android:state_selected="true" android:drawable="@drawable/btn_ok"/>    <item  android:drawable="@drawable/ic_launcher"/></selector>
R.layout.fth_tabspec_item:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="wrap_content"    android:gravity="center"    android:layout_height="wrap_content">    <ImageView        android:id="@+id/iv_fth"        android:layout_width="40dp"        android:layout_height="40dp" />    <TextView        android:id="@+id/tv_fth"        android:layout_width="wrap_content"        android:textColor="@color/text_color_select"        android:layout_height="wrap_content" /></LinearLayout>

结合布局文件和最终效果比较好理解。



2 0
原创粉丝点击