Android使用Fragment打造万能页面切换框架
首先我们来回忆一下传统用activity进行的页面切换,activity之间切换,首先需要新建intent对象,给该对象设置一些必须的参数,然后调用startactivity方法进行页面跳转。如果需要activity返回结果,则调用startactivityforresult方法,在onactivityresult方法中获得返回结果。此外,每一个要展示的activity需要在androidmanifest.xml文件中注册。而且,如果在某些特定的情况下(比如65536方法数爆炸)要动态加载dex,还得手动管理activity的生命周期。那么,有没有这么一种方法进行页面切换时,无需在androidmanifest.xml文件中声明这些信息,动态加载时又无需我们管理生命周期,等等优点呢。
我们来回忆一下,在android3.0之后,谷歌出了一个fragment,这个东西依赖于activity,其生命周期由宿主activity进行管理,并且可以通过fragmentmanager和fragmenttransaction等相关的类进行管理。那么我们能不能从fragment入手,打造一个完全由fragment组成的页面跳转框架呢。
使用fragment其实很简单,首先开启一个事务,通过add,replace,remove等方法进行添加,替换,移除等操作,这一切的操作可能需要依赖一个容器,这个容器提供一个id,进行对应操作时将这个id作为参数传入。之后通过相应方法提交事务就可以了,就像这样子。
- fragmentmanager fragmentmanager = getsupportfragmentmanager();
- fragmenttransaction fragmenttransaction = fragmentmanager.begintransaction();
- fragmenttransaction.replace(r.id.fragment_container, fragment);
- fragmenttransaction.commit();
然而我相信你一定有这样的经历,在使用fragment进行页面切换时又得不断用代码控制其显示与隐藏的逻辑,那么有没有这样一种方法在程序中不断复用这段代码呢?
首先,我们希望fragment能像activity那样,进行正确的跳转。那么需要什么,答案是fragment对象,我们肯定需要它的class全类名,当然跳转的时候可能会带上一些参数,这个参数应该通过bundle进行传递。而且,全类名可能太长,不便记忆,我们参考web的架构,应该还要取一个别名alias。就这样,一个fragment页面的三个基本属性就被我们抽取出来了,组成了如下的实体类。在这个实体类中,页面传递的参数为json形式的string字符串对象,在需要使用的时候我们通过该json构造出bundle。页面名变量mname是整个程序唯一标示该页面的参数,其值唯一,但是其对应的class全类名可以不唯一,也就是说从name到class的映射可以一对多。
public class corepage implements serializable { private static final long serialversionuid = 3736359137726536495l; private string mname; //页面名 private string mclazz; //页面class private string mparams; //传入参数,json object结构 public corepage(string name, string clazz, string params) { mname = name; mclazz = clazz; mparams = params; } public string getclazz() { return mclazz; } public void setclazz(string clazz) { mclazz = clazz; } public string getname() { return mname; } public void setname(string name) { mname = name; } public string getparams() { return mparams; } public void setparams(string params) { mparams = params; } @override public string tostring() { return "page{" + "mname='" + mname + '\'' + ", mclazz='" + mclazz + '\'' + ", mparams='" + mparams + '\'' + '}'; } }
实体类编写好了,为了更方便的进行页面跳转,我们需要像activity那样,有一个配置文件,里面存着fragment名到其全类名的映射关系。那么这些数据存在哪呢。我们参考网络数据,一般从网络上获取的数据有两种格式,一种是json,一种是xml,json由于其优点,在网络传输中被大量使用,这里,我们优先使用json,选定了json之后,就要选定一个json解析的框架,我们不使用android系统自带的,我们使用阿里的fastjson,当然你也可以使用gson或者jackson。我们的fragment有很多,所以这个fragment的配置文件应该是一个json数组。就像这个样子
[ { "name": "test1", "class": "cn.edu.zafu.corepage.sample.testfragment1", "params": { "param1": "value1", "param2": "value2" } }, { "name": "test2", "class": "cn.edu.zafu.corepage.sample.testfragment2", "params": "" } ]
有了这个配置,我们就要在程序进入时读取这个配置。我们将这个配置放在assets目录下,当然你也可以放在其他目录下,只有你能读取到就行,甚至你可以放在压缩包里。然而,实际情况下,这个文件不应该暴露,因为一旦暴露就存在风险。因此,大家可以采用更安全的方式存这些数据,比如把数据压缩到压缩包中,增加一个密码,而读取文件的内容的代码我们移到native中取实现,毕竟java层太容易被反编译了,而c/c++层相对来说会毕竟有难度。
这里为了简单,我们暂时放在assets目录下,那么要从assets目录中读取这个文件内容就必须有这么一个读取该目录下文件的函数,该目录在android中也算是一个比较特殊的目录了,可以通过getassets()函数,然后获得一个输入流,将文件内容读出,然后将对应的json解析出来就可以了。
/** * 从assets目录下读取文件 * * @param context 上下文 * @param filename 文件名 * @return */ private string readfilefromassets(context context, string filename) { string result = ""; try { inputstreamreader inputreader = new inputstreamreader(context.getresources().getassets().open(filename)); bufferedreader bufreader = new bufferedreader(inputreader); string line = ""; while ((line = bufreader.readline()) != null) result += line; } catch (exception e) { e.printstacktrace(); } return result; }
然后根据该文件内容读取json配置。读取出来后需要将这些数据保存下来。因此要有一个数据结构来保存这个对象,存完之后还要方便取出,存取的依据应该是fragment的表示,即前面提到的name,因此map这个数据结构是最适合不过了。
private map<string, corepage> mpagemap = new hashmap<string, corepage>(); //保存page的map
将配置读取出来存进该map,读取的时候判断name和class是否为空,为空则跳过。
/** * 从配置文件中读取page */ private void readconfig() { log.d(tag, "readconfig from json"); string content = readfilefromassets(mcontext, "page.json"); jsonarray jsonarray = json.parsearray(content); iterator<object> iterator = jsonarray.iterator(); jsonobject jsonpage = null; string pagename = null; string pageclazz = null; string pageparams = null; while (iterator.hasnext()) { jsonpage = (jsonobject) iterator.next(); pagename = jsonpage.getstring("name"); pageclazz = jsonpage.getstring("class"); pageparams = jsonpage.getstring("params"); if (textutils.isempty(pagename) || textutils.isempty(pageclazz)) { log.d(tag, "page name is null or pageclass is null"); return; } mpagemap.put(pagename, new corepage(pagename, pageclazz, pageparams)); log.d(tag, "put a page:" + pagename); } log.d(tag, "finished read pages,page size:" + mpagemap.size()); }
此外,除了从配置文件中读取,我们应该可以动态添加,对外提供这个函数。
/** * 新增新页面 * * @param name 页面名 * @param clazz 页面class * @param params 页面参数 * @return 是否新增成功 */ public boolean putpage(string name, class<? extends basefragment> clazz, map<string, string> params) { if (textutils.isempty(name) || clazz == null) { log.d(tag, "page name is null or pageclass is null"); return false; } if (mpagemap.containskey(name)) { log.d(tag, "page has already put!"); return false; } corepage corepage = new corepage(name, clazz.getname(), buildparams(params)); log.d(tag, "put a page:" + name); return true; } /** * 从hashmap中得到参数的json格式 * * @param params 页面map形式参数 * @return json格式参数 */ private string buildparams(map<string, string> params) { if (params == null) { return ""; } string result = json.tojsonstring(params); log.d(tag, "params:" + result); return result; }
文章开头已经说了,页面跳转的参数是json形式的字符串,我们还要这么一个函数,能够根据json字符串构造出一个bundle
/** * 根据page,从pageparams中获得bundle * * @param corepage 页面 * @return 页面的参数 */ private bundle buildbundle(corepage corepage) { bundle bundle = new bundle(); string key = null; object value = null; if (corepage != null && corepage.getparams() != null) { jsonobject j = json.parseobject(corepage.getparams()); if (j != null) { set<string> keyset = j.keyset(); if (keyset != null) { iterator<string> ite = keyset.iterator(); while (ite.hasnext()) { key = ite.next(); value = j.get(key); bundle.putstring(key, value.tostring()); } } } } return bundle; }
以上配置读取的一系列函数,构成了页面管理类corepagemanager,我们对其应用单例模式。
/** * 跳转页面管理 */ public class corepagemanager { private volatile static corepagemanager minstance = null; //单例 private context mcontext; //context上下文 /** * 构造函数私有化 */ private corepagemanager() { } /** * 获得单例 * * @return pagemanager */ public static corepagemanager getinstance() { if (minstance == null) { synchronized (corepagemanager.class) { if (minstance == null) { minstance = new corepagemanager(); } } } return minstance; } /** * 初始化配置 * * @param context 上下文 */ public void init(context context) { try { mcontext = context.getapplicationcontext(); readconfig(); } catch (exception e) { e.printstacktrace(); } } }
其中init函数暴露给程序入口,进行配置文件的读取。一般放在application的子类的oncreate方法中即可。
到这里为止,基本上我们一切已经就绪了。就差如何切换了。这里,在corepagemanager类中再提供两个核心函数,用于处理页面切换。
下面这个函数是页面切换的核心函数,首先根据参数从map中拿到对应的实体类,假设存在这个页面,通过class名用反射获得该fragment对象,调用前面写好的创建bundle的函数得到页面参数,并与当前函数中的入参bundle进行合并。将参数设置给fragment对象,开启一个fragment事务,查找id为fragment_container的fragment容器,如果该容器已经有fragment,则隐藏它,如果该函数传递了动画参数,则添加页面切换动画,然后将反射获得的fragment对象添加到该容器中,如果需要添加到返回栈,则调用addtobackstack,最后提交事务并返回该fragment对象。
整个函数很简单,就是我们平常在activity中写的切换fragment的代码
/** * 页面跳转核心函数之一 * 打开一个fragemnt * * @param fragmentmanager fragmentmanager管理类 * @param pagename 页面名 * @param bundle 参数 * @param animations 动画类型 * @param addtobackstack 是否添加到返回栈 * @return */ public fragment openpagewithnewfragmentmanager(fragmentmanager fragmentmanager, string pagename, bundle bundle, int[] animations, boolean addtobackstack) { basefragment fragment = null; try { corepage corepage = this.mpagemap.get(pagename); if (corepage == null) { log.d(tag, "page:" + pagename + " is null"); return null; } fragment = (basefragment) class.forname(corepage.getclazz()).newinstance(); bundle pagebundle = buildbundle(corepage); if (bundle != null) { pagebundle.putall(bundle); } fragment.setarguments(pagebundle); fragment.setpagename(pagename); fragmenttransaction fragmenttransaction = fragmentmanager.begintransaction(); if (animations != null && animations.length >= 4) { fragmenttransaction.setcustomanimations(animations[0], animations[1], animations[2], animations[3]); } fragment fragmentcontainer = fragmentmanager.findfragmentbyid(r.id.fragment_container); if (fragmentcontainer != null) { fragmenttransaction.hide(fragmentcontainer); } fragmenttransaction.add(r.id.fragment_container, fragment, pagename); if (addtobackstack) { fragmenttransaction.addtobackstack(pagename); } fragmenttransaction.commitallowingstateloss(); //fragmenttransaction.commit(); } catch (exception e) { e.printstacktrace(); log.d(tag, "fragment.error:" + e.getmessage()); return null; } return fragment; }
而上面这个函数中的id值在一个基础的布局中,之后的fragment都会添加到该布局中去。我们的基类activity也将使用这个布局,这个后续编写baseactivity的时候会提到
<?xml version="1.0" encoding="utf-8"?> <framelayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="fill_parent" android:layout_height="fill_parent" > </framelayout>
此外,我们再提供一个核心函数。就是如果返回栈中存在了目标fragment,则将其弹出,否则新建fragment打开。
/** * 页面跳转核心函数之一 * 打开一个fragement,如果返回栈中有则出栈,否则新建 * * @param fragmentmanager fragmentmanager管理类 * @param pagename 页面别名 * @param bundle 参数 * @param animations 动画 * @return 成功跳转到的fragment */ public fragment gotopage(fragmentmanager fragmentmanager, string pagename, bundle bundle, int[] animations) { log.d(tag, "gotopage:" + pagename); fragment fragment = null; if (fragmentmanager != null) { fragment = fragmentmanager.findfragmentbytag(pagename); } if (fragment != null) { fragmentmanager.popbackstackimmediate(pagename, 0); } else { fragment = this.openpagewithnewfragmentmanager(fragmentmanager, pagename, bundle, animations, true); } return fragment; }
细心的你可能已经注意到了页面跳转函数中用到了动画,其实这个动画是一个数组,为了方便使用,我们将其封装为枚举类,提供常见的几种动画形式。
package cn.edu.zafu.corepage.core; /** * 页面切换动画类别 */ public enum coreanim { none, /* 没有动画 */ present, /*由下到上动画 */ slide,/* 从左到右动画 */ fade;/*渐变 */ }
之后我们还要根据该枚举类获得对应的动画的xml文件。
/** * 动画转化,根据枚举类返回int数组 * * @param coreanim * @return */ public static int[] convertanimations(coreanim coreanim) { if (coreanim == coreanim.present) { int[] animations = {r.anim.push_in_down, r.anim.push_no_ani, r.anim.push_no_ani, r.anim.push_out_down}; return animations; } else if (coreanim == coreanim.fade) { int[] animations = {r.anim.alpha_in, r.anim.alpha_out, r.anim.alpha_in, r.anim.alpha_out}; return animations; } else if (coreanim == coreanim.slide) { int[] animations = {r.anim.slide_in_right, r.anim.slide_out_left, r.anim.slide_in_left, r.anim.slide_out_right}; return animations; } return null; }
这里贴出一个alpha_in.xml中的代码,其他文件类似,这些文件都位于res/anim目录下
<?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@android:integer/config_mediumanimtime" android:fromalpha="0.0" android:toalpha="1.0" />
到了这里,如果你都明白了,那么后面基本上就没有什么难度了,因为之后的功能都是基于以上内容。
前面我们定义了一个corepage实体类用于保存配置文件中实体类的信息,而页面切换过程中需要传递一些参数,比如是否添加到fragment返回栈,是否在新的activity中打开fragment,页面切换时的动画,传递的参数等等,通样,我们将其封装为实体类。由于该对象可能需要通过intent传递,这里我们将其实现parcelable接口。实现该接口方法很简单,假设使用的是android studio,使用快捷键alt+insert选择parcelable即可创建一个模板,我们将其补齐就好了。整个类如下,我们对外提供了多个重载的构造函数,其本质都是一样的,而前面的动画转换函数我们将其放入这个类中。
/** * 页面跳转控制参数 */ public class coreswitchbean implements parcelable { public static final parcelable.creator<coreswitchbean> creator = new parcelable.creator<coreswitchbean>() { @override public coreswitchbean createfromparcel(parcel in) { return new coreswitchbean(in); } @override public coreswitchbean[] newarray(int size) { return new coreswitchbean[size]; } }; private string mpagename; //页面名 private bundle mbundle; //相关数据 private int[] manim = null; //动画类型 private boolean maddtobackstack = true; //是否添加到栈中 private boolean mnewactivity = false; //是否起新的activity private int requestcode = -1; //fragment跳转 public coreswitchbean(string pagename) { this.mpagename = pagename; } public coreswitchbean(string pagename, bundle bundle) { this.mpagename = pagename; this.mbundle = bundle; } public coreswitchbean(string pagename, bundle bundle, coreanim coreanim) { this.mpagename = pagename; this.mbundle = bundle; this.setanim(coreanim); } public void setanim(coreanim anim) { manim = convertanimations(anim); } /** * 动画转化,根据枚举类返回int数组 * * @param coreanim * @return */ public static int[] convertanimations(coreanim coreanim) { if (coreanim == coreanim.present) { int[] animations = {r.anim.push_in_down, r.anim.push_no_ani, r.anim.push_no_ani, r.anim.push_out_down}; return animations; } else if (coreanim == coreanim.fade) { int[] animations = {r.anim.alpha_in, r.anim.alpha_out, r.anim.alpha_in, r.anim.alpha_out}; return animations; } else if (coreanim == coreanim.slide) { int[] animations = {r.anim.slide_in_right, r.anim.slide_out_left, r.anim.slide_in_left, r.anim.slide_out_right}; return animations; } return null; } public coreswitchbean(string pagename, bundle bundle, int[] anim) { this.mpagename = pagename; this.mbundle = bundle; this.manim = anim; } public coreswitchbean(string pagename, bundle bundle, coreanim coreanim, boolean addtobackstack) { this.mpagename = pagename; this.mbundle = bundle; this.setanim(coreanim); this.maddtobackstack = addtobackstack; } public coreswitchbean(string pagename, bundle bundle, int[] anim, boolean addtobackstack) { this.mpagename = pagename; this.mbundle = bundle; this.manim = anim; this.maddtobackstack = addtobackstack; } public coreswitchbean(string pagename, bundle bundle, coreanim coreanim, boolean addtobackstack, boolean newactivity) { this.mpagename = pagename; this.mbundle = bundle; this.setanim(coreanim); this.maddtobackstack = addtobackstack; this.mnewactivity = newactivity; } public coreswitchbean(string pagename, bundle bundle, int[] anim, boolean addtobackstack, boolean newactivity) { this.mpagename = pagename; this.mbundle = bundle; this.manim = anim; this.maddtobackstack = addtobackstack; this.mnewactivity = newactivity; } public coreswitchbean(string pagename, bundle bundle, int[] anim, boolean addtobackstack, boolean newactivity, int requestcode) { this.mpagename = pagename; this.mbundle = bundle; this.manim = anim; this.maddtobackstack = addtobackstack; this.mnewactivity = newactivity; this.requestcode = requestcode; } protected coreswitchbean(parcel in) { mpagename = in.readstring(); mbundle = in.readbundle(); int[] a = {in.readint(), in.readint(), in.readint(), in.readint()}; manim = a; maddtobackstack = in.readint() == 1 ? true : false; mnewactivity = in.readint() == 1 ? true : false; requestcode = in.readint(); } public string getpagename() { return mpagename; } public void setpagename(string pagename) { mpagename = pagename; } public boolean isnewactivity() { return mnewactivity; } public void setnewactivity(boolean newactivity) { mnewactivity = newactivity; } public boolean isaddtobackstack() { return maddtobackstack; } public void setaddtobackstack(boolean addtobackstack) { maddtobackstack = addtobackstack; } public int[] getanim() { return manim; } public void setanim(int[] anim) { manim = anim; } public bundle getbundle() { return mbundle; } public void setbundle(bundle bundle) { mbundle = bundle; } public int getrequestcode() { return requestcode; } public void setrequestcode(int requestcode) { this.requestcode = requestcode; } @override public string tostring() { return "switchbean{" + "mpagename='" + mpagename + '\'' + ", mbundle=" + mbundle + ", manim=" + arrays.tostring(manim) + ", maddtobackstack=" + maddtobackstack + ", mnewactivity=" + mnewactivity + ", requestcode=" + requestcode + '}'; } @override public int describecontents() { return 0; } @override public void writetoparcel(parcel out, int flags) { if (mpagename == null) { mpagename = ""; } if (mbundle == null) { mbundle = new bundle(); } if (manim == null) { int[] a = {-1, -1, -1, -1}; manim = a; } out.writestring(mpagename); mbundle.writetoparcel(out, flags); if (manim != null && manim.length == 4) { out.writeint(manim[0]); out.writeint(manim[1]); out.writeint(manim[2]); out.writeint(manim[3]); } else { out.writeint(-1); out.writeint(-1); out.writeint(-1); out.writeint(-1); } out.writeint(maddtobackstack ? 1 : 0); out.writeint(mnewactivity ? 1 : 0); out.writeint(requestcode); } }
该类中的部分属性有一些默认值,比如是否添加到返回栈,是否起新activity,我们默认在当前activity中打开fragment,并且添加到返回栈。有了这个类,之后的页面切换都通过该实体类进行传参就可以了。
然后,我们定义一个接口,让基类activity实现该接口,用于切换时的一些常用操作。fragment中调用宿主activity中该接口的方法即可。
/** * 页面跳转接口,用于控制页面跳转或启动新的activity */ public interface coreswitcher { /** * 返回到前一个页面(只有一个fragment时会关闭activityt) */ void poppage(); /** * fragmenttag 是否在当前顶上activity上的最顶上的fragment * * @param fragmenttag * @return */ boolean isfragmenttop(string fragmenttag); /** * 是否查找到某个page * * @param pagename * @return */ boolean findpage(final string pagename); /** * 跳转到某一个页面。 * * @param bean * @return */ fragment gotopage(coreswitchbean bean); /** * 打开一个新的页面 * * @param bean * @return */ fragment openpage(coreswitchbean bean); /** * 移除当前acitivity不需要的fragment * * @param fragmentlists */ void removeunlessfragment(list<string> fragmentlists); /** * 页面跳转,支持跨activity进行传递数据 * * @param page * @param fragment * @return */ public fragment openpageforresult(final coreswitchbean page, final basefragment fragment); }
到了这里,似乎已经初具模型了,接下来,我们实现该接口。为了保证在子线程中也能调用这些方法,我们需要一个主线程的handler来帮我们完成一部分工作。假设我们已经获得了这个handler。具体细节看下面的代码实现吧,仔细阅读以下不难理解的。
private static list<weakreference<baseactivity>> mactivities = new arraylist<weakreference<baseactivity>>(); //所有activity的引用 private handler mhandler = null; //线程安全的handler private weakreference<baseactivity> mcurrentinstance = null; //当前activity的引用 /** * 弹出页面 */ @override public void poppage() { poporfinishactivity(); //如果只有一个fagment则退出activty } /** * 保证在主线程操作 */ private void poporfinishactivity() { if (this.isfinishing()) { return; } if (this.getsupportfragmentmanager().getbackstackentrycount() > 1) { if (ismainthread()) { this.getsupportfragmentmanager().popbackstackimmediate(); } else { this.mhandler.post(new runnable() { @override public void run() { getsupportfragmentmanager().popbackstackimmediate(); } }); } } else { finishactivity(this, true); } } /** * 是否是主线程 * @return */ private boolean ismainthread() { return thread.currentthread() == this.getmainlooper().getthread(); } /** * 是否位于栈顶 * @param fragmenttag * @return */ @override public boolean isfragmenttop(string fragmenttag) { int size = mactivities.size(); if (size > 0) { weakreference<baseactivity> ref = mactivities.get(size - 1); baseactivity item = ref.get(); if (item != null && item == this) { fragmentactivity activity = item; fragmentmanager manager = activity.getsupportfragmentmanager(); if (manager != null) { int count = manager.getbackstackentrycount(); if (count >= 1) { fragmentmanager.backstackentry entry = manager.getbackstackentryat(count - 1); if (entry.getname().equalsignorecase(fragmenttag)) { return true; } } } } } return false; } /** * 查找fragment * @param pagename * @return */ @override public boolean findpage(string pagename) { int size = mactivities.size(); int j = size - 1; boolean hasfind = false; for (; j >= 0; j--) { weakreference<baseactivity> ref = mactivities.get(j); if (ref != null) { baseactivity item = ref.get(); if (item == null) { log.d(tag, "item is null"); continue; } fragmentmanager manager = item.getsupportfragmentmanager(); int count = manager.getbackstackentrycount(); for (int i = count - 1; i >= 0; i--) { string name = manager.getbackstackentryat(i).getname(); if (name.equalsignorecase(pagename)) { hasfind = true; break; } } if (hasfind) { break; } } } return hasfind; } /** * 弹出并用bundle刷新数据,在onfragmentdatareset中回调 * @param page * @return */ @override public fragment gotopage(coreswitchbean page) { if (page == null) { log.e(tag, "page name empty"); return null; } string pagename = page.getpagename(); if (!findpage(pagename)) { log.d(tag, "be sure you have the right pagename" + pagename); return this.openpage(page); } int size = mactivities.size(); int i = size - 1; for (; i >= 0; i--) { weakreference<baseactivity> ref = mactivities.get(i); if (ref != null) { baseactivity item = ref.get(); if (item == null) { log.d(tag, "item null"); continue; } boolean findinactivity = popfragmentinactivity(pagename, page.getbundle(), item); if (findinactivity) { break; } else { item.finish(); // 找不到就弹出 } } } return null; } /** * 当前activiti中弹fragment * @param pagename * @param bundle * @param findacitivity * @return */ protected boolean popfragmentinactivity(final string pagename, bundle bundle, baseactivity findacitivity) { if (pagename == null || findacitivity == null || findacitivity.isfinishing()) { return false; } else { final fragmentmanager fragmentmanager = findacitivity.getsupportfragmentmanager(); if (fragmentmanager != null) { fragment frg = fragmentmanager.findfragmentbytag(pagename); if (frg != null && frg instanceof basefragment) { if (fragmentmanager.getbackstackentrycount() > 1 && mhandler != null) { mhandler.postdelayed(new runnable() { @override public void run() { fragmentmanager.popbackstack(pagename, 0); } }, 100); } ((basefragment) frg).onfragmentdatareset(bundle);//默认为空实现,用于舒心返回时的页面数据,重写改方法完成数据刷新 return true; } } } return false; } /** * 根据switchpage打开activity * @param page */ public void startactivity(coreswitchbean page) { try { intent intent = new intent(this, baseactivity.class); intent.putextra("switchbean", page); this.startactivity(intent); int[] animations = page.getanim(); if (animations != null && animations.length >= 2) { this.overridependingtransition(animations[0], animations[1]); } } catch (exception e) { e.printstacktrace(); log.e(tag, e.getmessage()); } } /** * 根据switchbean打开fragment * @param page * @return */ @override public fragment openpage(coreswitchbean page) { boolean addtobackstack = page.isaddtobackstack(); boolean newactivity = page.isnewactivity(); bundle bundle = page.getbundle(); int[] animations = page.getanim(); if (newactivity) { startactivity(page); return null; } else { string pagename = page.getpagename(); return corepagemanager.getinstance().openpagewithnewfragmentmanager(getsupportfragmentmanager(), pagename, bundle, animations, addtobackstack); } } /** * 移除无用fragment * @param fragmentlists */ @override public void removeunlessfragment(list<string> fragmentlists) { if (this.isfinishing()) { return; } fragmentmanager manager = getsupportfragmentmanager(); if (manager != null) { fragmenttransaction transaction = manager.begintransaction(); for (string tag : fragmentlists) { fragment fragment = manager.findfragmentbytag(tag); if (fragment != null) { transaction.remove(fragment); } } transaction.commitallowingstateloss(); int count = manager.getbackstackentrycount(); if (count == 0) { this.finish(); } } } /** * 给basefragment调用 * @param page * @param fragment * @return */ @override public fragment openpageforresult(coreswitchbean page, basefragment fragment) { if (page != null) { if (page.isnewactivity()) { log.d(tag,"openpageforresult start new activity-----"+fragment.getpagename()); mfragmentforresult=fragment; mfragmentrequestcode=page.getrequestcode(); startactivityforresult(page); return null; }else{ string pagename=page.getpagename(); bundle bundle=page.getbundle(); int[] animations=page.getanim(); boolean addtobackstack=page.isaddtobackstack(); basefragment frg = (basefragment) corepagemanager.getinstance().openpagewithnewfragmentmanager(getsupportfragmentmanager(), pagename, bundle, animations, addtobackstack); if (frg==null){ return null; } final basefragment opener= fragment; frg.setrequestcode(page.getrequestcode()); frg.setfragmentfinishlistener(new basefragment.onfragmentfinishlistener() { @override public void onfragmentresult(int requestcode, int resultcode, intent intent) { opener.onfragmentresult(requestcode,resultcode,intent); } }); return frg; } }else{ log.d(tag, "openpageforresult.switchbean is null"); } return null; } public void startactivityforresult(coreswitchbean page) { try { intent intent = new intent(this, baseactivity.class); intent.putextra("switchbean", page); intent.putextra("startactivityforresult", "true"); this.startactivityforresult(intent, page.getrequestcode()); int[] animations = page.getanim(); if (animations != null && animations.length >= 2) { this.overridependingtransition(animations[0], animations[1]); } } catch (exception e) { e.printstacktrace(); } } /** * 如果是fragment发起的由fragment处理,否则默认处理 * @param requestcode * @param resultcode * @param data */ @override protected void onactivityresult(int requestcode, int resultcode, intent data) { log.d(tag, "onactivityresult from baseactivity" + requestcode + " " + resultcode); if (mfragmentrequestcode == requestcode && mfragmentforresult != null) { mfragmentforresult.onfragmentresult(mfragmentrequestcode, resultcode, data); } super.onactivityresult(requestcode, resultcode, data); }
除此之外,提供一些函数的重载便于调用以及一些工具函数。
/** * 仅用于接受应用退出广播,程序退出时有机会做一些必要的清理工作 */ private broadcastreceiver mexitreceiver = new broadcastreceiver() { @override public void onreceive(context context, intent intent) { string action = intent.getaction(); if (action.equals(config.action_exit_app)) { log.d(tag,"exit from broadcast"); finish(); } } }; /** * 返回最上层的activity * * @return */ public static baseactivity gettopactivity() { if (mactivities != null) { int size = mactivities.size(); if (size >= 1) { weakreference<baseactivity> ref = mactivities.get(size - 1); if (ref != null) { return ref.get(); } } } return null; } /** * 广播退出时清理activity列表 */ public static void uninit() { if (mactivities != null) { mactivities.clear(); } } /** * 获得当前活动页面名 * @return */ protected string getpagename() { basefragment frg = getactivefragment(); if (frg != null) { return frg.getpagename(); } return ""; } /** * 打开fragment,并设置是否新开activity,设置是否添加到返回栈 * * @param pagename * @param bundle * @param coreanim * @param addtobackstack * @param newactivity * @return */ public fragment openpage(string pagename, bundle bundle, coreanim coreanim, boolean addtobackstack, boolean newactivity) { coreswitchbean page = new coreswitchbean(pagename, bundle, coreanim, addtobackstack, newactivity); return openpage(page); } /** * 打开fragment,并设置是否新开activity,设置是否添加到返回栈 * * @param pagename * @param bundle * @param anim * @param addtobackstack * @param newactivity * @return */ public fragment openpage(string pagename, bundle bundle, int[] anim, boolean addtobackstack, boolean newactivity) { coreswitchbean page = new coreswitchbean(pagename, bundle, anim, addtobackstack, newactivity); return openpage(page); } /** * 打开fragment,并设置是否添加到返回栈 * * @param pagename * @param bundle * @param coreanim * @param addtobackstack * @return */ public fragment openpage(string pagename, bundle bundle, coreanim coreanim, boolean addtobackstack) { coreswitchbean page = new coreswitchbean(pagename, bundle, coreanim, addtobackstack); return openpage(page); } /** * 打开fragment,并设置是否添加到返回栈 * * @param pagename * @param bundle * @param anim * @param addtobackstack * @return */ public fragment openpage(string pagename, bundle bundle, int[] anim, boolean addtobackstack) { coreswitchbean page = new coreswitchbean(pagename, bundle, anim, addtobackstack); return openpage(page); } /** * 打开fragment * * @param pagename * @param bundle * @param coreanim * @return */ public fragment openpage(string pagename, bundle bundle, coreanim coreanim) { coreswitchbean page = new coreswitchbean(pagename, bundle, coreanim); return openpage(page); } /** * 打开fragment * * @param pagename * @param bundle * @param anim * @return */ public fragment openpage(string pagename, bundle bundle, int[] anim) { coreswitchbean page = new coreswitchbean(pagename, bundle, anim); return openpage(page); } /** * 如果当前activity中只有一个activity,则关闭activity,否则父类处理 */ @override public void onbackpressed() { if (this.getsupportfragmentmanager().getbackstackentrycount() == 1) { this.finishactivity(this, true); } else { super.onbackpressed(); } } /** * 如果fragment中处理了则fragment处理否则activity处理 * @param keycode * @param event * @return */ @override public boolean onkeydown(int keycode, keyevent event) { basefragment activefragment = getactivefragment(); boolean ishanlde = false; if (activefragment != null) { ishanlde = activefragment.onkeydown(keycode, event); } if (!ishanlde) { return super.onkeydown(keycode, event); } else { return ishanlde; } } /** * 获得当前活动fragmnet * * @return */ public basefragment getactivefragment() { if (this.isfinishing()) { return null; } fragmentmanager manager = this.getsupportfragmentmanager(); if (manager != null) { int count = manager.getbackstackentrycount(); if (count > 0) { string tag = manager.getbackstackentryat(count - 1).getname(); return (basefragment) manager.findfragmentbytag(tag); } } return null; } /** * 打印,调试用 */ private void printallactivities() { log.d(tag, "------------baseactivity print all------------activities size:" + mactivities.size()); for (weakreference<baseactivity> ref : mactivities) { if (ref != null) { baseactivity item = ref.get(); if (item != null) { log.d(tag, item.tostring()); } } } } /** * 结束activity,设置是否显示动画 * * @param activity * @param showanimation */ private void finishactivity(baseactivity activity, boolean showanimation) { if (activity != null) { activity.finish(); } if (showanimation) { //动画 int[] animations = null; if (activity.mfirstcoreswitchbean != null && activity.mfirstcoreswitchbean.getanim() != null) { animations = activity.mfirstcoreswitchbean.getanim(); } if (animations != null && animations.length >= 4) { overridependingtransition(animations[2], animations[3]); } } }
并在baseactivity的oncreate方法中完成一些初始化工作。
/** * 页面跳转都通过baseactivity 嵌套fragment来实现,动态替换fragment只需要指定相应的参数。 避免activity 需要再manifest中注册的问题。 * 1.管理应用中所有baseactivity 实例。 2.管理baseactivity 实例和fragment的跳转 */ public class baseactivity extends fragmentactivity implements coreswitcher { private static final string tag = baseactivity.class.getsimplename(); private static list<weakreference<baseactivity>> mactivities = new arraylist<weakreference<baseactivity>>(); protected coreswitchbean mfirstcoreswitchbean;//记录首个,用于页面切换 //所有activity的引用 private handler mhandler = null; private weakreference<baseactivity> mcurrentinstance = null; //当前activity的引用 private basefragment mfragmentforresult = null; //forresult 的fragment private int mfragmentrequestcode = -1; //请求码,必须大于等于0 /** * 仅用于接受应用退出广播,程序退出时有机会做一些必要的清理工作 */ private broadcastreceiver mexitreceiver = new broadcastreceiver() { @override public void onreceive(context context, intent intent) { string action = intent.getaction(); if (action.equals(config.action_exit_app)) { log.d(tag,"exit from broadcast"); finish(); } } }; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_base); intent mnewintent = getintent(); //处理新开activity的情况 if (null != savedinstancestate) { loadactivitysaveddata(savedinstancestate); //恢复数据 //需要用注解savewithactivity } mhandler = new handler(getmainlooper()); //获得主线程handler mcurrentinstance = new weakreference<baseactivity>(this); //当前activity弱引用 mactivities.add(mcurrentinstance); //当前activity增加到activity列表中 printallactivities(); //打印所有activity情况 init(mnewintent); //处理新开activity跳转 intentfilter filter = new intentfilter(); filter.addaction(config.action_exit_app); filter.addcategory(intent.category_default); baseapplication.getlocalbroadcastmanager().registerreceiver(mexitreceiver, filter); //注册本地广播,接收程序退出广播 } }
接下来就是处理基类basefragment的问题了,这里贴出该类所有代码,具体请参考注释。
public class basefragment extends fragment { private static final string tag = basefragment.class.getsimplename(); protected activity mactivity; //所在activity private string mpagename; //页面名 private int mrequestcode; //用于startforresult的requestcode private coreswitcher mpagecoreswitcher; //openpageforresult接口,用于传递返回结果 private onfragmentfinishlistener mfragmentfinishlistener; /** * 设置该接口用于返回结果 * @param listener onfragmentfinishlistener对象 */ public void setfragmentfinishlistener(onfragmentfinishlistener listener) { this.mfragmentfinishlistener = listener; } /** * 设置openpageforresult打开的页面的返回结果 * @param resultcode 返回结果码 * @param intent 返回的intent对象 */ public void setfragmentresult(int resultcode, intent intent) { if (mfragmentfinishlistener != null) { mfragmentfinishlistener.onfragmentresult(mrequestcode, resultcode, intent); } } /** * 得到requestcode * @return 请求码 */ public int getrequestcode() { return this.mrequestcode; } /** * 设置requestcode * @param code 请求码 */ public void setrequestcode(int code) { this.mrequestcode = code; } /** * 将activity中onkeydown在fragment中实现, * @param keycode * @param event * @return */ public boolean onkeydown(int keycode, keyevent event) { return false; } /** * 数据设置,回调 * @param bundle */ public void onfragmentdatareset(bundle bundle) { } /** * 弹出栈顶的fragment。如果activity中只有一个fragemnt时,acitivity也退出。 */ public void poptoback() { this.poptoback(null, null); } /** * 如果在fragment栈中找到,则跳转到该fragment中去,否则弹出栈顶 * @param pagename 页面名 * @param bundle 参数 */ public final void poptoback(string pagename, bundle bundle) { coreswitcher coreswitcher = getswitcher(); if (coreswitcher != null) { if (pagename == null) { coreswitcher.poppage(); } else { if (this.findpage(pagename)) { coreswitchbean page = new coreswitchbean(pagename, bundle); coreswitcher.gotopage(page); } else { coreswitcher.poppage(); } } } else { log.d(tag, "pageswitcher null"); } } /** * 得到页面切换switcher,即baseactivity * @return switcher */ public coreswitcher getswitcher() { synchronized (basefragment.this) {// 加强保护,保证pageswitcher 不为null if (mpagecoreswitcher == null) { if (this.mactivity != null && this.mactivity instanceof coreswitcher) { mpagecoreswitcher = (coreswitcher) this.mactivity; } if (mpagecoreswitcher == null) { baseactivity topactivity = baseactivity.gettopactivity(); if (topactivity != null && topactivity instanceof coreswitcher) { mpagecoreswitcher = (coreswitcher) topactivity; } } } } return mpagecoreswitcher; } public void setswitcher(coreswitcher pagecoreswitcher) { this.mpagecoreswitcher = pagecoreswitcher; } /** * 查找fragment是否存在,通过switcher查找 * @param pagename 页面名 * @return 是否找到 */ public boolean findpage(string pagename) { if (pagename == null) { log.d(tag, "pagename is null"); return false; } coreswitcher coreswitcher = getswitcher(); if (coreswitcher != null) { return coreswitcher.findpage(pagename); } else { log.d(tag, "pageswitch is null"); return false; } } /** * 对应fragment是否位于栈顶,通过switcher查找 * @param fragmenttag fragment的tag * @return 是否位于栈顶 */ public boolean isfragmenttop(string fragmenttag) { coreswitcher pagecoreswitcher = this.getswitcher(); if (pagecoreswitcher != null) { return pagecoreswitcher.isfragmenttop(fragmenttag); } else { log.d(tag, "pageswitcher is null"); return false; } } /** * 重新该方法用于获得返回的数据 * @param requestcode 请求码 * @param resultcode 返回结果码 * @param data 返回数据 */ public void onfragmentresult(int requestcode, int resultcode, intent data) { log.d(tag, "onfragmentresult from basefragment:requestcode-" + requestcode + " resultcode-" + resultcode); } /** * 在当前activity中打开一个fragment,并添加到返回栈中 * @param pagename fragemnt 名,在page.json中配置。 * @param bundle 页面跳转时传递的参数 * @param coreanim 指定的动画理性 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @return */ public final fragment openpage(string pagename, bundle bundle, coreanim coreanim) { return this.openpage(pagename, bundle, coreswitchbean.convertanimations(coreanim), true); } /** * 在当前activity中打开一个fragment,并设置是否添加到返回栈 * @param pagename fragemnt 名,在page.json中配置。 * @param bundle 页面跳转时传递的参数 * @param anim 指定的动画农林 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @param addtobackstack 是否添加到用户操作栈中 * @return */ public final fragment openpage(string pagename, bundle bundle, int[] anim, boolean addtobackstack) { return this.openpage(pagename, bundle, anim, addtobackstack, false); } /** * 打开一个fragment并设置是否新开activity,设置是否添加返回栈 * @param pagename fragemnt 名,在page.json中配置。 * @param bundle 页面跳转时传递的参数 * @param anim 指定的动画理性 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @param addtobackstack 是否添加到用户操作栈中 * @param newactivity 该页面是否新建一个activity * @return */ public final fragment openpage(string pagename, bundle bundle, int[] anim, boolean addtobackstack, boolean newactivity) { if (pagename == null) { log.d(tag, "pagename is null"); return null; } coreswitcher coreswitcher = this.getswitcher(); if (coreswitcher != null) { coreswitchbean page = new coreswitchbean(pagename, bundle, anim, addtobackstack, newactivity); return coreswitcher.openpage(page); } else { log.d(tag, "pageswitcher is null"); return null; } } /** * 在当前activity中打开一个fragment,并添加到返回栈中 * * @param pagename fragemnt 名,在page.json中配置。 * @param bundle 页面跳转时传递的参数 * @param anim 指定的动画理性 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @return */ public final fragment openpage(string pagename, bundle bundle, int[] anim) { return this.openpage(pagename, bundle, anim, true); } /** * 在当前activity中打开一个fragment,并设置是否添加到返回栈 * * @param pagename fragemnt 名,在page.json中配置。 * @param bundle 页面跳转时传递的参数 * @param coreanim 指定的动画理性 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @param addtobackstack 是否添加到用户操作栈中 * @return */ public final fragment openpage(string pagename, bundle bundle, coreanim coreanim, boolean addtobackstack) { return this.openpage(pagename, bundle, coreswitchbean.convertanimations(coreanim), addtobackstack, false); } /** * 打开一个fragment并设置是否新开activity,设置是否添加返回栈 * @param pagename fragemnt 名,在page.json中配置。 * @param bundle 页面跳转时传递的参数 * @param coreanim 指定的动画理性 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @param addtobackstack 是否添加到用户操作栈中 * @param newactivity 该页面是否新建一个activity * @return */ public final fragment openpage(string pagename, bundle bundle, coreanim coreanim, boolean addtobackstack, boolean newactivity) { return this.openpage(pagename, bundle, coreswitchbean.convertanimations(coreanim), addtobackstack, newactivity); } /** * @param pagename * @param bundle * @param coreanim * @return */ public fragment gotopage(string pagename, bundle bundle, coreanim coreanim) { return this.gotopage(pagename, bundle, coreanim,false); } /** * 新建或跳转到一个页面(fragment)。找不到pagename fragment时,就新建fragment。找到pagename * fragment时,则弹出该fragement到栈顶上的所有actvity和fragment * * @param pagename fragemnt 名,在在configure.zip 的pagecontext.txt中配置。 * @param bundle 页面跳转时传递的参数 * @param coreanim 指定的动画理性 none/slide(左右平移)/present(由下向上)/fade(fade 动画) * @param newactivity 该页面是否新建一个activity * @return */ public fragment gotopage(string pagename, bundle bundle, coreanim coreanim, boolean newactivity) { coreswitcher pagecoreswitcher = this.getswitcher(); if (pagecoreswitcher != null) { coreswitchbean page = new coreswitchbean(pagename, bundle, coreanim, true, newactivity); return pagecoreswitcher.gotopage(page); } else { log.d(tag, "pageswitcher is null"); return null; } } /** * 打开fragment并请求获得返回值 * @param pagename * @param bundle * @param coreanim * @param requestcode 请求码 * @return */ public final fragment openpageforresult(string pagename, bundle bundle, coreanim coreanim, int requestcode) { return this.openpageforresult(false, pagename, bundle, coreanim, requestcode); } /** * 打开fragment并请求获得返回值,并设置是否在新activity中打开 * @param newactivity * @param pagename * @param bundle * @param coreanim * @param requestcode * @return */ public final fragment openpageforresult(boolean newactivity, string pagename, bundle bundle, coreanim coreanim, int requestcode) { coreswitcher pagecoreswitcher = this.getswitcher(); if (pagecoreswitcher != null) { coreswitchbean page = new coreswitchbean(pagename, bundle, coreanim, true, newactivity); page.setrequestcode(requestcode); return pagecoreswitcher.openpageforresult(page, this); } else { log.d(tag, "pageswitcher is null"); return null; } } @override public void onattach(activity activity) { super.onattach(activity); mactivity = activity; } @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); if (getpagename() != null) { log.d(tag, "====fragment.oncreate====" + getpagename()); } } public string getpagename() { return mpagename; } public void setpagename(string pagename) { mpagename = pagename; } @override public void ondetach() { super.ondetach(); mactivity = null; } //页面跳转接口 public interface onfragmentfinishlistener { void onfragmentresult(int requestcode, int resultcode, intent intent); } }
其实无论baseactivity还是basefragment的原理都是一样的,都是通过接口中的函数进行切换,最终都是调用了我们前面所说的两个核心函数。所谓的难点,其实就是openpageforresult的函数。如果调整函数中指定了新开activity,则直接调用startactivityforresult函数进行跳转,目标activity中为baseactivity,传递一些参数用于识别。
intent intent = new intent(this, baseactivity.class); intent.putextra("switchbean", page); intent.putextra("startactivityforresult", "true");
然后再oncreate中获得intent
intent mnewintent = getintent(); init(mnewintent);
调用了init函数,在里面获得传递的两个参数,如果是startactivityforresult,则对fragment设置回调函数,当我们手动设置了setfragmentresult函数后回调就会被调用,即onfragmentresult函数回调
private void init(intent mnewintent) { try { coreswitchbean page = mnewintent.getparcelableextra("switchbean"); string startactivityforresult = mnewintent.getstringextra("startactivityforresult"); this.mfirstcoreswitchbean = page; if (page != null) { basefragment fragment = null; boolean addtobackstack = page.isaddtobackstack(); string pagename = page.getpagename(); bundle bundle = page.getbundle(); fragment = (basefragment) corepagemanager.getinstance().openpagewithnewfragmentmanager(getsupportfragmentmanager(), pagename, bundle, null, addtobackstack); if (fragment != null) { if ("true".equalsignorecase(startactivityforresult)) { fragment.setrequestcode(page.getrequestcode()); fragment.setfragmentfinishlistener(new basefragment.onfragmentfinishlistener() { @override public void onfragmentresult(int requestcode, int resultcode, intent intent) { baseactivity.this.setresult(resultcode, intent); } }); } } else { this.finish(); } } } catch (exception e) { e.printstacktrace(); log.d(tag, e.getmessage()); this.finish(); } }
最后的最后,也就是整个fragment跳转框架的初始化了,继承application编写一个应用程序类完成初始化。
public class baseapplication extends application { private static localbroadcastmanager mlocalbroadcatmanager; private static context mcontext; private static baseapplication instance; public static context getcontext() { return mcontext; } @override public void oncreate() { super.oncreate(); instance = this; mcontext = this.getapplicationcontext(); corepagemanager.getinstance().init(this); } /** * 发送本地广播退出程序 */ public void exitapp() { intent intent = new intent(); intent.setaction(config.action_exit_app); intent.addcategory(intent.category_default); baseapplication.getlocalbroadcastmanager().sendbroadcast(intent); baseactivity.uninit(); } public static localbroadcastmanager getlocalbroadcastmanager() { if (mlocalbroadcatmanager == null) { mlocalbroadcatmanager = localbroadcastmanager.getinstance(mcontext); } return mlocalbroadcatmanager; } }
完成声明
<manifest package="cn.edu.zafu.corepage" xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowbackup="true" android:label="@string/app_name" > <activity android:name="cn.edu.zafu.corepage.base.baseactivity" android:configchanges="keyboardhidden|orientation|screensize" android:exported="false" android:launchmode="standard" android:screenorientation="portrait" android:theme="@style/baseactivitytheme" android:windowsoftinputmode="adjustunspecified|statehidden" > </activity> </application> </manifest>
之后的一切就会变得特别简单,调用即可。由于我是用android studio建的module,因此直接引用该module即可,然后提供一个程序入口activity,该类继承baseactivity,将该activity声明在manifest文件中,然后我们再也不用新建activity了,后续的页面跳转全都使用fragment来完成。并且可以设置动画类型等一系列的参数。
使用非常简单
openpage("test1",null, coreanim.slide); //打开一个页面,不传递参数第二个传null,第三个参数为动画类型,此方法有重载方法,第四个参数表示是否添加到返回栈,第五个参数表示是否新开activity,一般情况下,只需传递前三个参数即可,而动画类型,处理传递枚举类,还支持自定义的动画,对应的文件参考res/anim目录下的相应文件 openpageforresult("test2",bundle,coreanim.fade,requestcode); //打开一个页面并获得返回结果,之后调用setfragmentresult和poptoback设置结果 setfragmentresult(500, intent); poptoback(); 重写onfragmentresult函数获取返回结果 @override public void onfragmentresult(int requestcode, int resultcode, intent data) { } //这个使用过程同startactivityfor的整个过程
效果图:
以上就是本文的全部内容,希望对大家的学习有所帮助。
推荐阅读
-
Android使用Fragment打造万能页面切换框架
-
Android使用Fragment打造万能页面切换框架
-
Android使用TabLayou+fragment+viewpager实现滑动切换页面效果
-
Android基础之使用Fragment控制切换多个页面
-
Android使用TabLayou+fragment+viewpager实现滑动切换页面效果
-
Android使用Fragment打造万能页面切换框架
-
Android基础之使用Fragment控制切换多个页面
-
Android基础之使用Fragment控制切换多个页面
-
Android BottomTabBar控件的使用 Fragment 页面切换