《一个Android工程的从零开始》-6、base(五) BaseFragment封装

来源:互联网 发布:淘宝海运到美国 编辑:程序博客网 时间:2024/06/04 18:07

先扯两句

首先呢,要先跟大家道个歉,之前结束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自动给生成的一系列方法,当然,如果你也创建一个,应该会比我这个内容更多,因为上面贴出来的已经是经过我筛选过的了,毕竟你让一个喜欢偷懒的人,上来就看那么多的代码,反正我个人是忍不了。
下面就说一下我留下的这些代码都有上面用:

  1. BaseFragment
    构造方法,这里面暂时唯一一个没有用到的方法,留下它的目的只是单纯的为了与下面的newInstance做对比,当然,如果不适用onCreate中的getArgument传递参数的时候,可以用构造方法传参,只不过现在都很少使用了罢了。
  2. newInstance
    这里的作用与上面的构造方法相同,毕竟可以看到,其中就只有一个构造方法罢了,至于为什么当下很流行使用newInstance而不使用构造方法,主要还是为了去耦合。好吧,我也比较讨厌这些看起来特别高大上的专业词汇。就我个人理解,使用它最好的一种玩法就是复用,如果使用构造方法的时候,就只能创建一个对象,而如果将newInstance中创建的对象放在变成全局变量,再做个判空处理,你就会发现,这里我们可以在多场合使用同一个对象,都不用传参数了,甚至连状态都保留了。(以上说明的是对newInstance与构造方法的区别,并不仅限于Fragment)
  3. onCreate
    用于获取argument,也就是从Activity中传递来的参数获取,暂时没有发现其他用处。
  4. onCreateView与onViewCreated(上面代码中没有)以及onDestroyView
    onCreateView是当视图创建前执行,onViewCreated是在视图创建完毕后执行,是一对,一般是用来解析布局的,当然如果看了我上一篇博客,应该会看到其中写的一段,关于ButterKnife.findById在Fragment中运用的,其中的view参数就可以使用这里解析的布局代替,而这个方法的使用就是在onViewCreated中,同时在onViewCreated方法运行之后,才可以对我们的控件做操作,不然很容易出现空指针异常。而onCreateView与onDestroyView这一对的使用则是ButterKnife的绑定与解绑,方法参见前面的代码就可以。
  5. onAttach
    Fragment与Activity绑定时执行的方法,有的时候在Fragment中使用getActivity会报空指针异常,所以这个时候我们就需要onAttach来帮忙了,因为你会看到,它的参数正是Context,只要将它保存起来,在使用的时候直接调用就可以解决这个问题了。不过也正因为这个操作我们在BaseFragment封装时做了操作,所以一般直接使用Context,也就不需要再去调用它了。
  6. onDetach
    与上面的onAttach方法可以算做一对,上面是绑定Activity的方法,onDetach则是解绑时执行的方法,而这个方法中做的操作,就是将我们整个Fragment中使用到的与Activity相关联的参数置空,防止发生OOM。
  7. 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中,也可以等用到时再去封装。

附录

《一个Android工程的从零开始》目录

阅读全文
0 0
原创粉丝点击