《一個Android工程的從零開始》階段總結(jié)與修改1-base

先扯兩句

最近在開發(fā)一個項目,算算時間也有一周沒有繼續(xù)寫《一個android工程的從零開始》了填抬,先跟大家道個歉翻默。當(dāng)然,這段時間真正實(shí)際操作中抒痒,也發(fā)現(xiàn)了自己的Base封裝中有一些Bug幌绍,正好在這次開發(fā)過程中找出來,并且予以改正故响,特此發(fā)一篇博客說明一下傀广,之前博客對應(yīng)的部分會給予提示,部分內(nèi)容就不予以修改了彩届,算是為大家也為自己在出錯的時候提供一個可查找對照的方向伪冰。錯誤的部分為大家使用過程中帶來的不便十分抱歉。
閑言少敘樟蠕,老規(guī)矩還是先上我的Git庫贮聂,然后開始正文。
MyBaseApplication (https://github.com/BanShouWeng/MyBaseApplication)

正文

這部分主要分為BaseActivity封裝修改寨辩、BaseFragment封裝修改吓懈、Retrofit header動態(tài)添加封裝三部分。

BaseActivity封裝修改

BaseActivity布局

BaseActivity中靡狞,布局修改的部分是ScrollView布局的部分耻警,當(dāng)然,這部分使用不會報錯甸怕,只是無法實(shí)現(xiàn)我們要求甘穿,具體會出現(xiàn)什么問題,我創(chuàng)建了一個測試Activity蕾各,布局文件相當(dāng)簡單:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    tools:context="com.iyubatestapplication.ui.TestActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="大家注意看標(biāo)題"
        android:textSize="100sp" />
</LinearLayout>

而java部分也是簡單異常:

public class TestActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setBaseConten tView(R.layout.activity_test);
        setTitle("Test");
    }
}

那么我們來看看效果是什么樣的:

這里寫圖片描述

大家可以看到扒磁,Title部分竟然也隨著滑動了,這明顯不是工程中我們想要的效果式曲。
至于造成這個現(xiàn)象的原因妨托,其實(shí)很簡單缸榛,那就是之前集成的時候,我將ScrollView放在了最外層兰伤,也就是放在了Title的外面内颗,那么ScrollView內(nèi)的都會滑動,自然也就會出現(xiàn)Title隨之滑動的現(xiàn)象了敦腔,既然好的了問題均澳,那么解決起來自然就建簡單了,只要將ScrollView放到Title下面一層不就好了嘛符衔。不過真的這么簡單嗎找前?

這里寫圖片描述

這是京東的布局,如果套用我們剛剛的設(shè)想判族,那么大家思考一下躺盛,下方的工具欄在我們滑動的過程中會出現(xiàn)什么效果?沒錯形帮,就如同上面的Title一樣槽惫,會出現(xiàn)隨著滑動的情況。當(dāng)然辩撑,這是首頁所特有的界斜,想必大多數(shù)APP只會有一個首頁,而且就功能而言合冀,也就是“我的”版塊才會用到ScrollView各薇,而其他版塊基本都會有RecyclerView(或者ListView、GridView君躺、VLayout等)得糜,所以說這個部分我們可以單獨(dú)搭建。
可是晰洒,如果當(dāng)前的Activity中需要創(chuàng)建帶有Title或者底部固定位置布局的Fragment,那么前面Title的問題就會再次出現(xiàn)啥箭,作為一個一直將偷懶作為人生準(zhǔn)則的人谍珊,當(dāng)然想要弄一個都考慮到的情況,所以這部分的布局代碼就被我修改成了這樣一副樣子:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.banshouweng.mybaseapplication.base.BaseActivity">

    <include
        android:id="@+id/base_title_layout"
        layout="@layout/title_layout"></include>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/base_main_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:visibility="gone"></LinearLayout>

        <ScrollView
            android:id="@+id/base_scroll_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"></ScrollView>
    </FrameLayout>
</LinearLayout>

將LinearLayout與ScrollView放在一起急侥,那么就可以想用哪里選哪里砌滞,當(dāng)然,我們的setBaseContentView方法也要做一定的調(diào)整坏怪,成為了兩個方法贝润,setBaseContentView與setBaseScrollContentView,分別對應(yīng)沒有ScrollView和有ScrollView兩種情況:

    /**
     * 引用頭部布局
     *
     * @param layoutId 布局id
     */
    public void setBaseContentView(int layoutId) {
        LinearLayout layout = (LinearLayout) findViewById(R.id.base_main_layout);

        //獲取布局铝宵,并在BaseActivity基礎(chǔ)上顯示
        final View view = getLayoutInflater().inflate(layoutId, null);
        //關(guān)閉鍵盤
        hideKeyBoard();
        //給EditText的父控件設(shè)置焦點(diǎn)打掘,防止鍵盤自動彈出
        view.setFocusable(true);
        view.setFocusableInTouchMode(true);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        layout.addView(view, params);
        layout.setVisibility(View.VISIBLE);
    }

    /**
     * 引用頭部布局且當(dāng)前頁面基于ScrollView
     *
     * @param layoutId 布局id
     */
    public void setBaseScrollContentView(int layoutId) {
        ScrollView layout = (ScrollView) findViewById(R.id.base_scroll_view);

        //當(dāng)子布局高度值不足ScrollView時华畏,用這個方法可以充滿ScrollView,防止布局無法顯示
        layout.setFillViewport(true);

        //獲取布局尊蚁,并在BaseActivity基礎(chǔ)上顯示
        final View view = getLayoutInflater().inflate(layoutId, null);
        //關(guān)閉鍵盤
        hideKeyBoard();
        //給EditText的父控件設(shè)置焦點(diǎn)亡笑,防止鍵盤自動彈出
        view.setFocusable(true);
        view.setFocusableInTouchMode(true);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
        layout.addView(view, params);
        layout.setVisibility(View.VISIBLE);
    }

大家可以看得出來,我這里給方法的注釋是“引用頭部布局”也就是說横朋,以上的所有討論都是基于會引用我們的Title的情況下的仑乌,畢竟如果不使用Title的時候,我們可以直接使用setContentView方法進(jìn)行布局初始化琴锭,當(dāng)然晰甚,看過我之前博客的也會知道,我創(chuàng)建了一個hideTitle方法决帖,用于處理Title的顯隱厕九。不過hideTitle + setBaseContentView就是setContentView,只有這種情況下古瓤,就建議大家直接使用setContentView止剖,而hideTitle + setBaseScrollContentView則相當(dāng)于在在setContentView的基礎(chǔ)上嵌套了一層ScrollView,如果真的有需要落君,這個組合還是可以使用的穿香。
當(dāng)然,如果我們的布局中嵌套了ScrollView的情況下绎速,在使用ListView與GridView的時候皮获,會出現(xiàn)顯示不全的情況等,這個部分想必大家都已經(jīng)清楚了纹冤,而解決方法就是需要我們自定義一個自己的ListView洒宝,一般都為其命名為MyListView,而重寫的方法也很簡單萌京,只需要繼承ListView雁歌,并重寫以下方法即可:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        try {
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                    MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
        } catch (Exception e) {

        }
    }

不過好在我最近使用RecyclerView的時候,故意沒有重寫一下知残,簡單做了個測試靠瞎,并沒有發(fā)現(xiàn)ListView想同的問題,所以暫時就直接用了求妹,如果大家對這部分有什么其他的發(fā)現(xiàn)乏盐,歡迎一起溝通。

ps:在工程開發(fā)過程中制恍,尤其是電商APP的產(chǎn)品瀏覽父能,經(jīng)常會上設(shè)置一鍵返回頭部的按鈕,這個按鈕就很顯然就不適合在ScrollView布局的內(nèi)層净神,所以我們可以考慮將其集成在BaseActivity與Title一層何吝,當(dāng)然記得將父布局改成RelativeLayout溉委,并在下方FrameLayout中添加屬性android:layout_below=“@+id/base_title_layout”

ps的ps:以上建議是在本框架基礎(chǔ)上進(jìn)行的操作,當(dāng)然也可以脫離框架自行編寫一個布局岔霸,而且商品瀏覽一把都是有列表布局的薛躬,因此也不需要使用ScrollView,非要在上下添加其他布局的情況呆细,RecyclerView可以判斷onCreateViewHolder中的viewType做對應(yīng)適配型宝,ListView、GridView通用的方法就是添加Header絮爷,至于RecyclerView添加header的方法趴酣,大家可以看看張鴻洋大神的Android 優(yōu)雅的為RecyclerView添加HeaderView和FooterView,或許會有幫助坑夯。

BaseActivity方法修改####

前面說了布局岖寞,是會影響到我們使用的,下面說的方法柜蜈,呃仗谆,一部分也影響到我們使用,先上方法吧淑履。

    /**
     * 最右側(cè)文本功能鍵設(shè)置方法
     *
     * @param text          文本信息
     * @param clickListener 點(diǎn)擊事件
     * @return 將當(dāng)前TextView返回方便進(jìn)一步處理
     */
    public TextView setBaseRightText(String text, View.OnClickListener clickListener) {
        TextView baseRightText = (TextView) findViewById(R.id.base_right_text);
        baseRightText.setText(text);
        baseRightText.setVisibility(View.VISIBLE);
        baseRightText.setOnClickListener(clickListener);
        return baseRightText;
    }

上面這個方法看過我前面博客的應(yīng)該比較熟悉隶垮,那就是我在最右側(cè)設(shè)置了一個文本功能鍵的使用方法,包括設(shè)置文本秘噪、設(shè)置點(diǎn)擊時間等狸吞,不過若是細(xì)看的話,很簡單就會發(fā)現(xiàn)與之前的不同指煎。

第一:創(chuàng)建的baseRightText從全局變量變成了如今的局部變量蹋偏,也就是說當(dāng)我們不使用這個功能鍵的時候,雖然解析布局的時候依然會解析base_right_text對應(yīng)id的TextView至壤,不過至少(TextView) findViewById方法可以偷懶不執(zhí)行了威始。

第二:TextView baseRightText = (TextView) findViewById(R.id.base_right_text),很顯然這并不是ButterKnife的控件綁定方法像街,至于為什么這么寫字逗,主要還是因為我們無法在BaseActivity和集成它的Activity中同時添加ButterKnife.bind(this);,不然會出現(xiàn)如下錯誤:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.banshouweng.mybaseapplication/com.banshouweng.mybaseapplication.ui.activity.MainActivity}: java.lang.IllegalStateException: Required view 'address_list' with ID 2131427418 for field 'addressList' was not found. If this view is optional add '@Nullable' (fields) or '@Optional' (methods) annotation.
...
Caused by: java.lang.IllegalStateException: Required view 'address_list' with ID 2131427418 for field 'addressList' was not found. If this view is optional add '@Nullable' (fields) or '@Optional' (methods) annotation.

原因就是在集成BaseActivity的Activity(以MainActivity為例)的onCreate宅广,會有super.onCreate(savedInstanceState);方法,也就是說MainActivity的onCreate執(zhí)行之前我們會先執(zhí)行BaseActivity的onCreate方法些举,而BaseActivity中有ButtKnife.bind(this);方法跟狱,看過我之前的博客的會知道,我在BaseActivity中有這么一段代碼:

    if (!(this instanceof MainActivity)) {
            activities.add(this);
    }

先把我查的this是什么貼出來:

this確實(shí)是當(dāng)前activity的指針户魏,它可以傳給Context是因為Activity是Context的一個子類

也就是說當(dāng)MainActivity繼承BaseActivity時驶臊,this就是MainActivity挪挤,所以綁定控件的時候,就會以BaseActivity的布局文件作為參照关翎,而這部分自然是沒有MainActivity中對應(yīng)的布局文件的扛门,自然就會報上述錯誤。
反之纵寝,如果是在MainActivity中添加ButtKnife.bind(this);方法论寨,而不再BaseActivity中添加,我們可以在下圖位置查找到MainActivity_ViewBinding類:

這里寫圖片描述

在其中可以看到:

public class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  @UiThread
  public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;

    target.addressList = Utils.findRequiredViewAsType(source, R.id.address_list, "field 'addressList'", RecyclerView.class);
  }

  @Override
  @CallSuper
  public void unbind() {
    MainActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.addressList = null;
  }
}

也就是說考阱,其中只有我在MainActivity中的addressList控件猾愿,并沒有BaseActivity中的控件猎醇,所以很遺憾,這樣依然會報上述錯誤火焰,因此,在BaseActivity中只能使用TextView baseRightText = (TextView) findViewById(R.id.base_right_text);關(guān)聯(lián)控件與布局文件胧沫。

其三:@return 將當(dāng)前TextView返回方便進(jìn)一步處理
這個部分也比較好理解昌简,有很多情況下,我們需要做界面的復(fù)用绒怨,也就是說右上角的圖片功能鍵纯赎、文本功能鍵都不是固定的,需要修改資源窖逗,或者隱藏掉已經(jīng)顯示出來的功能鍵址否,而如果每次都創(chuàng)建一個對應(yīng)的方法就得不償失了,所以這里我們便將對應(yīng)的功能鍵控件返回到調(diào)用的Activity中碎紊,需要處理的時候佑附,處理對應(yīng)返回的控件即可。

其四:baseRightText.setOnClickListener(clickListener);
這里不再創(chuàng)建對應(yīng)的新接口仗考,而是統(tǒng)一使用OnClickListener音同,當(dāng)然,作為懶人秃嗜,之前我一直懶得記每個對應(yīng)控件的Id权均,不過第三點(diǎn)已經(jīng)說了,我們將對應(yīng)的控件返回了回來锅锨,所以只需要調(diào)用對應(yīng)控件的getId()方法就可以很優(yōu)雅的解決掉我們的偷懶問題叽赊。

BaseFragment封裝修改

前面BaseActivity寫了那么多,作為一個懶人必搞,大家就別指望我在這里也會寫那么多了必指,這里我只總結(jié)了一句話,那就是參見“BaseActivity封裝修改”恕洲,對應(yīng)做修改即可——和諧社會塔橡,不能打人的梅割。

Retrofit header動態(tài)添加封裝

這個部分呢,是最近有這方面需求葛家,所以我就查看了一下户辞,首先還是先感謝zhuhai__yizhiRetrofit添加header參數(shù)的幾種方法。幫了個大忙癞谒。
大家可以通過zhuhai__yizhi了解一下對應(yīng)的三種方法底燎,而作為一個懶人的我,實(shí)在是太懶了扯俱,而且是集成的Retrofit2.3.0的我书蚪,就稍微嘗試了一下,結(jié)果發(fā)現(xiàn)了一個更好的偷懶方法:

//Post
public interface RetrofitPostService {

    @FormUrlEncoded
    @POST("{action}")
    Observable<ResponseBody> postResult(@Path("action") String action, @HeaderMap Map<String, String> headerParams, @FieldMap Map<String, String> params);
}

//Get
public interface RetrofitGetService {
    @GET("{action}")
    Observable<ResponseBody> getResult(@Path("action") String action, @HeaderMap Map<String, String> headerParams, @QueryMap Map<String, String> params);
}

大家應(yīng)該看出來了迅栅,那就是@HeaderMap Map<String, String> headerParams參數(shù)殊校,用法就是Key是字段,Value是我們要傳遞的值读存,當(dāng)然現(xiàn)階段我接觸到的为流,需要傳遞Header的還是比較少的,但是多了解一下總歸是沒有壞處的嘛让簿。

附錄

《一個Android工程的從零開始》- 目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敬察,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子尔当,更是在濱河造成了極大的恐慌莲祸,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椭迎,死亡現(xiàn)場離奇詭異锐帜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)畜号,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門缴阎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人简软,你說我怎么就攤上這事蛮拔。” “怎么了痹升?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵建炫,是天一觀的道長。 經(jīng)常有香客問我疼蛾,道長踱卵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮惋砂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绳锅。我一直安慰自己西饵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布鳞芙。 她就那樣靜靜地躺著眷柔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪原朝。 梳的紋絲不亂的頭發(fā)上驯嘱,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音喳坠,去河邊找鬼鞠评。 笑死,一個胖子當(dāng)著我的面吹牛壕鹉,可吹牛的內(nèi)容都是我干的剃幌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼晾浴,長吁一口氣:“原來是場噩夢啊……” “哼负乡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脊凰,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抖棘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狸涌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體切省,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年杈抢,在試婚紗的時候發(fā)現(xiàn)自己被綠了数尿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惶楼,死狀恐怖右蹦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歼捐,我是刑警寧澤何陆,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站豹储,受9級特大地震影響贷盲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一巩剖、第九天 我趴在偏房一處隱蔽的房頂上張望铝穷。 院中可真熱鬧,春花似錦佳魔、人聲如沸曙聂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宁脊。三九已至,卻和暖如春贤姆,著一層夾襖步出監(jiān)牢的瞬間榆苞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工霞捡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坐漏,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓弄砍,卻偏偏與公主長得像仙畦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子音婶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容