標籤:android tabbar 自訂ui
轉載請註明本文出自JFlex的部落格http://blog.csdn.net/jflex/article/details/46492501,請尊重他人的辛勤勞動成果,謝謝!
Android UI之自訂——類似iOS的Tabbar
Tabbar最早出現在iOS,iOS中的TabBarController實現了這個功能,開發起來相當簡單。現在的APP,大多數都會使用Tabbar來作為應用的功能導航,介面簡單清晰。那麼Android常見的實現是通過RadioGroup來實現,今天將帶來自訂實現,補充RadioGroup實現的不足。
先看看常見的軟體中的使用:
這個是高鐵管家APP,大家應該非常熟悉。這個APP的首頁底部就是一個類似iOS的Tabbar。這裡就不多舉例子了,接下來直接進入正題。
RadioGroup實現Tabbar1、為什麼用RadioGroup實現呢
熟悉RadioGroup的都知道,一個RadioGroup中只能選中一個RadioButton。而Tabbar剛好就是這麼一個效果,所以用RadioGroup再好不過了。
2、實現代碼
<RadioGroup android:id="@+id/rgHomeMenu" android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal" > <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:button="@null" android:checked="true" android:drawableTop="@drawable/icon_project_selector" android:gravity="center" android:paddingBottom="5dp" android:paddingTop="8dp" android:text="@string/project" android:textColor="@color/menu_txt_selector" android:textSize="12sp" /> <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:button="@null" android:drawableTop="@drawable/icon_msg_selector" android:gravity="center" android:paddingBottom="5dp" android:paddingTop="8dp" android:text="@string/msg" android:textColor="@color/menu_txt_selector" android:textSize="12sp" /> <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:button="@null" android:drawableTop="@drawable/icon_mine_selector" android:gravity="center" android:paddingBottom="5dp" android:paddingTop="8dp" android:text="@string/mine" android:textColor="@color/menu_txt_selector" android:textSize="12sp" /> </RadioGroup>
- RadioGroup必須使用RadioButton作為子控制項
- 由於Tabbar是片、下文字,所有需要將android:button設定空,去掉RadioButton的預設圖。片設定android:drawableTop屬性,文字設定android:text屬性,其他屬性按照實際需求調整即可。
- 實現起來很簡單,字碼頁比較簡潔。
3、優缺點
- 優點:代碼簡潔,容易實現
- 缺點:擴充性不足。因為每一個Item是RadioButton,所以只能使用這個空間的相關功能,如果需要擴充,就做不到了。
自訂Tabbar1、需求:比如現在需要在訊息上添加類似iOS的badgeview的訊息提示。
2、實現代碼
TabGroup
這個類需要實作類別似RadioGroup的作用,代碼借鑒RadioGroup實現。
package com.snicesoft.tabbar;import android.annotation.SuppressLint;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;@SuppressLint("NewApi")public class TabGroup extends LinearLayout { public TabGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } public TabGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public TabGroup(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TabGroup(Context context) { super(context); init(); } private void init() { setOrientation(HORIZONTAL); } int mCheckedId = -1; OnTabGroupCheckedListener onTabGroupCheckedListener; public void setOnTabGroupCheckedListener( OnTabGroupCheckedListener onTabGroupCheckedListener) { this.onTabGroupCheckedListener = onTabGroupCheckedListener; } @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { if (child instanceof TabItem) { final TabItem tab = (TabItem) child; if (tab.isChecked()) { check(tab.getId()); } } super.addView(child, index, params); } public void check(int checkId) { if (mCheckedId == checkId) { return; } setCheckedStateForView(mCheckedId, false); setCheckedId(checkId); mCheckedId = checkId; if (onTabGroupCheckedListener != null) onTabGroupCheckedListener.onChecked(checkId); } private void setCheckedId(int id) { View checkedView = findViewById(id); if (checkedView != null && checkedView instanceof TabItem) { ((TabItem) checkedView).setChecked(true); } } private void setCheckedStateForView(int viewId, boolean checked) { View checkedView = findViewById(viewId); if (checkedView != null && checkedView instanceof TabItem) { ((TabItem) checkedView).setChecked(checked); } } public interface OnTabGroupCheckedListener { public void onChecked(int checkedId); }}
TabItem
TabItem需要整合RadioButton的功能,也需要擴充性更強。所以選擇整合RelativeLayout,需要有check的狀態操作,那麼需要實現Checkable。
package com.snicesoft.tabbar;import java.util.ArrayList;import java.util.HashMap;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.drawable.Drawable;import android.graphics.drawable.StateListDrawable;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;import android.widget.Checkable;import android.widget.RelativeLayout;@SuppressLint("NewApi")public class TabItem extends RelativeLayout implements Checkable { private ArrayList<Checkable> chechableList = new ArrayList<Checkable>(); private HashMap<View, StateListDrawable> stateListDrawableMap = new HashMap<View, StateListDrawable>(); private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; public TabItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } public TabItem(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public TabItem(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TabItem(Context context) { super(context); init(); } private void init() { super.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { onChecked(); if (OnClickListener != null) OnClickListener.onClick(v); } }); } @Override public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) { super.addView(child, index, params); setStates(child); } private void setStates(View child) { Drawable drawable = child.getBackground(); if (drawable != null && drawable instanceof StateListDrawable) { stateListDrawableMap.put(child, (StateListDrawable) drawable); } child.setClickable(false); if (child instanceof Checkable) { chechableList.add((Checkable) child); } if (child instanceof ViewGroup) { final ViewGroup group = (ViewGroup) child; if (group.getChildCount() > 0) { for (int i = 0; i < group.getChildCount(); i++) { setStates(group.getChildAt(i)); } } } } OnClickListener OnClickListener; @Override public void setOnClickListener(OnClickListener l) { OnClickListener = l; } boolean isChecked = false; @Override public void setChecked(boolean checked) { if (isChecked == checked) return; for (Checkable ca : chechableList) { ca.setChecked(checked); } if (checked) { for (View v : stateListDrawableMap.keySet()) { StateListDrawable drawable = stateListDrawableMap.get(v); drawable.setState(CHECKED_STATE_SET); v.setBackground(drawable.getCurrent()); } } else { for (View v : stateListDrawableMap.keySet()) { v.setBackground(stateListDrawableMap.get(v)); } } isChecked = checked; } private void onChecked() { if (getParent() instanceof TabGroup) { final TabGroup group = (TabGroup) getParent(); group.check(getId()); } } @Override public boolean isChecked() { return isChecked; } @Override public void setPressed(boolean pressed) { super.setPressed(pressed); if (!pressed) { setChecked(true); } } @Override public void toggle() { setChecked(!isChecked); }}
RelativeLayout中的預設帶有Pressed屬性的組件,不如Button,會攔截onClick事件,所以TabItem中的所有組件都背設定不可點擊。
- 為了讓TabItem的相容性達到RadioButton一樣,所以在setChecked方法中將TabItem中包含的所有整合Checkable的View強制調用setChecked方法,能夠達到同步效果(點擊TabItem的時候,能夠將check的狀態傳遞到子控制項中)
- 為了讓TabItem中的組件能夠使用selector,需要用到StateListDrawable來控制不同狀態的背景顯示。
3、優缺點
- 優點:使用方法和RadioGroup一致,擴充性強
- 缺點:layout代碼比RadioGroup多,沒有設定預設checked屬性
總結
這種自訂控制項,只是在原生控制項的基礎上改進,是比較初級的,只需要掌握改進原理,修改的方法很多種的。本人的修改只是一個簡單的示範,希望大家有好的改進方法,不吝賜教。
本例子源碼下載
Android UI之自訂——類似iOS的Tabbar