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

先扯两句

首先呢,要先跟大家道个歉,之前结束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工程的从零开始》目录

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容