改變Android按鈕背景顏色的高效方法
本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!
本文將介紹一種有效改變Android按鈕顏色的方法。
按鈕可以在狀態改變時改變其顏色(例如按下,禁用,高亮顯示)。但是,這需要一一說明每個狀態。這篇文章將提供你一個根據狀態變化輕鬆改變按鈕顏色的方法。如果你正在寫自定義檢視,那麼不妨也來讀一讀,因為中間我會涉及到如何用自定義屬性實現自定義檢視的相關內容。
如何實現
Android提供了靈活的繪製選擇機制,可根據檢視狀態轉變檢視外觀。每個狀態通過一個單獨的部分而存在。例如:在正常、禁用、按下、高亮狀態下的按鈕有著不同的背景顏色。請看下面的程式碼示例:
button_1_background.xml
<selector xmlns:android="http://schemas.android.com/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>
每個狀態drawables的屬性(button_1_selected
, button_1_focused
,button_1_normal
)必須定義在相應的在drawables目錄下:
button_1_normal.xml
<shape xmlns:android="http://schemas.android.com/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.android.com/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.android.com/apk/res/android"> <solid android:color="@color/button_1_selected_background"/> <corners android:radius="10dip"/> </shape>
然後設定按鈕背景:
android:background="@drawable/button_1_background"
這種方法非常靈活。但是,當你的app有許多按鈕,而每個按鈕的顏色又各不相同時,維護每個按鈕的上述所有XML檔案就會變得異常困難起來。如果你改變正常狀態的按鈕顏色,那麼你必須改變其他狀態的顏色。在上面的例子中,每個按鈕需要4個XML檔案。那麼如果你的應用程式有10個或更多個按鈕呢?
為了清楚說明我的意思,請看下面的截圖:
這些截圖來自於一款免費產品BMEX。
這兩張圖片分別是app的主螢幕和傳送螢幕。兩個螢幕都採用了Metro風格。每個螢幕都有6個不同顏色的按鈕。並且按鈕的顏色會根據狀態的改變而改變。共計12個按鈕,所以我們需要12個drawable selector XML檔案和24個drawable state XML檔案。並且隨著app的發展,軟體還得允許新的螢幕和新的按鈕的新增。維護這些內容可不是一項簡單的任務。
為了使過程更加簡單和高效,我們另尋了一種更有效的解決方案——並且已經實現在自定義按鈕檢視中。這是一個容易初始化的按鈕。我們稱之為RoundButton,因為它支援圓角。
在另一個產品中,我們需要高亮功能,但是,又不想因此單獨建立自定義檢視。所以,我們把它新增到RoundButton
中。請看下面的截圖:
正如你所見,我們可以選擇也可以不選螢幕上的按鈕(頂部的列表圖表和每個元素後面的新增圖示)。當按鈕被選中後,它的highlighted狀態就被設定為true,反之,則為false。並且按鈕的外觀會作適當改變。在上面的例子中,高亮模式使用了“image”。在這種模式下,影像的可見象素會被繪製為高亮顏色。
首先,我們為RoundButton定義屬性集。這是一組可以通過佈局XML設定的屬性。
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>
我們增加了 image
,bgcolor
,text
,邊框圓角半徑,highlightColor和highlightMode屬性。按下狀態的顏色會從bgcolor匯出(後面會描述的)。
實現按鈕
首先,我們需要實現建構函式和解析引數。我們建立了3個不同的建構函式:
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); }
所有這些建構函式呼叫init方法。
現在,我們需要實現init方法。它將屬性集和預設樣式作為輸入引數。在init方法中,我們獲取屬性值,並初始化內部變數。如果屬性集為null,那就使用預設值。
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); }
然後,我們建立另一個init方法。這個方法用於建立物件,並需要渲染按鈕的內容。 此處的init方法宣告為public,因為建立RoundButton時需要呼叫它。它建立了背景和按下時的“噴漆(paint)”——繪製正常和按下狀態時的背景的物件。按下的顏色選取比bgcolor更亮的顏色。使顏色變亮的的方法,稍後會進行說明。這裡初始化了高亮模式。如果背景設定為高亮,那就建立高亮噴漆,用於繪製高亮時的按鈕背景。如果影像模式設定為高亮,那就建立高亮影像。在createHighlightImage方法中建立影像的程式碼,之後會一一給出。
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); }
要獲得按下狀態的色值,我們建立了brighter方法。它將顏色作為引數,並返回比該顏色更亮的顏色。這個方法也很簡單:
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); }
接下來的方法是createHighlightImage。當影像設定為高亮模式時,它會呼叫上面所示的方法。但是開頭有一些比較棘手的程式碼。它需要得到影像的畫素。然後處理畫素 ——如果畫素是不透明的(alpha != 0),就用高亮色值取代它,但是如果畫素是透明的,那就不用改動。通過這種操作,我們建立了更高亮的影像。然後,我們將修改後的畫素放回點陣圖。並且在方法的最後,建立並返回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); }
為了處理狀態變化,我們需要處理觸控事件。所以需要實現觸控處理。當我們觸控按鈕時,它的狀態就會變為pressed(按下),並重繪按鈕中的內容。當按鈕沒有被觸控,那它的pressed標誌就設定為false,並重繪按鈕中的內容。
@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); }
然後,我們實現onDraw按鈕方法。此方法繪製了按鈕的內容。自定義檢視首次展示以及每次重繪時就呼叫這個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專案,原始碼和XML資原始檔。你可以將它們複製到你的app專案中。或者編譯RoundButton專案並將其作為庫新增到你的專案。
如果你使用的是視覺化編輯器,那就直接從控制元件列表中選擇RoundButton,在新增它之後,設定其屬性。
除了視覺化編輯器,RoundButton既可以從佈局XML,也可以從程式碼中插入。從佈局XML新增的話,你可以這麼使用。示例如下:
<RelativeLayout xmlns:android="http://schemas.android.com/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.android.com/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,可以創造新的RoundButton例項。呼叫它的init方法傳遞影像(可為null),bgcolo和text。並新增RoundButton到你的ViewGroup:
roundButton = new RoundButton(context); roundButton.init(image, bgcolor, text);
進一步設想
此外,我們還可以改變RoundButton
的形狀。例如,製作圓形按鈕,正如現在很多Android app中所見的那樣。也可能配置影像位置(left、right、top、bottom)。等等。
總結
這篇文章主要描述瞭如何實現根據狀態改變背景的自定義按鈕。這個簡單的元件能為我們節省很多時間。希望能對你有用。
連結
許可證
這篇文章,以及相關原始碼和檔案,都是經過The BSD License許可的。
譯文連結:http://www.codeceo.com/article/android-button-color.html
翻譯作者:碼農網 – 小峰
[ 轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]
相關文章
- 選中按鈕改變文字大小和顏色
- JavaScript點選按鈕切換背景顏色JavaScript
- 快速搭建直播平臺,點選按鈕(Button)後改變顏色
- 視訊直播app原始碼,按鈕被按下時顏色隨著改變APP原始碼
- 使用 Promise 迴圈改變 div 背景顏色Promise
- Flutter改變狀態列字型、狀態列背景顏色、Appbar背景顏色的方式FlutterAPP
- 直播軟體原始碼,改變button的背景顏色原始碼
- 點選按鈕實現切換頁面背景顏色效果
- 點選按鈕設定其背景顏色程式碼例項
- android狀態列一體化(改變狀態列的背景顏色)Android
- Android 圓形ProgressBar 改變顏色Android
- 靜態文字顏色背景改變示例源程式 (轉)
- 短視訊開發,點選按鈕Button,更換背景顏色
- 滑鼠懸浮按鈕背景變色效果程式碼例項
- css3 ::selection的用法(改變選中文字的背景顏色或者文字顏色)CSSS3
- 搭建直播平臺,Ext Js grid 改變單元格背景顏色的方法JS
- Android 使用ColorMatrix改變圖片顏色AndroidColorMatrix
- 改變SVG圖的顏色SVG
- Android開發——Java程式碼動態改變顏色字型的方法AndroidJava
- Android中使按鈕的背景變得透明&前端中css設定透明背景Android前端CSS
- 利用CSS改變圖片顏色的多種方法!CSS
- css3背景顏色漸變CSSS3
- JavaScript 背景顏色隨機變化JavaScript隨機
- 如何操作SAP UI5應用Footer區域工具欄按鈕的背景顏色UI
- CardView改變陰影顏色View
- iOS 背景圖層的顏色漸變效果iOS
- 利用CSS改變圖片顏色的100種方法!CSS
- react native拖動上方顯示值,改變背景顏色的sliderReact NativeIDE
- web前端 改變SVG圖的顏色Web前端SVG
- 改變UITableView選中行高亮的顏色UIView
- javascript網頁背景顏色漸變效果JavaScript網頁
- css樣式背景顏色漸變效果CSS
- 短視訊平臺原始碼,點選ul/li改變背景顏色原始碼
- 自繪按鈕實現顏色選擇器
- CSS 改變文字選中顏色CSS
- 直播平臺開發,Android端簡單的顏色背景變換Android
- 設定toast的字型顏色和背景顏色AST
- Android的RadioButton隨著選中狀態的改變字型顏色也改變Android