android使用FragmentActivity、Fragment实现tab切换的功能
目前主流的app都在下面有一排按钮来选择切换页面,可以使用Fragment来实现Tab效果来达到目的,要想用Fragment 功能必须先让activity继承FragmentActivity,其原因是里面包含了Fragment运作的FragmentManager接口的实现类 FragmentManagerImpl ,由这个类管理所有Fragment的显示、隐藏,下面我们实现一个有首页和设置两个界面的切换效果
重叠问题描述
在android中应用程序异常结束的可能性是非常大的,比如一键清理,省电功能等。它们会直接就把你的应用给关闭啦,应用非正常关闭后你的应用里的activity都会被回收,但是应用里的Fragment却还存在并没有回收掉,当你再次打开的时候activity会重新创建,创建的时候它会自动找到之前没有销毁的Fragment来加载直接显示到Tab里面。这个时候你的代码初始化里面还有一个创建Fragment的代码,会再创建一次fragment,之前的Fragment就是野的啦没法控制啦,一直就在那显示着。知道啦原因下面说处理方法。
解决重叠问题
重写Activity的onAttachFragment这个方法,它带啦一个参数Fragment就是加载之前已经存在的对象,我们判断下是不是我们对应的tab对象是的话就直接设置上去,然后在创建tab的初始化函数里判断下只在在tab对象是null的时候才去实例化Fragment对象
示例代码
请自己引入android.support.v4 支持库
首先创建两个界面的类继承Fragment,首页类HomeFragment和设置类SetFragment
HomeFragment.java
package com.androidnodesocket; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * Created by Administrator on 2017-10-16. */ public class HomeFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view= inflater.inflate(R.layout.activity_home, container, false); //取xml中的元素方法(跟activity中有一些不一样) // searchbtn = (TextView) view.findViewById(R.id.tv_top_title); return view; } }
SetFragment.java
package com.androidnodesocket; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * Created by Administrator on 2017-10-16. */ public class SetFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view= inflater.inflate(R.layout.activity_set, container, false); return view; } }
布局文件 activity_home.xml和activity_set.xml这两个文件很简单就一个字符串就不贴代码啦自己创建好就行,然后就是创建一个主的框架类来加载这两个页面起个名字 MainFragmentActivity
package com.androidnodesocket; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.view.View; import android.widget.Button; /** * Created by Administrator on 2017-10-16. */ public class MainFragmentActivity extends FragmentActivity { private FragmentManager fmg; private Fragment fmHome,fmSet; private Button btnHome,btnSet; @Override public void onAttachFragment(android.app.Fragment fragment) { super.onAttachFragment(fragment); //下面代码处理系统因为省电或其它原因关闭activity后fragment没有关闭再次创建activity时fragment重叠的问题 if (fmHome == null && fragment instanceof HomeFragment) { fmHome = (HomeFragment) fragment; } else if (fmSet == null && fragment instanceof SetFragment) { fmSet = (SetFragment) fragment; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mainfragment); btnHome = (LinearLayout) findViewById(R.id.nav_home); btnSet = (LinearLayout) findViewById(R.id.nav_set); //初始化管理器 fmg = getSupportFragmentManager(); if(fmHome == null){ fmHome = new HomeFragment(); } if(fmSet==null){ fmSet = new SetFragment(); } //初始化的时候需要显示一个fragment,假设我们显示第二个fragment //向容器中添加或者替换fragment时必须 开启事务 操作完成后 提交事务 FragmentTransaction ft = fmg.beginTransaction(); ft.add(R.id.main_container, fmHome).commit(); } /** * 单击导航调用此方法 * * @param v */ public void clickNav(View v) { int id = v.getId(); FragmentTransaction ft = fmg.beginTransaction(); switch (id) { case R.id.nav_home: if (!fmHome.isAdded()) { ft.hide(fmSet).show(fmHome).add(R.id.main_container, fmHome).commit(); } else { ft.hide(fmSet).show(fmHome).commit(); } break; case R.id.nav_set: if (!fmSet.isAdded()) { ft.hide(fmHome).show(fmSet).add(R.id.main_container, fmSet).commit(); } else { ft.hide(fmHome).show(fmSet).commit(); } break; } } }
布局文件 activity_mainfragment.xml,里面设计好导航的位置,并且还有一个加载界面的容器id为main_container
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#FFFFFF"> <RelativeLayout android:id="@+id/main_container" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@+id/menu_line"></RelativeLayout> <View android:id="@+id/menu_line" android:layout_width="fill_parent" android:layout_height="1dp" android:layout_above="@+id/menu_bottom" android:background="@color/bg_Gray" /> <LinearLayout android:id="@+id/menu_bottom" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="horizontal" android:paddingTop="8dp"> <Button android:id="@+id/nav_home" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:onClick="clickNav" android:text="首页" /> <Button android:id="@+id/nav_set" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:onClick="clickNav" android:text="设置" /> </LinearLayout> </RelativeLayout>
可以啦,下面是效果图
加一个过滤的动画,首先要定义进出的xml特效文件,把这些文件放言 入res/anim文件夹中
in_from_left.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:fromXDelta="-100%p" android:toXDelta="0%p" android:duration="200" /> </set>
in_from_right.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:fromXDelta="100%p" android:toXDelta="0%p" android:duration="200" /> </set>
out_from_left.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:fromXDelta="0%p" android:toXDelta="-100%p" android:duration="200" /> </set>
out_from_right.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator"> <translate android:fromXDelta="0%p" android:toXDelta="100%p" android:duration="200" /> </set>
修改事件处理代码
/** * 单击导航调用此方法 * * @param v */ public void clickNav(View v) { int id = v.getId(); FragmentTransaction ft = fmg.beginTransaction(); switch (id) { case R.id.nav_home: ft.setCustomAnimations(R.anim.in_from_left,0, 0, R.anim.out_from_left); if (!fmHome.isAdded()) { ft.hide(fmSet).show(fmHome).add(R.id.main_container, fmHome).commit(); } else { ft.hide(fmSet).show(fmHome).commit(); } break; case R.id.nav_set: ft.setCustomAnimations(R.anim.in_from_right,0,0, R.anim.out_from_right); if (!fmSet.isAdded()) { ft.hide(fmHome).show(fmSet).add(R.id.main_container, fmSet).commit(); } else { ft.hide(fmHome).show(fmSet).commit(); } break; } }
ft.setCustomAnimations 关于这个函数的参数传递中间的两个直接传0 ,传其它值的话动画就会错乱(是个坑弄啦好久都没弄好)。关于原因请自行百度,谷歌查询