編輯:關於Android編程
創建復合控件可以很好地創建出具有重用功能的控件集合。這種方式通常需要繼承一個合適的ViewGroup,再給它添加指定功能的控件,從而組合成新的復合控件。通過這種方式創建的控件,我們一般會給它指定一些可配置的屬性,讓它具有更強的拓展性。下面就以一個TopBar為示例,講解如何創建復合控件。
我們知道為了應用程序風格的統一,很多應用程序都有一些共通的UI界面,比如下圖中所示的TopBar這樣一個標題欄。
為一個View提供可自定義的屬性非常簡單,只需要在res資源目錄的values目錄下創建一個attrs.xml的屬性定義文件,並在該文件中通過如下代碼定義相應的屬性即可。
我們在代碼中通過標簽聲明了使用自定義屬性,並通過name屬性來確定引用的名稱。最後,通過標簽來聲明具體的自定義屬性,比如在這裡定義了標題文字的字體、大小、顏色,左邊按鈕的文字顏色、背景、字體,右邊按鈕的文字顏色、背景、字體等屬性,並通過format屬性來指定屬性的類型。這裡需要注意的就是,有些屬性可以是顏色屬性,也可以是引用屬性。比如按鈕的背景,可以把它指定為具體的顏色,也可以把它指定為一張圖片,所以使用“|”來分隔不同的屬性—-“reference|color”。
在確定好屬性後,就可以創建一個自定義控件—-TopBar,並讓它繼承自ViewGroup,從而組合一些需要的控件。這裡為了簡單,我們繼承RelativeLayout。在構造方法中,通過如下所示代碼來獲取XML布局文件中自定義的那些屬性,即與我們使用系統提供的那些屬性一樣。
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
系統提供了TypedArray這樣的數據結構來獲取自定義屬性集,後面引用的styleable的TopBar,就是我們在XML中通過所指定的name名。接下來,通過TypedArray對象的getString()、getColor()等方法,就可以獲取這些定義的屬性值,代碼如下所示。
// 通過這個方法,將你在attrs.xml中定義的declare-styleable的所有屬性值存儲到TypedArray中
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
// 從TypedArray中取出對應的值來為要設置的屬性賦值
mLeftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, 0);
mLeftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);
mLeftText = ta.getString(R.styleable.TopBar_leftText);
mRightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, 0);
mRightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);
mRightText = ta.getString(R.styleable.TopBar_rightText);
mTitleTextSize = ta.getDimension(R.styleable.TopBar__titleTextSize, 10);
mTitleTextColor = ta.getColor(R.styleable.TopBar__titleTextColor, 0);
mTitle = ta.getString(R.styleable.TopBar__title);
// 獲取完TypedArray的值後,一般要調用recycle方法來避免重新創建時的錯誤
ta.recycle();
這裡需要注意的是,當獲取完所有的屬性值後,需要調用TypedArray的recycle方法來完成資源的回收。
接下來,我們就可以開始組合控件了。UI模板TopBar實際上由三個控件組成,即左邊的點擊按鈕mLeftButton,右邊的點擊按鈕mRightButton和中間的標題欄mTitleView。通過動態添加控件的方式,使用addView()方法將這三個控件加入到定義的TopBar模板中,並給它們設置我們前面所獲取到的具體的屬性值,比如標題的文字顏色、大小等,代碼如下所示。
// 為創建的組件元素賦值
// 值就來源於我們在引用的xml文件中給對應屬性的賦值
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mRightTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleView.setText(mTitle);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(Gravity.CENTER);
// 為組件元素設置相應的布局元素
mLeftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mLeftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
// 添加到ViewGroup
addView(mLeftButton, mLeftLayoutParams);
mRightLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mRightLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
addView(mRightButton, mRightLayoutParams);
mTitleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mTitleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
addView(mTitleView, mTitleLayoutParams);
·定義接口
在UI模板類中定義一個左右按鈕點擊的接口,並創建兩個方法,分別用於左邊按鈕的點擊和右邊按鈕的點擊,代碼如下所示。
// 接口對象,實現回調機制,在回調方法中
// 通過映射的接口對象調用接口中的方法
// 而不用去考慮如何實現,具體的實現由調用者去創建
public interface topbarClickListener {
// 左按鈕點擊事件
void leftClick();
// 右按鈕點擊事件
void rightClick();
}
·暴露接口給調用者
在模板方法中,為左、右按鈕增加點擊事件,但不去實現具體的邏輯,而是調用接口中相應的點擊方法,代碼如下所示。
// 按鈕的點擊事件,不需要具體的實現,
// 只需調用接口的方法,回調的時候,會有具體的實現
mRightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.rightClick();
}
});
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.leftClick();
}
});
// 暴露一個方法給調用者來注冊接口回調
// 通過接口來獲得回調者對接口方法的實現
public void setOnTopbarClickListener(topbarClickListener mListener) {
this.mListener = mListener;
}
·實現接口回調
在調用者的代碼中,調用者需要實現這樣一個接口,並完成接口中的方法,確定具體的實現邏輯,並使用第二步中暴露的方法,將接口的對象傳遞進去,從而完成回調。通常情況下,可以使用匿名內部類的形式來實現接口中的方法,代碼如下所示。
mTopBar.setOnTopbarClickListener(new MyTopBar.topbarClickListener() {
@Override
public void leftClick() {
Toast.makeText(MainActivity.this,
"left", Toast.LENGTH_SHORT)
.show();
}
@Override
public void rightClick() {
Toast.makeText(MainActivity.this,
"right", Toast.LENGTH_SHORT)
.show();
}
});
這裡為了簡單演示,只顯示兩個Toast來區分不同的按鈕點擊事件。除了通過接口回調的方式來實現動態的控制UI模板,同樣可以使用公共方法來動態地修改UI模板中的UI,這樣就進一步提高了模板的可定制性,代碼如下所示。
/**
* 設置按鈕的顯示與否 通過id區分按鈕,flag區分是否顯示
*
* @param id id
* @param flag 是否顯示
*/
public void setButtonVisable(int id, boolean flag) {
if (flag) {
if (id == 0) {
mLeftButton.setVisibility(View.VISIBLE);
} else {
mRightButton.setVisibility(View.VISIBLE);
}
} else {
if (id == 0) {
mLeftButton.setVisibility(View.GONE);
} else {
mRightButton.setVisibility(View.GONE);
}
}
}
通過如上所示代碼,當調用者通過TopBar對象調用這個方法後,根據參數,調用者就可以了動態地控制按鈕的顯示,代碼如下所示。
// 控制topbar上組件的狀態
mTopBar.setButtonVisable(0, true);
mTopBar.setButtonVisable(1, false);
最後一步,自然是在需要使用的地方引用UI模板,在引用前,需要指定引用第三方控件的名字空間。在布局文件中,可以看到如下一行代碼。
xmlns:android="http://schemas.android.com/apk/res/android"
這行代碼就是在指定引用的名字空間xmlns,即xml namespace。這裡指定了名字空間為“android”,因此在接下來使用系統屬性的時候,才可以使用“android:”來引用Android的系統屬性。同樣地,如果要使用自定義的屬性,那麼就需要創建自己的名字空間,在Android Studio中,第三方的控件都使用如下代碼來引入名字空間。
xmlns:custom="http://schemas.android.com/apk/res-auto"
這裡我們將引入的第三方控件的名字空間取為custom,之後再XML文件中使用自定義的屬性時,就可以通過這個名字空間來引用,代碼如下所示。
使用自定義的View與系統原生的View最大的區別就是在申明控件時,需要指定完整的包名,而在引用自定義的屬性時,需要使用自定義的xmlns名字。
再更進一步,如果將這個UI模板寫到一個布局文件中,代碼如下所示。
通過如上所示的代碼,我們就可以在其他的布局文件中,直接通過標簽來引用這個UI模板View,代碼如下所示。
這樣就更加滿足了我們的模板需求。
運行程序後,顯示效果如下圖所示。
1. 前言 在Android開發中,如果是一些簡單的布局,都很容易搞定,但是一旦涉及到復雜的頁面,特別是為了兼容小屏手機而使用了ScrollView以後,就會出
開始之前 最近學習了一下NDK的開發, 就來分享一下. 對一個新鮮事物, 我們先解決的無非就是三件事情: 是什麼?為什麼?怎麼做?.NDK簡介 (英語:native de
本文實例講述了Android編程實現任務管理器的方法。分享給大家供大家參考,具體如下:任務管理器可以實現的功能有:1.查看當前系統下運行的所有的進程2.可以查看每個進程的
本文實例分析了Android持久化技術之文件的讀取與寫入操作。分享給大家供大家參考,具體如下:1、文件存儲(1)在Android的持久化技術中,文件存儲是最基本的一種數據