關于標題##
“空布局控件的運用”可能大多數人都沒看懂,就如同我只是知道功能,卻不知道該如何稱呼一樣,再次先表達一下歉意,如果誰知道這部分內容的正確命名格式,歡迎隨時分享。
再扯兩句
雖然不知道該扯什么 ,但還是習慣性的想要要扯兩句。說實話,上一篇博客確實有一些亂,起初想寫的東西實際上并不多,甚至這一篇的內容原計劃都是在上一篇中,只是隨著寫的過程拓展太多,只能把今天的部分拿出來,再寫一篇了(多虧開始的時候我就很機智的把昨天的標題設置成了base(一)而不是base(上),不然后面的標題還真沒法寫了。。。)。
不過我最初的目的也是想寫的老少皆宜,就盡力把自己遇到過的坑,或者自以為學到的點都展現出來,希望新手看的過程不需要過多的自行百度,老人能夠重溫一些早已遺忘的點。
好了,再多廢話就不說了,下面就開始我們繼續BaseActivity之旅。
另外,為了更方便代碼管理,我將碼云上的工程分為了基于ConstraintLayout與仍使用LinearLayout、RelativeLayout配合的兩個工程,如果有興趣的可以對應查看。
正文
正文部分依舊是繼續說明的BaseActivity的創建過程,只不過是這次是進入了java代碼的階段,該階段一共將分為兩個部分:其一是布局相關的部分,是上一篇博客內容的延續;另一部分是其他常用的方法、常量、變量的預設定。
本篇博客將為大家帶來的是布局相關的內容,其他的內容會在后面與大家分享。
布局相關
首先,有一件事需要提前說明一下,本系列博客的題目是《一個Android工程的從零開始》,所以昨天的三種布局展示中,LinearLayout與RelativeLayout單獨使用,在工程中實際上都不科學,因此想了解的可以看一下我的上一篇博客《一個Android工程的從零開始》-2、base(一)上傳的部分是工程中常用的兩者配合的方式。
在今天內容開始之前,還需要做的一件事,就是最后完善一下昨天的布局,其實就是為今天將要使用到的控件加上id(id的功能詳見附錄1,同時附上了一些細節調整),并在下面空白的部分加上一個空的布局控件而已(這個控件要充滿所有空白的部分),至于這個布局控件的用處,下面會有說明。
首先還是先將修改過的布局代碼貼出來。
非ConstraintLayout布局
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:id="@+id/base_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mybaseapplication.base.BaseActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/base_title_layout"
android:layout_width="match_parent"
android:layout_height="50dp"
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="visible" />
</RelativeLayout>
<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>
</ScrollView>
ConstraintLayout布局
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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:id="@+id/base_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".base.BaseActivity">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/base_title"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="@string/title"
android:textColor="@android:color/white"
android:textSize="@dimen/size_20"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<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" />
<ImageView
android:id="@+id/base_right_icon2"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/size_13"
android:src="@mipmap/add"
android:tint="@android:color/white"
android:visibility="gone"
app:layout_constraintRight_toLeftOf="@+id/base_right_icon1" />
<ImageView
android:id="@+id/base_right_icon1"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/size_13"
android:src="@mipmap/more"
android:tint="@android:color/white"
android:visibility="gone"
app:layout_constraintRight_toRightOf="@+id/base_right_text" />
<TextView
android:id="@+id/base_right_text"
android:layout_width="50dp"
android:layout_height="50dp"
android:gravity="center"
android:text="@string/make_sure"
android:textColor="@android:color/white"
android:textSize="@dimen/size_17"
android:visibility="visible"
app:layout_constraintRight_toRightOf="@+id/base_title" />
<android.support.constraint.ConstraintLayout
android:id="@+id/base_main_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="0dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:layout_marginTop="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="@+id/base_back"
app:layout_constraintRight_toRightOf="@+id/base_right_text"
app:layout_constraintTop_toBottomOf="@+id/base_title"
app:layout_constraintVertical_bias="0.0">
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</ScrollView>
強烈建議使用ScrollView內嵌套ConstraintLayout的看一下附錄2。
以上就是在上一篇博客的基礎上修改的布局代碼,使用屬性基本沒變,如果不明白可以看一下上一篇我的上一篇博客(本篇開篇有鏈接)。
下面開始對應布局部分對應的java。
空布局控件的運用
首先,我們先從今天剛剛添加的,填充下面空白部分的布局控件開始吧。
剛創建工程的時候,系統會默認給我們生成一個MainActivity(如果你沒有改名字的話),下面我們就在MainActivity的基礎上進行我們后續的操作,幫助大家完善BaseActivity的同時,也能同時體會到為何要費這么大的力氣去創建一個BaseActivity。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
上面的代碼就是MainActivity生出來就帶著的,無非就是一個類,繼承了AppCompatActivity,這個類與Activity的區別,參見cxm11的AppCompatActivity,然后重寫了onCreate方法。而布局文件中則只有一個簡單的TextView,內容則是經典的不能再經典的“Hello World”。
不過為了方便與BaseActivity配合的效果更明顯的表示,我將TextView的布局更改了一下,充滿父控件,加了背景顏色。至于效果,先保密,一會對比的時候再給大家看。
先給大家看一下我們運用后添加的新布局的,也就是id為base_main_layout布局,下面分別是傳統布局與ConstraintLayout布局對應的方法,
傳統布局
public void setBaseContentView(int layoutId) {
((ScrollView)findViewById(R.id.base_scroll_view)).setFillViewport(true);
LinearLayout layout = (LinearLayout) findViewById(R.id.base_main_layout);
LayoutInflater inflater = getLayoutInflater();
final View view = inflater.inflate(layoutId, null);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
layout.addView(view, params);
}
ConstraintLayout布局
public void setBaseContentView(int layoutId) {
((ScrollView)findViewById(R.id.base_scroll_view)).setFillViewport(true);
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.base_main_layout);
LayoutInflater inflater = getLayoutInflater();
final View view = inflater.inflate(layoutId, null);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
layout.addView(view, params);
}
顯而易見,這兩種布局對應的代碼唯一的區別就是在于base_main_layout對應的控件是LinearLayout還是ConstraintLayout,其他部分都是相同的。
首先說一下方法中的第一行代碼吧,也是最讓我想罵娘的一行代碼。
findViewById(控件id),可以理解為校長點名,叫到誰的ID,就是誰,不過為了出來的結果不過都是這個學校的學生,所以這個時候就需要老師出來強轉一下告訴校長,你點名的這個人是哪個班的(不過比名字多的限定是同一個xml文件中的id只能是唯一的)。所以這就是前面“(ScrollView)”作用,就是將得到的控件裝換成為我們需要的ScrollView。校長為什么找你,肯定有事啊,那就是“.”后面的內容。
這里我不得不吐槽一下ScrollView,放在其內的控件的高度是默認的wrag_content,所以我防止了一個布局文件在其中之后,在程序執行的時候,由于其中什么也沒有,計算了高度是0,再添加布局的時候,由于高度是0,所以就什么都顯示不出來了,具體效果圖,會在后面一起給出。
感謝Caesardadi的《ScrollView子View為自定義View時需要注意的幾點問題》幫了個大忙,沒讓我剛開始博客就夭折了,不然剛開始直接出一個顯示不了的bug,而且還是我最引以為傲的點,后面也就不用寫了。
在Caesardadi的博客中提到了上面我使用的方法
mScrollView.setFillViewport(true); 本方法是使子View可以拉伸來填滿整個屏幕
所以,如此設置之后,我們傳遞來布局就能正常顯示了。
而顯示布局的所使用的方法就是布局的addView,可以將其他的布局添加到當前的布局控件之中,比如上面的方法是添加到LinearLayout中,而第二個方法則是添加到ConstraintLayout中。
而布局傳遞的方式自然是通過的萬能的id,只不過這次傳遞的是layout的id而不再是前面使用的控件的id,至于layout的id去哪里找。
其中的xml的文件名就是id只是要記得將“.xml”后綴去掉
使用getLayoutInflater();獲取布局解析器,執行布局解析器的inflate(解析)方法就可以將布局解析出來,得到的是一個View,其中有兩個參數,分別是布局id以及父控件(傳null)。關于這部分內容,我在些這篇博客的過程中也發現了一個疑問,在最下方的疑問部分的疑問1,如果有哪位大能能夠幫助解答,感激不盡。
拋開疑問先不談,上面的在這部分代碼還是能在正常工作的,如果后面找到答案,也會對應發布到我的博客中。
而這個方法調用時也很簡單,先給大家在看一下原本的MainActivity中的代碼是什么樣的:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
我們在看一下更改之后的:
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setBaseContentView(R.layout.activity_main);
}
}
只需要兩步就搞定了,1、將繼承AppCompatActivity 改為繼承BaseActivity;2、將setContentView改為setBaseContentView,也就是剛剛我們自己寫的方法。
下面我們來看一下效果:
原本的布局:
好吧,這就是我剛剛說的修改后的布局,美觀100%無緣了,好在效果夠明確,下面再看一下使用了BaseActivity之后的是什么樣的呢:
大家看出來變化了沒有,所以只要之后用到這種布局的,就都使用一下使用一下BaseActivity,簡簡單單改兩處地方,就可以輕松實現,不然用時你就需要做一遍我在上一篇博客中所做的操作,新頁面加不停,工作重復不斷,想想都覺得累,你說呢。
或許有人會問,做為什么出來的效果跟我的不一樣,在標題欄上面會多出來一部分內容,就像下圖中紅色框內的部分:
這個部分參見附錄3。
小結結語
以上這么長的篇幅,不知道大家看得如何,為了防止有人對我動刀動槍,臨時決定,頭部的處理就留在下一篇博客繼續了。
別打臉!我也不想,可是隨著寫,總會拓展進來一些新的東西,我想與大家一起進步嗎,仔細一點不是都能多學點東西嘛,嘿嘿嘿,好了,我要趕下一篇博客嘍!敬請期待!
附錄###
附錄1:####
android:id="@+id/XXX":
就好像生出一個控件孩子,你給孩子取的名字,取了名字后自然需要向計生辦報備,區別就在于這個現實中需要我們自己操作的過程,在as中自動在執行了,也就是將這個名字寫入R.java,并未其生成一個int值的對應編號,也就相當于我們的身份證號。之后對控件如何操作,我們就可以使用它的名字XXX來代替了。
android:contentDescription:
這個屬性是之前所沒有注意到的,今天看到報黃才看了一下,是方便一些生理功能有缺陷的人使用應用程序的。比如我們有一個ImageView里面放置一張顏色復雜的圖片,可能一些色弱色盲的人,分不清這張圖片中畫的是什么東西。如果用戶安裝了輔助瀏覽工具比如TalkBack,TalkBack就會大聲朗讀出用戶目前正在瀏覽的內容。TextView控件TalkBack可以直接讀出里面的內容,但是ImageView TalkBack就只能去讀contentDescription的值,告訴用戶這個圖片到底是什么。
android:layout_toLeftOf:
是我依然使用的方法,但是在2.3.2中就已經報黃了,提示我們最好使用android:layout_toStartOf代替,但是替換后會提示將API level升至17,并且,還需要同時設置android:layout_toLeftOf屬性,直到老平臺忽略TextAlignment屬性。所以這里就沒有更換。
android:textSize
這里報黃我也很無奈啊,至于原因和解決方法很簡單,那就是Google規定字體大小統一使用sp作單位,而這里我卻使用了dp做單位。至于為什么,那就要從android的屏幕適配說起了,內容太多,大家自行百度一下dp這個單位是什么含義就能明白了。
附錄2:####
想必來看附錄2的朋友已經體驗到ScrollView內嵌ConstraintLayout是有多惡心了吧,當然,如果你還沒有被惡心到,可以去嘗試一下。
布局代碼的目的就是將用一個布局控件充滿下方的標題下方的空白處,可是ScrollView內的直接子控件默認設置的高度是wrap_content,即便你設置了match_parent也依然如此(這部分在后面java部分也會有影響),所以當我們使用ConstraintLayout玩“拼圖游戲”的時候,就會發現,新加進來的布局將原本的標題的布局都破壞了。
作為一個懶漢,我并沒有去深究造成布局破壞的原因,而是直接將ScrollView注釋掉,在ConstraintLayout中先把布局搞定,然后重新把ScrollView釋放出來即可。
附錄3:####
如果出現上面的問題,只需要修改一下AndroidManifest.xml中的主題就好。
將框內的部分修改成:
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
這樣再運行的時候,就不會有上面的那部分了。
至于AndroidManifest.xml在哪里,還是上圖:
當然,作為懶人怎么可能這么費力去找,我們可以做一下如下操作:
這次是不是不需要我加箭頭,你也能找到在哪里了!
不過,世界的進步是靠懶人推動的,當沒有java文件或者是xml文件打開的時候,可以看到as出現如下的內容
不知道大家有沒有好奇使用過的,而其中第一個就是我要說的,雙擊shift,在出現的提示框中輸入你要搜的文件名即可
顯而易見,我還沒輸入全,它自己就耐不住寂寞蹦出來了,只需要鼠標左擊或者敲回車鍵就可以打開。
附錄4:####
疑問###
疑問1:
解析的部分只能使用final View view = inflater.inflate(layoutId, null);,再將得到的view添加到layout中,而不能直接使用inflater.inflate(layoutId, (ViewGroup) layout, true);或者inflater.inflate(layoutId, (ViewGroup) layout, false);。
代碼如下:
正常顯示:
public void setBaseContentView(int layoutId) {
((ScrollView)findViewById(R.id.base_scroll_view)).setFillViewport(true);
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.base_main_layout);
LayoutInflater inflater = getLayoutInflater();
final View view = inflater.inflate(layoutId, null);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
layout.addView(view, params);
}
非正常顯示:
public void setBaseContentView(int layoutId) {
((ScrollView)findViewById(R.id.base_scroll_view)).setFillViewport(true);
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.base_main_layout);
LayoutInflater inflater = getLayoutInflater();
layout = (ConstraintLayout)inflater.inflate(layoutId, (ViewGroup) layout, true);
}
以及:
public void setBaseContentView(int layoutId) {
((ScrollView)findViewById(R.id.base_scroll_view)).setFillViewport(true);
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.base_main_layout);
LayoutInflater inflater = getLayoutInflater();
layout = (ConstraintLayout)inflater.inflate(layoutId, (ViewGroup) layout, false);
}