先扯两句
首先呢,要先跟大家道个歉,之前结束BaseActivity部分和写ButterK的部分都有些兴奋,博客发出去以后,竟然忘了将代码提交git了,一打开项目,看都是绿色、蓝色的文件,实在是不好意思,已经提交了,有需要的可以去查看了。 我的代码都是随着写博客随着完善的,所以希望今天的最后能够想起来提交,不然。。。大家不要打我,我怕疼。。。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)
正文
Fragment基本方法
前面也提到了,上一篇已经将BaseActivity封装结束了,当然只是一些基本的操作,在后续的过程中,肯定会随着新功能的运用逐步去完善。 而今天我们所研究的内容呢,看到标题大家自然也就明白了,就是BaseFragment的封装,当然,与前一段所说一样,这个部分的内容也是暂时性的一些基础内容,并不是说今天的内容搞定了,以后都不需要进到BaseFragment中做任何修改了的,所以因人而异,因项目而异。 之前都是先分析,再贴代码,今天改改套路,先开个大:
package com.banshouweng.mybaseapplication.base;
import android.content.Context;
import android.net.Uri;
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;
import com.banshouweng.mybaseapplication.R;
/**
* 《一个Android工程的从零开始》
*@author 半寿翁
*@博客:
*@ CSDN http://blog.csdn.net/u010513377/article/details/74455960
*@ 简书 http://www.jianshu.com/p/1410051701fe
*/
public class BaseFragment extends Fragment {
private OnFragmentInteractionListener mListener;
private Unbinder unbinder;
public BaseFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @return A new instance of fragment BaseFragment.
*/
// TODO: Rename and change types and number of parameters
public static BaseFragment newInstance() {
BaseFragment fragment = new BaseFragment();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
unbinder = ButterKnife.bind(this, view);
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_base, container, false);
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
细心的或许会发现,我把包名给改了,填上banshouweng,实在是所以找的demo之类的都有这类的,没办法,网上找的demo都有类似的个人推广,我怎么忍心不给自己也添加一个呢,嘿嘿。
好了,进入正题。
上面呢,是创建一个block fragment后,AS自动给生成的一系列方法,当然,如果你也创建一个,应该会比我这个内容更多,因为上面贴出来的已经是经过我筛选过的了,毕竟你让一个喜欢偷懒的人,上来就看那么多的代码,反正我个人是忍不了。
下面就说一下我留下的这些代码都有上面用:
- BaseFragment
构造方法,这里面暂时唯一一个没有用到的方法,留下它的目的只是单纯的为了与下面的newInstance做对比,当然,如果不适用onCreate中的getArgument传递参数的时候,可以用构造方法传参,只不过现在都很少使用了罢了。 - newInstance
这里的作用与上面的构造方法相同,毕竟可以看到,其中就只有一个构造方法罢了,至于为什么当下很流行使用newInstance而不使用构造方法,主要还是为了去耦合。好吧,我也比较讨厌这些看起来特别高大上的专业词汇。就我个人理解,使用它最好的一种玩法就是复用,如果使用构造方法的时候,就只能创建一个对象,而如果将newInstance中创建的对象放在变成全局变量,再做个判空处理,你就会发现,这里我们可以在多场合使用同一个对象,都不用传参数了,甚至连状态都保留了。(以上说明的是对newInstance与构造方法的区别,并不仅限于Fragment) - onCreate
用于获取argument,也就是从Activity中传递来的参数获取,暂时没有发现其他用处。 - onCreateView与onViewCreated(上面代码中没有)以及onDestroyView
onCreateView是当视图创建前执行,onViewCreated是在视图创建完毕后执行,是一对,一般是用来解析布局的,当然如果看了我上一篇博客,应该会看到其中写的一段,关于ButterKnife.findById在Fragment中运用的,其中的view参数就可以使用这里解析的布局代替,而这个方法的使用就是在onViewCreated中,同时在onViewCreated方法运行之后,才可以对我们的控件做操作,不然很容易出现空指针异常。而onCreateView与onDestroyView这一对的使用则是ButterKnife的绑定与解绑,方法参见前面的代码就可以。 - onAttach
Fragment与Activity绑定时执行的方法,有的时候在Fragment中使用getActivity会报空指针异常,所以这个时候我们就需要onAttach来帮忙了,因为你会看到,它的参数正是Context,只要将它保存起来,在使用的时候直接调用就可以解决这个问题了。不过也正因为这个操作我们在BaseFragment封装时做了操作,所以一般直接使用Context,也就不需要再去调用它了。 - onDetach
与上面的onAttach方法可以算做一对,上面是绑定Activity的方法,onDetach则是解绑时执行的方法,而这个方法中做的操作,就是将我们整个Fragment中使用到的与Activity相关联的参数置空,防止发生OOM。 - OnFragmentInteractionListener与onButtonPressed
这个部分是之前还打算自己写的内容,没想到现在AS已经为我们提出了解决方法。作用就是将Fragment中的参数传递到Activity中,AS生成的这个传递的是Uri,我们在使用的时候,可以自行定义所要传递的内容,同样也可以在接口中写添加其他的抽象方法,而不需要的时候也可以不写。onButtonPressed的目的是告诉我们这个部分的代码应该如何使用,根据自己的需求灵活运用即可。
BaseFragment封装
其实这个部分呢,说起来复杂,可也比较简单,毕竟之前我们已经封装过了BaseActivity,很多的方法都是共通的,所以对应的部分,我们只需要粘贴过来,略作修改即可,所以这里,我就将我们一些有差异的地方写出来,其他的部分,大家看一下代码就好。
方法封装
写BaseActivity的时候,我们是先从布局开始的,为了公平起见,写BaseFragment就从方法封装开始好了。
当然,如果你真的认为我这么安排是什么所谓的公平,那就只能说明你太单纯了,作为我们这么一个懒人,哪有什么时间去考虑什么公平,有那时间睡觉多好。所以这么安排只有一个原因,那就是方法的封装更简单。
不过在方法封装之前,我们有一件事需要处理一下,也就是刚刚onAttach中所说的内容。而且对于这部分,看过我前面博客的应该会了解到,在写BaseActivity方法封装的时候,说到了为什么会专门创建两个对象,Context和Activity,就是为了与Fragment中引用的向统一,至于这两个对象是否还有其他作用就有待我们将来探索了,但是在Fragment中可以肯定是很必要的。
既然必要那就创建之,上代码:
/**
* 用于传递的上下文信息
*/
public Context context;
public Activity activity;
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context.getApplicationContext();
activity = (Activity) context;
}
以上就完成了获取Context和Activity的操作,后面Fragment的运用中很大程度上依赖这两个对象,这部分个人的建议就是,直接将BaseActivity中的方法复制过来,然后就会看到个别报错的地方,然后在调用的方法前面添加上“activity. ”,this判断是否与所绑定的Activity有关,有的替换成activity,没有的替换成context,就完成了方法的封装,例如:
//报错部分
public void startActivity(Class<?> clz) {
startActivity(new Intent(this, clz));
}
//修改之后
public void startActivity(Class<?> clz) {
startActivity(new Intent(context, clz));
}
//修改前
case R.id.base_back:
if (isResetBack) {
onClickBack.clickBack();
} else {
finish();
}
break;
//修改后
case R.id.base_back:
if (isResetBack) {
onClickBack.clickBack();
} else {
activity.finish();
}
break;
这么多代码直接针贴过来就可以,包括网络监听以及进度条的部分,当然,如果实在偷懒,进度条的部分可以不用复制过来,而是在需要使用的Fragment中,使用上面说到的接口OnFragmentInteractionListener直接调用BaseActivity中的进度条。
最后需要说明的一点,个人建议,将BaseActivity在onCreate中执行的方法调用都放置在onViewCreated方法中进行,放置出现没有必要的空指针。
Title
看到标题大家应该知道,前面我们已经将方法封装完成了,对于这种可以偷懒的任务我可是相当喜欢了。
可是很不幸的是,这次需要花些心思的竟然落在Title上,不过好在难度有限,让我这样的懒人很感动啊。
Title的封装
哎,看到Title,我就又想起来第一篇博客里面的那三种情况了,说起来还真是麻烦,还需要考虑那么多因素,重新写一个真心一万个不愿意,或者有人会想到,我们可以直接把BaseActivity布局中的直接复制,贴到BaseFragment的布局,想想也是个方法,可是万一下次再什么地方还需要这个Title布局(虽然我暂时还没想到哪里还会需要),我们还需要去找布局,再复制,对于我这种懒人来说,怎么可能忍得了!
所以就有了上面的标题,那就是Title的封装,你没有看错,布局也是可以封装的,而且也不困难,下面我们就开始。
首先是需要创建一个xml布局,类型当然选择的是Layout,我这里将这个布局很无脑的命名为title_layout,随后将BaseActivity中的Title布局剪切下来粘贴到这个布局中,也就成了这个样子:
<?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="@dimen/title_height"
android:background="@color/colorPrimary">
<ImageView
android:id="@+id/base_back"
android:layout_width="50dp"
android:layout_height="50dp"
android:padding="@dimen/size_13"
android:src="@mipmap/back"
android:tint="@android:color/white" />
<TextView
android:id="@+id/base_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/title"
android:textColor="@android:color/white"
android:textSize="@dimen/size_20" />
<ImageView
android:id="@+id/base_right_icon2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_toLeftOf="@+id/base_right_icon1"
android:contentDescription="@string/second_function_key"
android:padding="@dimen/size_13"
android:src="@mipmap/add"
android:tint="@android:color/white"
android:visibility="gone" />
<ImageView
android:id="@+id/base_right_icon1"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:contentDescription="@string/first_function_key"
android:padding="@dimen/size_13"
android:src="@mipmap/more"
android:tint="@android:color/white"
android:visibility="gone" />
<TextView
android:id="@+id/base_right_text"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:gravity="center"
android:text="@string/make_sure"
android:textColor="@android:color/white"
android:textSize="@dimen/size_17"
android:visibility="gone" />
</RelativeLayout>
可是很显然,剪切下来,那么在BaseActivity中也没有了这个Title布局,这可怎么办呢,下面就让我们见证一下Title疯转的神奇之处吧,在BaseActivity布局之前Title的位置上,写如下的代码:
<include
android:id="@+id/base_title_layout"
layout="@layout/title_layout"></include>
是不是看到之前被我们剪切的Title布局又重新显示出来了?至于include是什么,怎么用,我这种小菜鸟就不在此多言了,还是直接上链接,大家一起去瞻仰一下大神们是如何讲解的,没错就是我们郭霖郭神的Android最佳性能实践(四)——布局优化技巧。当然,打击可以看到优化的部分除了include还有merge以及ViewStub,大家可以看一下郭神的博客自行理解一下,我这里后面的博客应该也会用到这两部分的优化,不过我这种懒人你们懂的,没用到的时候,怎么可能写到自己的博客里,所以这部分只能在后面随缘讲解了。
好了闲言少叙,搞定了BaseActivity,下面我们也该处理一下BaseFragment中的布局了,不过很好运的是由于Fragment也是在Activity的布局中使用的,所以既然我们的BaseActivity外已经 嵌套了一层ScrollView了,所以BaseFragment的布局中就不再需要多此一举了,我们只需要进行其中的布局即可,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/base_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.banshouweng.mybaseapplication.base.BaseFragment">
<include
android:id="@+id/base_title_layout"
layout="@layout/title_layout"></include>
<LinearLayout
android:id="@+id/base_main_layout"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:orientation="vertical"></LinearLayout>
</LinearLayout>
是不是看起来要简洁一些,至此我们的Title的封装也就结束了,当然,也就是include的使用罢了。
Title对应的方法
1、初始化控件
这里需要注意的地方是,偷懒的我把initView改成了使用ButterKnife的样子,所以在BaseActivity中的是如下代码:
/**
* 控件初始化
*/
public void initView() {
baseBack = ButterKnife.findById(activity, R.id.base_back);
baseRightIcon1 = ButterKnife.findById(activity, R.id.base_right_icon1);
baseRightIcon2 = ButterKnife.findById(activity, R.id.base_right_icon2);
baseTitle = ButterKnife.findById(activity, R.id.base_title);
baseRightText = ButterKnife.findById(activity, R.id.base_right_text);
baseTitleLayout = ButterKnife.findById(activity, R.id.base_title_layout);
baseMainLayout = ButterKnife.findById(activity, R.id.base_main_layout);
baseScrollView = ButterKnife.findById(activity, R.id.base_scroll_view);
}
而在BaseFragment切记一定要修改成如下:
/**
* 控件初始化
*/
private void initView() {
baseBack = ButterKnife.findById(currentLayout, R.id.base_back);
baseRightIcon1 = ButterKnife.findById(currentLayout, R.id.base_right_icon1);
baseRightIcon2 = ButterKnife.findById(currentLayout, R.id.base_right_icon2);
baseTitle = ButterKnife.findById(currentLayout, R.id.base_title);
baseRightText = ButterKnife.findById(currentLayout, R.id.base_right_text);
baseTitleLayout = ButterKnife.findById(currentLayout, R.id.base_title_layout);
baseMainLayout = ButterKnife.findById(currentLayout, R.id.base_main_layout);
baseScrollView = ButterKnife.findById(currentLayout, R.id.base_scroll_view);
}
其中的currentLayout是在onCreateView中保存下来的,这么做的主要原因是,如果这里我们使用的是activity,则表示这些代码我们是让ButterKnife去Fragment所绑定的Activity中去寻找控件,而我们的目的却是让其在当前的Fragment中寻找,所以这里不能使用activity,而需要替换成currentLayout。
2、Title方法封装
所有的控件都已经初始化完成,那么我们也该进行方法的封装了,其实说来说去还是那几个方法而已,我们还是只需要复制过来即可,而其调用的方式也与BaseActivity中封装的方法调用的方式完全相同,所以这部分到此也就结束了。
ps:为了防止结束的太过仓促,在结尾的部分,我还是对BaseActivity做一些补充比较好,那就是在BaseActivity中,我们使用了ScrollView,是为了兼容一些分辨率过低的手机,防止页面内容显示不全,可是有一些页面是没有Title的,例如首页就经常不需要使用Title,而之前我想的是,直接使用setContentView方法,所以就没有做对应的处理,却忽略了如果一旦使用了setContentView方法,那么ScrollView的效果就不在了,所以就又添加了一个隐藏头布局的方法:
/**
* 隐藏头布局
*/
public void hideTitle() {
baseTitleLayout.setVisibility(View.GONE);
}
当然,没有确定Fragment中是否需要使用,我们可以先封装到BaseFragment中,也可以等用到时再去封装。
网友评论