作者:李旺成###
時間:2016年4月7日###
AndroidStudyDemo 系列篇章開始了!!!
AndroidStudyDemo 你值得擁有
AndroidStudyDemo之Android4.x介紹
AndroidStudyDemo之Android5.x新API介紹(一)
AndroidStudyDemo之Android5.x新API介紹(二)
今天給大家介紹一下我的 AndroidStudyDemo 下的 Android4Study 中的相關內容,先看下面的動態圖,演示了該 Demo 中所包含的內容。
提示:該 Demo 會以單獨項目的的形式提供,并且會集成到 AndroidStudyDemo 項目當中(周末),你可以根據自己的需要選取。
下面我將闡述一下該 Demo 中所涉及的知識點。
目錄
一、 Switch
二、 Space
三、 PopupMenu
四、 GlidLayout
五、 TextureView
一、Switch
Switch 是一個可以在兩種狀態切換的開關控件,它只有兩個狀態:開和關;和 CheckBox 挺像。
Google 的 Nexus 系列的升級到 4.x(汗,就是原生系統),在系統設置頁面可以看到很多打開設備都使用了該控件。以前羨慕 iOS 上提供了漂亮的 UISwitch控件,OK,Google 也為廣大 Android 程序員提供了類似的開關(別高興的太早,設計師們會告訴你,我要的不是這個效果~~哈哈,我們早已習慣了不是,但是不妨礙我們自己用...)。
來看看效果圖:
Switch 類關系圖
學習一個新控件的時候,我的習慣是先看看該控件的繼承結構(在 AndroidStudio 光標置類名上,點擊 F4 按鈕即可查看該類的繼承結構)。為什么?如果該控件是繼承自你熟悉的控件,那簡單了,看看是不是多加了幾個屬性就好了;如果不是,那可能就需要花點時間來熟悉下了。好了,不多說這些,上圖:
Button,熟悉吧!CompoundButton,這個不是很眼熟,那好來看看下面這個段代碼:
mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO
}
});
沒錯 CheckBox 的 setOnCheckedChangeListener 用的就是 CompoundButton 中的 OnCheckedChangeListener 接口,CompoundButton 出現了,還和 CheckBox 扯上了關系。好吧,這個時候要么去看看 CompoundButton,要么去看看 CheckBox,看你喜好。我選熟悉的 —— CheckBox,如下圖:
Switch 的簡單使用
這下清晰了吧!CheckBox 和 Switch 是兄弟。好了繞了這么一大圈,其實只是想分享下我一般怎么去學習一個新控件。
既然是兄弟,那應該很像,那直接當成 CheckBox 試一下啊!好,試一下設置 textXXX 相關的屬性,直接看提示有哪些 textXXX 屬性:
上圖用紅色框圈出來的有點面生,看下 CheckBox 的,類比學習嘛,上圖:
上面那幾個在 CheckBox 中沒有,嗯,看著字面上就是“關的文本”和“開的文本”的意思(方法二:猜),實踐出真知,多簡單的事。上代碼:
<Switch
android:id="@+id/switch_test1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="開"
android:textOff="關"/>
好了,布局文件搞定了,來監聽下事件:
this.mTest1Switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// TODO
}
});
毫無違和感,和 CheckBox 一樣。看到這里應該清楚了,這就是 CheckBox 的另一個版本,做成了開關的樣式而已,多加了幾個屬性,用法和 CheckBox 基本類似。
Switch 自定義樣式
android:thumb : 設置滑塊圖片
android:thumbTextPadding : 設置背景圖片和文字之間的間距,可用來控制 Switch 整體寬度
android:track : 設置背景圖
提示:等你高高興興的把自己的 .9 圖都設置上去后來看效果,怎么看都感覺不對勁。為什么?高度好像有點不聽使喚,layout_height 和 minHeight 齊上,還是不管用。好吧!這不是本文重點(咱就用原生的,要不就自己去實現一個,網上多的是仿 iOS 開關的自定義控件)
好了,Switch 的介紹到這里了。
二、Space
Space 是一個空白控件,那有什么用 —— 占位。要效果圖,好吧,上圖:
Space 類關系圖
繼承結構很簡單,看圖:
對,就是繼承自 View,熟悉吧!但是,這回沒轍了,View 基本上算是所有控件的基類了,這回看你怎么類比學習。確實,龍生九子各有不同,甚至很多都不像龍了。那就不類比了,直接點擊進去看看,看什么是個問題 —— 這還用說,當然是看 draw() 方法,上圖:
坑!空方法,什么都沒有。對了,就是什么都沒有,所以才高效(沒什么要 draw 的當然高效了)。因為 Space 的定位就是空白控件,哥們就是用來占位的,不要想多了。
Space 的簡單使用
侮辱我的智商嗎?好吧,為了隊形整齊,看代碼吧:
<Space
android:id="@+id/space_test1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#ff00ff"/>
background,這是幾個意思,別介,只是驗證一下,哥們就是個空白控件...
好了,Space 結束。
三、PopupMenu
PopupMenu 是彈出式菜單,它會在指定組件上方或下方彈出菜單。
PopupMenu 有沒有感覺和 PopupWindow 名字上很像,先看看效果圖:
PopupMenu 類關系圖
繼承自 Object,這個更熟悉了,但是沒用...
那在看看它的基友 PopupWindow,上圖:
沒轍了,只能看文檔了,看下 PopupMenu 的官方文檔:
沒什么可說的,先看看怎么讓它顯示出來。
PopupMenu 的簡單使用
使用 PopupMenu 創建菜單的步驟:
- 調用new PopupMenu(Context context,View anchor)創建下拉菜單,anchor代表要激發該彈出菜單的組件。
- 調用MenuInflater的inflate()方法將菜單資源填充到PopupMenu中。
- 調用PopupMenu的show()方法顯示彈出式菜單。
看代碼,源碼之下,一切都無所遁形:
// 創建 PopupMenu
private void createPopupMenuByCode() {
mPopupMenu1 = new PopupMenu(this, findViewById(R.id.btn_popupmenu1));
Menu menu = mPopupMenu1.getMenu();
// 通過代碼添加菜單項
menu.add(Menu.NONE, MENU_ITEM_COPY_ID, 0, "唐僧");
menu.add(Menu.NONE, MENU_ITEM_PASTE_ID, 1, "孫悟空");
}
private void createPopupMenuFromXML() {
mPopupMenu2 = new PopupMenu(this, findViewById(R.id.btn_popupmenu2));
Menu menu = mPopupMenu2.getMenu();
// 通過XML文件添加菜單項
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.popupmenu, menu);
}
private void createPopupMenuFromMixture() {
mPopupMenu3 = new PopupMenu(this, findViewById(R.id.btn_popupmenu3));
Menu menu = mPopupMenu3.getMenu();
// 通過代碼添加菜單項
menu.add(Menu.NONE, MENU_ITEM_COPY_ID, 0, "唐僧");
menu.add(Menu.NONE, MENU_ITEM_PASTE_ID, 1, "孫悟空");
// 通過XML文件添加菜單項
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.popupmenu, menu);
}
// 顯示 PopupMenu
private void showPopupMenu1() {
mPopupMenu1.show();
}
private void showPopupMenu2() {
mPopupMenu2.show();
}
private void showPopupMenu3() {
mPopupMenu3.show();
}
很簡單吧,這里就不和 PopupWindow 對比了,沒什么意思,也不是本文重點,PopupMenu 介紹到這里了。項目源碼在文末會給出 GitHub 地址。
四、GlidLayout
GridLayout 是網格布局,為解決嵌套而生,將布局以行和列進行分割。同樣的先看效果圖:
上圖將網格的行列顯示的很清晰,對人如其名(錯了,控件如其名),這貨就是個網格布局,使用虛細線將布局劃分為行,列和單元格——支持一個控件在行,列上都有交錯排列。(參考自:廖煜嶸——“Android 4.0新增Space及GridLayout初談”)
前面說了,GridLayout 是為了解決嵌套而生(嵌套,不用說了吧!不好,不僅影響性能,還增加了布局文件的復雜度),如果僅僅是這個可能還體現不了 GlidLayout 的強大。拿 LinearLayout 開刀(誰讓它最容易寫出嵌套,不找它找誰),在一些復雜的布局中,有時會遇到這樣一個問題 —— 在 LinearLayout 中不能同時在 X,Y 軸方向上進行控件的對齊。
如下圖描述了這個缺點(引自:廖煜嶸——“Android 4.0新增Space及GridLayout初談”):
這里,當 Email address 這個標簽的文本發生變化時,既要保持跟其右邊控件的下部的基線對齊,又要保持跟下面的控件的右邊緣對齊,而用嵌套布局的方式是不能實現的,因為不能夠同時在 X,Y 軸上進行控件的對齊。于是我們便需要引入新的布局方式 GridLayout。(引自:廖煜嶸——“Android 4.0新增Space及GridLayout初談”)
GlidLayout 類關系圖
不出意料,沒有繼承自常見的那五大布局,那只能是 ViewGroup 了。來看看官方文檔:
一眼掃去,加了幾個行列相關的屬性,那就試試吧!
GlidLayout 簡單使用
簡易計算器布局
先看效果圖:
布局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alignmentMode="alignBounds"
android:columnCount="4"
android:orientation="horizontal"
android:rowCount="5">
<!-- 第一行 -->
<Button
android:id="@+id/one"
android:text="1"/>
<Button
android:id="@+id/two"
android:text="2" />
<Button
android:id="@+id/three"
android:text="3" />
<Button
android:id="@+id/devide"
android:text="/" />
<!-- 第二行 -->
<Button
android:id="@+id/four"
android:text="4" />
<Button
android:id="@+id/five"
android:text="5" />
<Button
android:id="@+id/six"
android:text="6" />
<Button
android:id="@+id/multiply"
android:text="×"
android:layout_gravity="fill" />
<!-- 第三行 -->
<Button
android:id="@+id/seven"
android:text="7" />
<Button
android:id="@+id/eight"
android:text="8" />
<Button
android:id="@+id/nine"
android:text="9" />
<Button
android:id="@+id/minus"
android:text="-"
android:layout_gravity="fill"/>
<!-- 第四行 -->
<Button
android:id="@+id/zero"
android:layout_columnSpan="2"
android:layout_gravity="fill"
android:text="0" />
<Button
android:id="@+id/point"
android:text="." />
<Button
android:id="@+id/plus"
android:layout_gravity="fill"
android:layout_rowSpan="2"
android:text="+" />
<!-- 第五行 -->
<Button
android:id="@+id/equal"
android:layout_columnSpan="3"
android:layout_gravity="fill"
android:text="=" />
</GridLayout>
簡單解釋一下
GridLayout 提供了和 LinearLayout 類似的 API(降低使用門檻,它們也算兄弟不是),只是布局的思路不大一樣,GridLayout 有點像屏幕顯示的原理,占用不同的單元格來形成不同的圖案(當然只是簡單的類比一下,不要較真,為了便于理解而已)。
GridLayout的布局策略簡單分為以下三個部分:
首先,它與LinearLayout布局一樣,也分為水平和垂直兩種方式,默認是水平布局,一個控件挨著一個控件從左到右依次排列,但是通過指定android:columnCount 設置列數的屬性后,控件會自動換行進行排列。另一方面,對于 GridLayout 布局中的子控件,默認按照 wrap_content 的方式設置其顯示,這只需要在 GridLayout 布局中顯式聲明即可。
其次,若要指定某控件顯示在固定的行或列,只需設置該子控件的android:layout_row 和 android:layout_column 屬性即可,但是需要注意:android:layout_row=”0”表示從第一行開始,android:layout_column=”0”表示從第一列開始,這與編程語言中一維數組的賦值情況類似。
最后,如果需要設置某控件跨越多行或多列,只需將該子控件的android:layout_rowSpan 或者 layout_columnSpan 屬性設置為數值,再設置其 layout_gravity 屬性為 fill 即可,前一個設置表明該控件跨越的行數或列數,后一個設置表明該控件填滿所跨越的整行或整列。
(參考自:李響——"淺談android4.0開發之GridLayout布局")
LinearLayout 之殤
上面說了,LinearLayout 容易嵌套(那你不會用 RelativeLayout啊,“劇情”需要,不要較真),而且不能同時在X,Y軸方向上進行控件的對齊。好,GridLayout 閃亮登出的時刻到了,先看效果圖:
直接上代碼:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alignmentMode="alignBounds"
android:columnCount="4"
android:columnOrderPreserved="false"
android:useDefaultMargins="true">
<!-- 第一行跨4列 -->
<TextView
android:layout_columnSpan="4"
android:layout_gravity="center_horizontal"
android:text="@string/text_emailsetup"
android:textSize="22sp" />
<!-- 第二行跨4列 -->
<TextView
android:layout_columnSpan="4"
android:layout_gravity="left"
android:text="@string/text_emailsetup_tip"
android:textSize="14sp" />
<!-- 第三行一列 -->
<TextView
android:layout_gravity="right"
android:text="@string/text_emailaddress_tip" />
<!-- 第三行二列 -->
<EditText android:ems="8" />
<!-- 第四行一列 -->
<TextView
android:layout_column="0"
android:layout_gravity="right"
android:text="@string/text_emailpassword_tip" />
<!-- 第四行二列 -->
<EditText android:ems="6" />
<!-- 第五行跨三列 -->
<Space
android:layout_row="4"
android:layout_column="0"
android:layout_columnSpan="3"
android:layout_gravity="fill" />
<!-- 第六行四列 -->
<Button
android:layout_row="5"
android:layout_column="3"
android:text="@string/text_nextstep" />
</GridLayout>
看到 layout_row、layout_column 和 layout_columnSpan,有沒有覺得和 TabLayout 有點像,對,在這兩個布局中這些屬性的作用是相似的,看看注釋,自己再去試試,相信這沒什么難的。(Android 4.0新增Space及GridLayout初談 中有較詳細的說明,在這我就不贅述了)
GridLayout 的布局方向
在 GridLayout 中,默認的布局是水平方向的,即將控件從左到右,從上到下進行排列,比如下圖中的文本“1-1”即放置在第1行第1列中,以此類推。
要使用垂直的布局,很簡單,和 LinearLayout 一樣,它也有 orientation 屬性,設置為“vertical”即可,注意查看控件排列的變化。
直接看圖:
GridLayout 中子控件 layout_gravity 和 gravity 使用
要實現控件間的對齊,可以通過設置android:layout_gravity="fill_horizontal",來,看看效果:
使用android:layout_gravity="fill",可以使子控件伸展充滿被包含的父控件,看效果:
GridLayout 中子控件的 xxxWeight 屬性
xxxWeight,與 LinearLayout 中子控件的 layout_weight 屬性功能類似,可以實現行或列平均分配,上圖:
一切都在代碼里,你懂的:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gl_gridlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="4"
android:rowCount="7">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#eee"
android:text="0"
android:textColor="#000"
android:textSize="50sp"
android:layout_columnSpan="4" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clear"
android:layout_columnSpan="4" />
<Button
android:id="@+id/one"
android:text="1"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/two"
android:text="2"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/three"
android:text="3"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/devide"
android:text="/"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/four"
android:text="4"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/five"
android:text="5"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/six"
android:text="6"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/multiply"
android:text="×"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/seven"
android:text="7"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/eight"
android:text="8"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/nine"
android:text="9"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/minus"
android:text="-"
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/zero"
android:text="0"
android:layout_columnSpan="2"
android:layout_columnWeight="1"
android:layout_gravity="fill"
android:layout_rowWeight="1" />
<Button
android:id="@+id/point"
android:text="."
android:layout_columnWeight="1"
android:layout_rowWeight="1" />
<Button
android:id="@+id/plus"
android:text="+"
android:layout_columnWeight="1"
android:layout_rowSpan="2"
android:layout_rowWeight="1" />
<Button
android:id="@+id/equal"
android:text="="
android:layout_width="match_parent"
android:layout_columnSpan="4"
android:layout_rowWeight="1" />
</GridLayout>
好了,GridLayout 的介紹就到這里了。
五、TextureView
TextureView 作為 SurfaceView 的兄弟,可彌補其不足。老規矩了,先上圖:
如果你想顯示一段在線視頻或者任意的數據流比如視頻或者 OpenGL 場景,你可以用 android 中的 TextureView 做到。
TextureView 的兄弟 SurfaceView
應用程序的視頻或者opengl內容往往是顯示在一個特別的UI控件中:SurfaceView。SurfaceView 的工作方式是創建一個置于應用窗口之后的新窗口。這種方式的效率非常高,因為 SurfaceView 窗口刷新的時候不需要重繪應用程序的窗口(Android 普通窗口的視圖繪制機制是一層一層的,任何一個子元素或者是局部的刷新都會導致整個視圖結構全部重繪一次,因此效率非常低下,不過滿足普通應用界面的需求還是綽綽有余),但是 SurfaceView 也有一些非常不便的限制。
因為 SurfaceView 的內容不在應用窗口上,所以不能使用變換(平移、縮放、旋轉等)。也難以放在 ListView 或者 ScrollView 中,不能使用 UI 控件的一些特性比如 View.setAlpha()。
為了解決這個問題 Android 4.0 中引入了 TextureView。
(原諒我吧,上述內容引自: Android TextureView 簡易教程,該作者確實說得不錯,我沒什么好補充的了,哈哈,還有個原因就是有點累了,偷個懶)
TextureView 類關系圖
看看繼承結構,如下圖:
繼承自 View,那沒什么說的,看看它的兄弟 SurfaceView,見圖:
還真是好兄弟,一個“德性”。好吧,那看看文檔:
(Tip:一個文檔還節選,別說屏幕不夠長,就是不愿折騰...呵呵)
不多說了,官方文檔上直接給了示例代碼,OK,那就用它的代碼試試(試試又不會**不是)。
TextureView 的簡單使用
TextureView 可以像一般的 View 一樣執行一些變換操作,設置透明度等。這里為了演示方便,我使用了兩個下拉列表來改變透明度和變換角度,看代碼吧:
public class TextureViewDemoActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
private static final String TAG = "TextureViewDemoActivity";
private Spinner mSPSetAlpha;
private Spinner mSPSetRotation;
private TextureView mTTVTest;
private Camera mCamera;
private Float[] mAlphaValueArr = {0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
private Float[] mRotationValueArr = {15.0f, 30.0f, 45.0f, 60.0f, 90.0f};
private float mAlphaValue = 1.0f;
private float mRotationValue = 90.0f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_textureviewdemo);
initView();
initData();
initListener();
}
private void initView() {
this.mSPSetAlpha = (Spinner) this.findViewById(R.id.sp_setalphavalue);
this.mSPSetRotation = (Spinner) this.findViewById(R.id.sp_setrotationvalue);
this.mTTVTest = (TextureView) this.findViewById(R.id.ttv_test);
}
private void initData() {
ArrayAdapter<Float> setAlphaAdapter = new ArrayAdapter<Float>(this,android.R.layout.simple_list_item_1, mAlphaValueArr);
setAlphaAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
this.mSPSetAlpha.setAdapter(setAlphaAdapter);
ArrayAdapter<Float> setRotationAdapter = new ArrayAdapter<Float>(this,android.R.layout.simple_list_item_1, mRotationValueArr);
setRotationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
this.mSPSetRotation.setAdapter(setRotationAdapter);
}
private void initListener() {
this.mTTVTest.setSurfaceTextureListener(this);
this.mSPSetAlpha.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mAlphaValue = mAlphaValueArr[position];
mTTVTest.setAlpha(mAlphaValue);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
this.mSPSetRotation.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mRotationValue = mRotationValueArr[position];
mTTVTest.setRotation(mRotationValue);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mCamera = Camera.open();
try {
mCamera.setPreviewTexture(surface);
} catch (IOException t) {
Log.e(TAG, t.getMessage());
}
mCamera.startPreview();
mTTVTest.setAlpha(mAlphaValue);
mTTVTest.setRotation(mRotationValue);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
}
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
布局很簡單,這里就不貼了!
這里只是一個簡單的用法展示,具體的使用中有沒有坑,還未知,你要是遇到了,請告訴我。
推薦:一個不用翻墻的在線 Android 文檔網站:踏得網
總結到這,講的比較簡單,你要是感覺有用,那就幫忙點個喜歡,祝好...
附錄:
Andorid4.x 新控件介紹思維導圖
Andorid4.x 新控件介紹思維導圖.png
參考:
http://blog.csdn.net/pku_android/article/details/7343258
http://blog.csdn.net/pku_android/article/details/7343258
http://blog.csdn.net/xerophyte000/article/details/46045041
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1213/2153.html
http://tech.it168.com/a2011/1122/1277/000001277274.shtml
http://blog.csdn.net/zhangzeyuaaa/article/details/40711047
http://www.tuicool.com/articles/Abu6ji