|
本文將介紹一種有效改變Android按鈕顏色的方法。 按鈕可以在狀態(tài)改變時(shí)改變其顏色(例如按下,禁用,高亮顯示)。但是,這需要一一說(shuō)明每個(gè)狀態(tài)。這篇文章將提供你一個(gè)根據(jù)狀態(tài)變化輕松改變按鈕顏色的方法。如果你正在寫(xiě)自定義視圖,那么不妨也來(lái)讀一讀,因?yàn)橹虚g我會(huì)涉及到如何用自定義屬性實(shí)現(xiàn)自定義視圖的相關(guān)內(nèi)容。 如何實(shí)現(xiàn)Android提供了靈活的繪制選擇機(jī)制,可根據(jù)視圖狀態(tài)轉(zhuǎn)變視圖外觀。每個(gè)狀態(tài)通過(guò)一個(gè)單獨(dú)的部分而存在。例如:在正常、禁用、按下、高亮狀態(tài)下的按鈕有著不同的背景顏色。請(qǐng)看下面的代碼示例: button_1_background.xml <selector xmlns:android="http://schemas./apk/res/android"> <!— pressed state --> <item android:drawable="@drawable/button_1_selected" android:state_pressed="true"/> <!-- focused state --> <item android:drawable="@drawable/button_1_focused" android:state_focused="true"/> <!-- default state --> <item android:drawable="@drawable/button_1_normal"/> </selector> 每個(gè)狀態(tài)drawables的屬性( button_1_normal.xml <shape xmlns:android="http://schemas./apk/res/android">
<solid android:color="@color/button_1_normal_background"/>
<corners android:radius="10dip"/>
</shape>
button_1_focused.xml <shape xmlns:android="http://schemas./apk/res/android">
<solid android:color="@color/button_1_focused_background"/>
<corners android:radius="10dip"/>
</shape>
button_1_selected.xml <shape xmlns:android="http://schemas./apk/res/android">
<solid android:color="@color/button_1_selected_background"/>
<corners android:radius="10dip"/>
</shape>
然后設(shè)置按鈕背景: android:background="@drawable/button_1_background" 這種方法非常靈活。但是,當(dāng)你的app有許多按鈕,而每個(gè)按鈕的顏色又各不相同時(shí),維護(hù)每個(gè)按鈕的上述所有XML文件就會(huì)變得異常困難起來(lái)。如果你改變正常狀態(tài)的按鈕顏色,那么你必須改變其他狀態(tài)的顏色。在上面的例子中,每個(gè)按鈕需要4個(gè)XML文件。那么如果你的應(yīng)用程序有10個(gè)或更多個(gè)按鈕呢? 為了清楚說(shuō)明我的意思,請(qǐng)看下面的截圖:
這些截圖來(lái)自于一款免費(fèi)產(chǎn)品BMEX。 這兩張圖片分別是app的主屏幕和發(fā)送屏幕。兩個(gè)屏幕都采用了Metro風(fēng)格。每個(gè)屏幕都有6個(gè)不同顏色的按鈕。并且按鈕的顏色會(huì)根據(jù)狀態(tài)的改變而改變。共計(jì)12個(gè)按鈕,所以我們需要12個(gè)drawable selector XML文件和24個(gè)drawable state XML文件。并且隨著app的發(fā)展,軟件還得允許新的屏幕和新的按鈕的添加。維護(hù)這些內(nèi)容可不是一項(xiàng)簡(jiǎn)單的任務(wù)。 為了使過(guò)程更加簡(jiǎn)單和高效,我們另尋了一種更有效的解決方案——并且已經(jīng)實(shí)現(xiàn)在自定義按鈕視圖中。這是一個(gè)容易初始化的按鈕。我們稱(chēng)之為RoundButton,因?yàn)樗С謭A角。 在另一個(gè)產(chǎn)品中,我們需要高亮功能,但是,又不想因此單獨(dú)創(chuàng)建自定義視圖。所以,我們把它添加到
正如你所見(jiàn),我們可以選擇也可以不選屏幕上的按鈕(頂部的列表圖表和每個(gè)元素后面的添加圖標(biāo))。當(dāng)按鈕被選中后,它的highlighted狀態(tài)就被設(shè)置為true,反之,則為false。并且按鈕的外觀會(huì)作適當(dāng)改變。在上面的例子中,高亮模式使用了“image”。在這種模式下,圖像的可見(jiàn)象素會(huì)被繪制為高亮顏色。 首先,我們?yōu)镽oundButton定義屬性集。這是一組可以通過(guò)布局XML設(shè)置的屬性。 attrs_round_button.xml <resources>
<declare-styleable name="RoundButton">
<attr name="image" format="reference"/>
<attr name="bgcolor" format="color"/>
<attr name="text" format="string"/>
<attr name="radius" format="float"/>
<attr name="highlightColor" format="color"/>
<attr name="highlightMode" format="enum">
<enum name="none" value="0"/>
<enum name="image" value="1"/>
<enum name="background" value="2"/>
</attr>
</declare-styleable>
</resources>
我們?cè)黾恿?nbsp; 實(shí)現(xiàn)按鈕首先,我們需要實(shí)現(xiàn)構(gòu)造函數(shù)和解析參數(shù)。我們創(chuàng)建了3個(gè)不同的構(gòu)造函數(shù): public RoundButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
public RoundButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public RoundButton(Context context) {
super(context);
init(null, 0);
}
所有這些構(gòu)造函數(shù)調(diào)用init方法。 現(xiàn)在,我們需要實(shí)現(xiàn)init方法。它將屬性集和默認(rèn)樣式作為輸入?yún)?shù)。在init方法中,我們獲取屬性值,并初始化內(nèi)部變量。如果屬性集為null,那就使用默認(rèn)值。 private void init(AttributeSet attrs, int defStyle) {
Drawable image;
int bgcolor;
String text;
if (attrs != null) {
final TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.RoundButton, defStyle, 0);
image = a.getDrawable(R.styleable.RoundButton_image);
bgcolor = a.getColor(R.styleable.RoundButton_bgcolor, 0xffffffff);
text = a.getString(R.styleable.RoundButton_text);
radius = a.getFloat(R.styleable.RoundButton_radius, 12.0f);
highlightMode = HighlightMode.getValue(a.getInt
(R.styleable.RoundButton_highlightMode, HighlightMode.None.ordinal()));
highlightColor = a.getColor(R.styleable.RoundButton_highlightColor, 0xff00b5ff);
a.recycle();
}
else {
image = null;
text = "";
bgcolor = 0xff808080;
radius = 12.0f;
highlightMode = HighlightMode.None;
highlightColor = 0xff00b5ff;
}
init(image, bgcolor, text);
}
然后,我們創(chuàng)建另一個(gè)init方法。這個(gè)方法用于創(chuàng)建對(duì)象,并需要渲染按鈕的內(nèi)容。 此處的init方法聲明為public,因?yàn)閯?chuàng)建RoundButton時(shí)需要調(diào)用它。它創(chuàng)建了背景和按下時(shí)的“噴漆(paint)”——繪制正常和按下?tīng)顟B(tài)時(shí)的背景的對(duì)象。按下的顏色選取比bgcolor更亮的顏色。使顏色變亮的的方法,稍后會(huì)進(jìn)行說(shuō)明。這里初始化了高亮模式。如果背景設(shè)置為高亮,那就創(chuàng)建高亮噴漆,用于繪制高亮?xí)r的按鈕背景。如果圖像模式設(shè)置為高亮,那就創(chuàng)建高亮圖像。在createHighlightImage方法中創(chuàng)建圖像的代碼,之后會(huì)一一給出。 public void init(Drawable image, int bgcolor, String text) {
this.image = image;
bgpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgpaint.setColor(bgcolor);
pressedBgpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
pressedBgpaint.setColor(brighter(bgcolor));
if (text == null)
text = "";
this.text = text;
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(0xffffffff);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(pixelsToSp(getContext(), textSize));
if (highlightMode == HighlightMode.Background) {
highlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
highlightPaint.setColor(highlightColor);
}
else if (highlightMode == HighlightMode.Image) {
highlightImage = createHighlightImage();
}
setClickable(true);
}
要獲得按下?tīng)顟B(tài)的色值,我們創(chuàng)建了brighter方法。它將顏色作為參數(shù),并返回比該顏色更亮的顏色。這個(gè)方法也很簡(jiǎn)單: public void init(Drawable image, int bgcolor, String text) {
this.image = image;
bgpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgpaint.setColor(bgcolor);
pressedBgpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
pressedBgpaint.setColor(brighter(bgcolor));
if (text == null)
text = "";
this.text = text;
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(0xffffffff);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(pixelsToSp(getContext(), textSize));
if (highlightMode == HighlightMode.Background) {
highlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
highlightPaint.setColor(highlightColor);
}
else if (highlightMode == HighlightMode.Image) {
highlightImage = createHighlightImage();
}
setClickable(true);
}
接下來(lái)的方法是createHighlightImage。當(dāng)圖像設(shè)置為高亮模式時(shí),它會(huì)調(diào)用上面所示的方法。但是開(kāi)頭有一些比較棘手的代碼。它需要得到圖像的像素。然后處理像素 ——如果像素是不透明的(alpha != 0),就用高亮色值取代它,但是如果像素是透明的,那就不用改動(dòng)。通過(guò)這種操作,我們創(chuàng)建了更高亮的圖像。然后,我們將修改后的像素放回位圖。并且在方法的最后,創(chuàng)建并返回BitmapDrawable。 private Drawable createHighlightImage() {
int width = image.getIntrinsicWidth();
int height = image.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
image.setBounds(0, 0, width, height);
image.draw(canvas);
int count = bitmap.getWidth() * bitmap.getHeight();
int pixels[] = new int[count];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
for (int n = 0; n < count; n++) {
boolean v = (Color.alpha(pixels[n])) != 0;
if (v) {
int pixel = pixels[n];
int alpha = Color.alpha(pixel);
int red = Color.red(highlightColor);
int green = Color.green(highlightColor);
int blue = Color.blue(highlightColor);
int color = Color.argb(alpha, red, green, blue);
pixels[n] = color;
}
}
bitmap.setPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
return new BitmapDrawable(getResources(), bitmap);
}
為了處理狀態(tài)變化,我們需要處理觸摸事件。所以需要實(shí)現(xiàn)觸摸處理。當(dāng)我們觸摸按鈕時(shí),它的狀態(tài)就會(huì)變?yōu)閜ressed(按下),并重繪按鈕中的內(nèi)容。當(dāng)按鈕沒(méi)有被觸摸,那它的pressed標(biāo)志就設(shè)置為false,并重繪按鈕中的內(nèi)容。 @Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
pressed = true;
invalidate();
break;
case MotionEvent.ACTION_UP:
pressed = false;
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_HOVER_EXIT:
pressed = false;
invalidate();
break;
}
return super.onTouchEvent(event);
}
然后,我們實(shí)現(xiàn)onDraw按鈕方法。此方法繪制了按鈕的內(nèi)容。自定義視圖首次展示以及每次重繪時(shí)就調(diào)用這個(gè)onDraw方法。 protected void onDraw(Canvas canvas) {
RectF bounds = new RectF(0, 0, getWidth(), getHeight());
Drawable image = null;
Paint bgPaint = null;
switch (highlightMode) {
case None:
image = this.image;
bgPaint = pressed ? pressedBgpaint : this.bgpaint;
break;
case Background:
image = this.image;
if (pressed)
bgPaint = pressedBgpaint;
else
bgPaint = highlighted ? highlightPaint : this.bgpaint;
break;
case Image:
image = highlighted ? highlightImage : this.image;
bgPaint = pressed ? pressedBgpaint : this.bgpaint;
break;
}
if (radius != 0.0f)
canvas.drawRoundRect(bounds, radius, radius, bgPaint);
else
canvas.drawRect(bounds, bgPaint);
Rect textBounds = new Rect();
if (text.length() > 0)
textPaint.getTextBounds(text, 0, text.length(), textBounds);
float h_dst = ((image != null) ? image.getMinimumHeight() +
((text.length() > 0) ? spacing : 0) : 0) + textBounds.height();
float xd = (bounds.width() - ((image != null) ? image.getMinimumWidth() : 0)) / 2;
float yd = (bounds.height() - h_dst) / 2;
if (image != null) {
image.setBounds((int) xd, (int) yd, (int)
(xd + image.getMinimumWidth()), (int) (yd + image.getMinimumHeight()));
image.draw(canvas);
}
float xt = (bounds.width() - 0 * textBounds.width()) / 2;
float yt = yd + ((image != null) ? image.getMinimumHeight() +
((text.length() > 0) ? spacing : 0) : textBounds.height());// + textBounds.height();
canvas.drawText(text, xt, yt, textPaint);
if (checked && checkable && checkedImage != null) {
checkedImage.setBounds((int) (bounds.width() -
checkedImage.getMinimumWidth()), (int) (bounds.height() - checkedImage.getMinimumHeight()),
(int) bounds.width(), (int) bounds.height());
checkedImage.draw(canvas);
}
}
用法為了整合RoundButton到代碼,你需要下載源代碼文件。在源代碼文件中,有Eclipse項(xiàng)目,源代碼和XML資源文件。你可以將它們復(fù)制到你的app項(xiàng)目中?;蛘呔幾gRoundButton項(xiàng)目并將其作為庫(kù)添加到你的項(xiàng)目。 如果你使用的是可視化編輯器,那就直接從控件列表中選擇RoundButton,在添加它之后,設(shè)置其屬性。 除了可視化編輯器,RoundButton既可以從布局XML,也可以從代碼中插入。從布局XML添加的話(huà),你可以這么使用。示例如下: <RelativeLayout xmlns:android="http://schemas./apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false"
android:descendantFocusability="blocksDescendants"
android:orientation="horizontal"
xmlns:app="http://schemas./apk/res/com.bitgriff.bamp">
<com.bitgriff.bamp.helpers.RoundButton
android:id="@+id/button"
app:radius="0"
app:image="@drawable/ic_addtomedialibrary"
app:bgcolor="@color/transparent"
app:highlightMode="image"
android:layout_width="40dip"
android:layout_height="80dip"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"/>
</RelativeLayout>
從代碼添加RoundButton,可以創(chuàng)造新的RoundButton實(shí)例。調(diào)用它的init方法傳遞圖像(可為null),bgcolo和text。并添加RoundButton到你的viewGroup: roundButton = new RoundButton(context); roundButton.init(image, bgcolor, text); 進(jìn)一步設(shè)想此外,我們還可以改變 總結(jié)這篇文章主要描述了如何實(shí)現(xiàn)根據(jù)狀態(tài)改變背景的自定義按鈕。這個(gè)簡(jiǎn)單的組件能為我們節(jié)省很多時(shí)間。希望能對(duì)你有用。 |
|
|
來(lái)自: quasiceo > 《待分類(lèi)1》