《一個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.lxweimin.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閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容