轉載請注明出處:http://www.cnblogs.com/landptf/p/6290841.html
今天我們封裝一個底部的菜單欄,這個大多數的應用都會用到,因此我們來自定義,方便以后項目的使用。
該控件的實現將分上下篇來介紹,先來看一個菜單欄的子控件–MenuItemM,這個控件有什么用呢?我們來看下一些主流app上的一些控件,如:

以上三張圖片分別來自微信,今日頭條和去哪兒,接下來我們將看到如何通過一個控件來實現不同的效果。
首先看下我寫的一個deme

可以看到標題欄的消息控件,以及底部三個菜單項都是通過MenuItemM來實現的
這里面只是演示菜單欄的子控件,我們將在下一篇博客中完成底部菜單欄的封裝,這個控件里使用了上一篇博客介紹的一個控件ButtonExtendM,可以先看一下
http://www.cnblogs.com/landptf/p/6290810.html
接下來看下實現過程
1 定義屬性
1 <declare-styleable name="MenuItemM"> 2 <attr name="backColor" /> 3 <attr name="textColor" /> 4 <attr name="textColorPress" /> 5 <attr name="iconDrawable" /> 6 <attr name="iconDrawablePress" /> 7 <attr name="text" /> 8 <attr name="textSize" /> 9 <attr name="unReadCount" format="integer" /> 10 <attr name="visibleMore"> 11 <enum name="visible" value="0x00000000" /> 12 <enum name="gone" value="0x00000008" /> 13 </attr> 14 <attr name="visibleNew"> 15 <enum name="visible" value="0x00000000" /> 16 <enum name="gone" value="0x00000008" /> 17 </attr> 18 </declare-styleable>
這里面重點看一下visibleMore和visibleNew里面的兩個枚舉值,這里面與View源碼中的visible和gone保持一致。關於如何定義屬性以及使用,可以參考我之前的博客。
2 布局文件view_menu_item_m.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:landptf="http://schemas.android.com/apk/res-auto" 4 android:layout_width="wrap_content" 5 android:layout_height="wrap_content" 6 android:layout_gravity="center"> 7 8 <com.landptf.view.ButtonExtendM 9 android:id="@+id/bem_menu" 10 android:layout_width="match_parent" 11 android:layout_height="match_parent" 12 android:layout_marginRight="8dp" 13 landptf:style="iconUp" /> 14 15 <ImageView 16 android:id="@+id/iv_more" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:layout_gravity="top|right" 20 android:background="@drawable/icon_more" 21 android:visibility="gone" /> 22 23 <ImageView 24 android:id="@+id/iv_new" 25 android:layout_width="wrap_content" 26 android:layout_height="wrap_content" 27 android:layout_gravity="top|right" 28 android:background="@drawable/icon_new" 29 android:visibility="gone" /> 30 31 <com.landptf.view.ButtonM 32 android:id="@+id/btm_unread_count" 33 android:layout_width="20dp" 34 android:layout_height="20dp" 35 android:layout_gravity="top|right" 36 android:textSize="12sp" 37 android:visibility="gone" 38 landptf:backColor="#ff0000" 39 landptf:fillet="true" 40 landptf:shape="oval" 41 landptf:textColor="@android:color/white" /> 42 43 </FrameLayout>
這里面使用了FrameLayout,主要使用了ButtonExtendM上下結構的控件加上右上角的三種提示信息,數量提示,more提示,new提示
3 MenuItemM.java
1 package com.landptf.view; 2 3 import android.content.Context; 4 import android.content.res.ColorStateList; 5 import android.content.res.TypedArray; 6 import android.graphics.drawable.Drawable; 7 import android.util.AttributeSet; 8 import android.util.Log; 9 import android.view.Gravity; 10 import android.view.LayoutInflater; 11 import android.view.MotionEvent; 12 import android.view.View; 13 import android.widget.FrameLayout; 14 import android.widget.ImageView; 15 16 import com.landptf.R; 17 18 /** 19 * Created by landptf on 2016/11/07. 20 * 菜單按鈕,例如底部菜單的item或者消息控件 21 */ 22 public class MenuItemM extends FrameLayout { 23 24 private static final String TAG = MenuItemM.class.getSimpleName(); 25 26 /** 27 * 定義控件 28 */ 29 private ButtonExtendM bemMenu; 30 private ImageView ivMore; 31 private ImageView ivNew; 32 private ButtonM btmUnReadCount; 33 34 private OnClickListener onClickListener = null; 35 36 public interface OnClickListener { 37 void onClick(View v); 38 } 39 40 /** 41 * 設置View的Click事件 42 * 43 * @param l 44 */ 45 public void setOnClickListener(OnClickListener l) { 46 this.onClickListener = l; 47 //攔截ButtonExtendM控件的點擊事件,使其指向this.onclick 48 bemMenu.setOnClickListener(new ButtonExtendM.OnClickListener() { 49 @Override 50 public void onClick(View v) { 51 onClickListener.onClick(v); 52 } 53 }); 54 } 55 56 public MenuItemM(Context context) { 57 super(context); 58 } 59 60 public MenuItemM(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 64 public MenuItemM(Context context, AttributeSet attrs, int defStyleAttr) { 65 super(context, attrs, defStyleAttr); 66 init(context, attrs, defStyleAttr); 67 } 68 69 private void init(Context context, AttributeSet attrs, int defStyle) { 70 //加載布局 71 LayoutInflater.from(context).inflate(R.layout.view_menu_item_m, this, true); 72 //初始化控件 73 bemMenu = (ButtonExtendM) findViewById(R.id.bem_menu); 74 ivMore = (ImageView) findViewById(R.id.iv_more); 75 ivNew = (ImageView) findViewById(R.id.iv_new); 76 btmUnReadCount = (ButtonM) findViewById(R.id.btm_unread_count); 77 btmUnReadCount.setGravity(Gravity.CENTER); 78 TypedArray a = getContext().obtainStyledAttributes( 79 attrs, R.styleable.MenuItemM, defStyle, 0); 80 if (a != null) { 81 //設置背景色 82 ColorStateList colorList = a.getColorStateList(R.styleable.MenuItemM_backColor); 83 if (colorList != null) { 84 int backColor = colorList.getColorForState(getDrawableState(), 0); 85 if (backColor != 0) { 86 setBackColor(backColor); 87 } 88 } 89 //設置icon 90 Drawable iconDrawable = a.getDrawable(R.styleable.MenuItemM_iconDrawable); 91 if (iconDrawable != null) { 92 setIconDrawable(iconDrawable); 93 } 94 //記錄View被按下時的icon的圖片 95 Drawable iconDrawablePress = a.getDrawable(R.styleable.MenuItemM_iconDrawablePress); 96 if (iconDrawablePress != null) { 97 setIconDrawablePress(iconDrawablePress); 98 } 99 //設置文字的顏色 100 ColorStateList textColorList = a.getColorStateList(R.styleable.MenuItemM_textColor); 101 if (textColorList != null) { 102 int textColor = textColorList.getColorForState(getDrawableState(), 0); 103 if (textColor != 0) { 104 setTextColor(textColor); 105 } 106 } 107 //記錄View被按下時文字的顏色 108 ColorStateList textColorPressList = a.getColorStateList(R.styleable.MenuItemM_textColorPress); 109 if (textColorPressList != null) { 110 int textColorPress = textColorPressList.getColorForState(getDrawableState(), 0); 111 if (textColorPress != 0) { 112 setTextColorPress(textColorPress); 113 } 114 } 115 //設置顯示的文本內容 116 String text = a.getString(R.styleable.MenuItemM_text); 117 if (text != null) { 118 setText(text); 119 } 120 //設置文本字體大小 121 float textSize = a.getFloat(R.styleable.MenuItemM_textSize, 0); 122 if (textSize != 0) { 123 setTextSize(textSize); 124 } 125 //設置更多提示是否顯示 126 int visibleMore = a.getInt(R.styleable.MenuItemM_visibleMore, -1); 127 if (visibleMore != -1){ 128 setVisibilityMore(visibleMore); 129 } 130 //設置new提示是否顯示 131 int visibleNew = a.getInt(R.styleable.MenuItemM_visibleNew, -1); 132 if (visibleNew != -1){ 133 setVisibilityNew(visibleNew); 134 } 135 //設置消息未讀數量 136 int unReadCount = a.getInt(R.styleable.MenuItemM_unReadCount, -1); 137 if (unReadCount != -1){ 138 setUnReadCount(unReadCount); 139 } 140 a.recycle(); 141 } 142 143 setOnClickListener(new View.OnClickListener() { 144 @Override 145 public void onClick(View v) { 146 if (onClickListener != null) { 147 onClickListener.onClick(v); 148 } 149 } 150 }); 151 } 152 153 /** 154 * 設置為被選中狀態 155 * @param state in MotionEvent.ACTION_DOWN or MotionEvent.ACTION_UP 156 */ 157 public void setPressState(int state){ 158 if (state != MotionEvent.ACTION_DOWN && state != MotionEvent.ACTION_UP){ 159 Log.w(TAG, "無效參數"); 160 return; 161 } 162 bemMenu.setPressState(state); 163 } 164 165 /** 166 * 設置View的背景色 167 * 168 * @param backColor 169 */ 170 public void setBackColor(int backColor) { 171 bemMenu.setBackColor(backColor); 172 } 173 174 /** 175 * 設置icon的圖片 176 * 177 * @param iconDrawable 178 */ 179 public void setIconDrawable(Drawable iconDrawable) { 180 bemMenu.setIconDrawable(iconDrawable); 181 } 182 183 /** 184 * 設置View被按下時的icon的圖片 185 * 186 * @param iconDrawablePress 187 */ 188 public void setIconDrawablePress(Drawable iconDrawablePress) { 189 bemMenu.setIconDrawablePress(iconDrawablePress); 190 } 191 192 /** 193 * 設置文字的顏色 194 * 195 * @param textColor 196 */ 197 public void setTextColor(int textColor) { 198 if (textColor == 0) return; 199 bemMenu.setTextColor(textColor); 200 } 201 202 /** 203 * 設置View被按下時文字的顏色 204 * 205 * @param textColorPress 206 */ 207 public void setTextColorPress(int textColorPress) { 208 if (textColorPress == 0) return; 209 bemMenu.setTextColorPress(textColorPress); 210 } 211 212 /** 213 * 設置顯示的文本內容 214 * 215 * @param text 216 */ 217 public void setText(CharSequence text) { 218 bemMenu.setText(text); 219 } 220 221 /** 222 * 獲取顯示的文本 223 * 224 * @return 225 */ 226 public String getText() { 227 return bemMenu.getText(); 228 } 229 230 /** 231 * 設置文本字體大小 232 * 233 * @param size 234 */ 235 public void setTextSize(float size) { 236 bemMenu.setTextSize(size); 237 } 238 239 /** 240 * 設置更多提示是否顯示 241 * 如果顯示則先重置new和未讀數量圖標 242 * @param visibleMore 243 */ 244 public void setVisibilityMore(int visibleMore) { 245 if (visibleMore == VISIBLE) { 246 resetTip(); 247 } 248 ivMore.setVisibility(visibleMore); 249 } 250 251 /** 252 * 設置New提示是否顯示 253 * 如果顯示則先重置更多和未讀數量圖標 254 * @param visibleNew 255 */ 256 public void setVisibilityNew(int visibleNew) { 257 if (visibleNew == VISIBLE) { 258 resetTip(); 259 } 260 ivNew.setVisibility(visibleNew); 261 } 262 263 /** 264 * 設置未讀數量 265 * 如果小於等於0,表示隱藏 266 * 如果大於99,則將其隱藏,同時顯示更多的提示 267 * 如果在0-99區間,則隱藏更多和new圖標 268 * @param unReadCount 269 */ 270 public void setUnReadCount(int unReadCount){ 271 if (unReadCount <= 0){ 272 btmUnReadCount.setVisibility(GONE); 273 //如果先設置100(此時會顯示ivMore),再設置0,因此此處應將ivMore同時置為GONE 274 if (ivMore.getVisibility() == VISIBLE){ 275 ivMore.setVisibility(GONE); 276 } 277 return; 278 } 279 if (unReadCount > 99){ 280 setVisibilityMore(VISIBLE); 281 return; 282 } 283 resetTip(); 284 btmUnReadCount.setVisibility(VISIBLE); 285 btmUnReadCount.setText(unReadCount + ""); 286 } 287 288 /** 289 * 重置提示信息 290 */ 291 private void resetTip(){ 292 setVisibilityMore(GONE); 293 setVisibilityNew(GONE); 294 setUnReadCount(0); 295 } 296 297 }
代碼有點長,邏輯比較簡單,本身自定義控件的過程都是類似的,比較多的是對外提供的接口。
特別要注意的是使用時大小要設置為自定義,如果指定了大小或者match_parent,則子控件將居於左上角,無法居中。
4 最后簡單看下如何使用
1 <com.landptf.view.MenuItemM 2 android:id="@+id/mim_home_page" 3 android:layout_width="wrap_content" 4 android:layout_height="wrap_content" 5 android:layout_centerVertical="true" 6 android:layout_marginLeft="32dp" 7 landptf:iconDrawable="@drawable/icon_home_page" 8 landptf:iconDrawablePress="@drawable/icon_home_page_press" 9 landptf:textColor="#696969" 10 landptf:textColorPress="#303f9f" 11 landptf:text="首頁" 12 />
這里面主要使用了以下四個屬性,分別表示默認圖標和按下后顯示的圖標,以及文字顏色和按下后的文字顏色
1 landptf:iconDrawable="@drawable/icon_home_page" 2 landptf:iconDrawablePress="@drawable/icon_home_page_press" 3 landptf:textColor="#696969" 4 landptf:textColorPress="#303f9f"
1 final MenuItemM mimHomePage = (MenuItemM) findViewById(R.id.mim_home_page); 2 if (mimHomePage != null){ 3 //默認為選中狀態 4 mimHomePage.setPressState(MotionEvent.ACTION_DOWN); 5 mimHomePage.setVisibilityMore(View.VISIBLE); 6 mimHomePage.setOnClickListener(new MenuItemM.OnClickListener() { 7 @Override 8 public void onClick(View v) { 9 //按下后隱藏提示信息 10 mimHomePage.setVisibilityMore(View.GONE); 11 } 12 }); 13 }
好了,就介紹到這里了,更多的使用方法可以參考源碼MenuItemMTestActivity.java。
全部代碼已托管到開源中國的碼雲上,歡迎下載,地址:https://git.oschina.net/landptf/landptf.git
