組合控件是自定義控件的一種,只不過它是由其他幾個原生控件組合而成,故名組合控件。
在實際項目中,GUI會遇到一些可以提取出來做成自定義控件情況。
一個自定義控件的好處就是把一些需要模塊化的UI和邏輯放在一起,做到了高內聚,向其他模塊提供接口并很少
?依賴外界,這樣就是低耦合。一個自定義控件就是一個封閉的王國,這里由你掌控。
上述是我自己的一個體會,想必大家也會常做自定義控件吧,就像邏輯部分的模塊化一樣。
下面我要做一個例子,請看完成圖。

下面一排圖片加文字就是組合控件了,我是怎么做的呢?
其實這里用到了兩個組合控件,一個是圖片+文字,我把它叫一個Item,而三個在一起就是另一個控件了。
重點看這個Item,它有自己的屬性如圖片、文字、圖片大小、文字大小、不透明度等等。這些把它定義在attr文件中,然后在xml文件中
配置,就像我們用原生控件一樣。
先看attr文件。
~~~
xml?version="1.0"?encoding="utf-8"?>??
resources>??
????????declare-styleable?name="LevelMenuItem">??
????????attr?name="text"?format="string"?/>??
????????attr?name="text_color"?format="color"/>??
????????attr?name="text_size"?format="dimension"?/>??????????
????????attr?name="image_src"?format="reference"/>??
????????attr?name="image_bg"?format="reference"/>??
????????attr?name="image_alpha"?format="integer"?/>??
????????attr?name="image_height"?format="dimension">attr>??
????????attr?name="image_width"?format="dimension"?/>??
????declare-styleable>??
resources>??
~~~
這個文件在values下,和string文件同級。把你自己要定義的屬性都寫在這里吧。format是屬性的“單位”,如果你要問有多少中format呀?答案在[這里](http://blog.csdn.net/lincyang/article/details/7421757)。
有了屬性了,下面看看布局文件level_menu_item.xml。
~~~
xml?version="1.0"?encoding="utf-8"?>??
LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
????android:layout_width="fill_parent"??
????android:layout_height="fill_parent"??
????android:orientation="vertical"?>??
????????ImageView??
????????????android:id="@+id/image_item"??
????????????android:layout_width="fill_parent"??
????????????android:layout_height="fill_parent"??
????????????android:scaleType="fitCenter"??
????????????/>??
?????????TextView??
????????????android:id="@+id/tv_item"??
????????????android:layout_width="fill_parent"??
????????????android:layout_height="wrap_content"??
????????????android:gravity="center_horizontal"??
????????????android:textColor="#23ffffff"??
????????????android:textSize="25sp"???
?????????/>??????
LinearLayout>??
~~~
這里唯一值得一說的是文本的顏色。大家看見他是8位的,前兩位是表示不透明度的,后六位是表示顏色的,三色,范圍都是00~ff。
如果在java中設置顏色,需要這樣。
~~~
setTextColor(0x23ffffff);??
~~~
關于不透明度,一般美工會定義。有些要求不透明如30%這樣的,可以用整型換算一下。00~ff對應十進制為0~255,那么30%就是255x0.3=76.5,用科學計算機換算為4c。
更多顏色相關請看[《Android中設置文本顏色的三種辦法》](http://blog.csdn.net/lincyang/article/details/5871117)
然后我們就要寫一個類,我這繼承子線性布局。有兩個構造函數,我們主要在兩個參數的函數中工作。
~~~
public?class?LevelMenuItem?extends?LinearLayout?{??
??????
????public?LevelMenuItem(Context?context,?AttributeSet?attrs)?{??
????????super(context,?attrs);??
??????????
????}??
~~~
這個類中我們要完成的工作是,初始化控件屬性、提供外部修改屬性的接口、控件點擊的回調接口。
此類完整代碼:
~~~
package?com.linc.game;??
??
import?android.content.Context;??
import?android.content.res.TypedArray;??
import?android.util.AttributeSet;??
import?android.view.LayoutInflater;??
import?android.view.View;??
import?android.widget.ImageView;??
import?android.widget.LinearLayout;??
import?android.widget.TextView;??
/**?
?*?自定義一個關卡?
?*?共有7個屬性,看attr文件?
?*?在程序中提供修改這7個屬性的接口,?
?*?一個自定義控件的任務就算完成。?
?*?一個自定義控件的好處就是把一些需要模塊化的?
?*?UI和邏輯放在一起,做到了高內聚,向其他模塊提供接口并很少?
?*?依賴外界,這樣就是低耦合。一個自定義控件就是一個封閉的王國,?
?*?這里由你掌控。?
?*??
?*?編寫時,如果遇到在attr里寫好屬性,但是在這里認不出來,?
?*?就clean一下項目。切記。?
?*??
?*?@author?linc?
?*?
?*/??
public?class?LevelMenuItem?extends?LinearLayout?{??
????private?TextView?mTextView?=?null;??
????private?ImageView?mImageView?=?null;??
??????
????public?LevelMenuItem(Context?context)?{??
????????super(context);??
????}??
????public?LevelMenuItem(Context?context,?AttributeSet?attrs)?{??
????????super(context,?attrs);??
??????????
????????LayoutInflater?layoutInflater?=?(LayoutInflater)?context.??
????????????????????????getSystemService(Context.LAYOUT_INFLATER_SERVICE);??
????????layoutInflater.inflate(R.layout.level_menu_item,?this);??
??????????
????????TypedArray?typedArray?=?context.obtainStyledAttributes(attrs??
????????????????,R.styleable.LevelMenuItem);??
??????????
????????initWidget(typedArray);??
????}??
????private?void?initWidget(TypedArray?typedArray)??
????{??
????????mTextView?=?(TextView)findViewById(R.id.tv_item);??
????????String?textString?=?typedArray.getString(R.styleable.LevelMenuItem_text);??
????????int?textColor?=?typedArray.getColor(R.styleable.LevelMenuItem_text_color,??
????????????????0xffffffff);??
????????float?textSize?=?typedArray.getDimension(R.styleable.LevelMenuItem_text_size,??
????????????????20);??
????????mTextView.setText(textString);??
????????mTextView.setTextColor(textColor);??
????????mTextView.setTextSize(textSize);??
??????????
????????mImageView?=?(ImageView)findViewById(R.id.image_item);??
????????int?imageHeight?=?(int)?typedArray.getDimension(R.styleable.LevelMenuItem_image_height,?25);??
????????int?imageWidth?=?(int)?typedArray.getDimension(R.styleable.LevelMenuItem_image_width,?25);??
????????int?imageSrc?=?typedArray.getResourceId(R.styleable.LevelMenuItem_image_src,?0);??
????????int?imageBg?=?typedArray.getResourceId(R.styleable.LevelMenuItem_image_bg,?0);??
????????int?imageAlpha?=?typedArray.getInt(R.styleable.LevelMenuItem_image_alpha,?255);??
????????mImageView.setAlpha(imageAlpha);??
????????mImageView.setImageResource(imageSrc);??
????????mImageView.setBackgroundResource(imageBg);??
????????LayoutParams?layoutParams?=?new?LayoutParams(imageWidth,?imageHeight);??
????????mImageView.setLayoutParams(layoutParams);??
??????????
????????typedArray.recycle();??
????}??
????/**?
?????*?設置此控件的文本?
?????*?@param?text?
?????*/??
????public?void?setText(String?text)??
????{??
????????mTextView.setText(text);??
????}??
????/**?
?????*?設置文字顏色?
?????*?@param?textColor?
?????*/??
????public?void?setTextColor(int?textColor)??
????{??
????????mTextView.setTextColor(textColor);??
????}??
????/**?
?????*?設置字體大小?
?????*?@param?textSize?
?????*/??
????public?void?setTextSize(int?textSize)??
????{??
????????mTextView.setTextSize(textSize);??
????}??
????/**?
?????*?設置圖片?
?????*?@param?resId?
?????*/??
????public?void?setImageResource(int?resId)??
????{??
????????mImageView.setImageResource(resId);??
????}??
????/**?
?????*?設置圖片背景?
?????*/??
????public?void?setBackgroundResource(int?resId)??
????{??
????????mImageView.setBackgroundResource(resId);??
????}?????
????/**?
?????*?設置圖片的不透名度?
?????*?@param?alpha?
?????*/??
????public?void?setImageAlpha(int?alpha)??
????{??
????????mImageView.setAlpha(alpha);??
????}??
????/**?
?????*?設置圖片的大小?
?????*?這里面需要使用LayoutParams這個布局參數來設置?
?????*?@param?width?
?????*?@param?height?
?????*/??
????public?void?setImageSize(int?width,int?height)??
????{??
????????LayoutParams?layoutParams?=?new?LayoutParams(width,?height);??
????????mImageView.setLayoutParams(layoutParams);??
????}??
????/**?
?????*?image點擊事件的回調?
?????*?@param?listener?
?????*/??
????public?void?setOnClickListener(OnItemClickListener?listener)??
????{??
????????mImageView.setOnClickListener(new?View.OnClickListener()?{??
????????????@Override??
????????????public?void?onClick(View?v)?{??
????????????????listener.onImageClick();??
????????????}??
????????});??
????}??
????/**?
?????*?點擊事件接口?
?????*?@author?linc?
?????*?
?????*/??
????public?interface?OnItemClickListener??
????{??
????????public?void?onImageClick();??
????}??
}??
~~~
好,一個完整的組合控件就做好了,那么,我們如何使用呢?
我要在LevelMenu中用它。xml文件如下:
~~~
xml?version="1.0"?encoding="utf-8"?>?? LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
????xmlns:linc="http://schemas.android.com/apk/res/com.linc.game"??
????android:layout_width="fill_parent"??
????android:layout_height="fill_parent"??
????android:orientation="horizontal">??????
????com.linc.game.LevelMenuItem??
????????android:id="@+id/item1"??
????????android:layout_width="70dp"??
????????android:layout_height="80dp"??
????????linc:text="@string/item1"??
????????linc:text_size="14sp"??
????????linc:text_color="#80fa8072"??
????????linc:image_src="@drawable/orange_button_selector"??
????????linc:image_alpha="128"??
????????linc:image_height="48dp"??
????????linc:image_width="48dp"??
????????/>??
????com.linc.game.LevelMenuItem??
????????android:id="@+id/item2"??
????????android:layout_marginLeft="20dp"??
????????android:layout_width="70dp"??
????????android:layout_height="80dp"??
????????linc:text="@string/item2"??
????????linc:text_size="14sp"??
????????linc:text_color="#ffeee8aa"??
????????linc:image_src="@drawable/red_button_selector"??
????????linc:image_alpha="255"??
????????linc:image_height="48dp"??
????????linc:image_width="48dp"??
????????/>?????
????com.linc.game.LevelMenuItem??
????????android:id="@+id/item3"??
????????android:layout_marginLeft="20dp"??
????????android:layout_width="70dp"??
????????android:layout_height="80dp"??
????????linc:text="@string/item3"??
????????linc:text_size="14sp"??
????????linc:text_color="#80cd853f"??
????????linc:image_src="@drawable/yellow_button_selector"??
????????linc:image_alpha="128"??
????????linc:image_height="48dp"??
????????linc:image_width="48dp"??
????????/>?????????
LinearLayout>??
~~~
加入自己包名的索引
` xmlns:linc="http://schemas.android.com/apk/res/com.linc.game"??`
剩下的就一目了然了。
LevelMenu.java
~~~
package?com.linc.game;??
??
import?com.linc.game.LevelMenuItem.OnItemClickListener;??
import?android.content.Context;??
import?android.util.AttributeSet;??
import?android.util.Log;??
import?android.view.LayoutInflater;??
import?android.widget.LinearLayout;??
??
public?class?LevelMenu?extends?LinearLayout?{??
????private?LevelMenuItem?item1,item2,item3;??
??????
????public?LevelMenu(Context?context)?{??
????????super(context);??
??????????
????}??
??
??????
????public?LevelMenu(Context?context,?AttributeSet?attrs)?{??
????????super(context,?attrs);??
????????LayoutInflater?layoutInflater?=?(LayoutInflater)?context.??
????????????getSystemService(Context.LAYOUT_INFLATER_SERVICE);??
????????layoutInflater.inflate(R.layout.level_menu,?this);??
????????initWidget();??
????}??
????private?void?initWidget()??
????{??
????????item1?=?(LevelMenuItem)findViewById(R.id.item1);??
????????item2?=?(LevelMenuItem)findViewById(R.id.item2);??
????????item3?=?(LevelMenuItem)findViewById(R.id.item3);??
??????????
????????item1.setOnClickListener(new?OnItemClickListener()?{??
????????????@Override??
????????????public?void?onImageClick()?{??
????????????????Log.e("dfjdkfjd","dfdfd");??
????????????}??
????????});??
????}??
}??
~~~
在處理圖片點擊事件的時候,我用到了選擇器(selector),這是我們實際開發中最常用的小技巧了。它能描述的狀態很多,各位看官可以去查查。
~~~
xml?version="1.0"?encoding="utf-8"?>??
selector?xmlns:android="http://schemas.android.com/apk/res/android"?>??
????item?android:state_pressed="true"??
????????android:drawable="@drawable/button_push"/>??
????item?android:drawable="@drawable/orange_button"/>??
selector>??
~~~
好,組合控件的例子先到這里,實際功能在下一個實戰技巧中演練。
大家在做自定義控件時需要注意的是:
1、自定義控件類不能是是抽象類
2、要用
` (Context?context,?AttributeSet?attrs)??`
這個構造函數
否則報錯:android.view.InflateException: Binary XML file line #15: Error inflating cla。。。
- 前言
- 一:文本與布局
- 二:組合控件
- 三:性能測試類
- 四:語音識別
- 五:讀取Excel
- 六:PreferenceActivity使用詳解
- 七:按鈕控制ViewPager的左右翻頁
- 八:Ubuntu下切換JDK版本
- 九:最新Android開發環境(Eclipse+ADT+Android 5.0)
- 十:獲得屏幕物理尺寸、密度及分辨率
- 十一:Android Studio和Gradle
- 十二:Android Studio導入第三方類庫、jar包和so庫
- 十三:APK簽名
- 十四:混淆與反編譯
- 十五:多分辨率適配常用目錄
- 十六:getprop與dumpsys命令
- 十七:Linux下的模擬器硬件加速
- 十八:adb取出安裝在手機中的apk
- 十九:android studio導出jar包(Module)并獲得手機信息
- 二十:兩個開源的圖表/報表控件
- 二十一:Android原型設計工具探索
- 二十二:Android 5.1 SDK下載與配置
- 二十三:Android Studio的NDK開發
- 二十四:橫豎屏切換
- 二十五:模擬器如何重啟?試試Genymotion!
- 二十六:persistableMode與Activity的持久化
- 二十七:Maven編譯開源二維碼掃描項目zxing