1. 程式人生 > >Android自定義控制元件系列(三)—底部選單(上)

Android自定義控制元件系列(三)—底部選單(上)

今天我們封裝一個底部的選單欄,這個大多數的應用都會用到,因此我們來自定義,方便以後專案的使用。

該控制元件的實現將分上下篇來介紹,先來看一個選單欄的子控制元件–MenuItemM,這個控制元件有什麼用呢?我們來看下一些主流app上的一些控制元件,如:
這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

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

可以看到標題欄的訊息控制元件,以及底部三個選單項都是通過MenuItemM來實現的

接下來看下實現過程
1 定義屬性

<declare-styleable name="MenuItemM"
>
<attr name="backColor" /> <attr name="textColor" /> <attr name="textColorPress" /> <attr name="iconDrawable" /> <attr name="iconDrawablePress" /> <attr name="text" /> <attr name="textSize" /> <attr name="unReadCount" format="integer"
/>
<attr name="visibleMore"> <enum name="visible" value="0x00000000" /> <enum name="gone" value="0x00000008" /> </attr> <attr name="visibleNew"> <enum name="visible" value="0x00000000" /> <enum name="gone" value="0x00000008" /> </attr> </declare-styleable
>

這裡面重點看一下visibleMore和visibleNew裡面的兩個列舉值,這裡面與View原始碼中的visible和gone保持一致。關於如何定義屬性以及使用,可以參考我之前的部落格。

2 佈局檔案view_menu_item_m.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:landptf="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center">

    <com.landptf.view.ButtonExtendM
        android:id="@+id/bem_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginRight="8dp"
        landptf:style="iconUp" />

    <ImageView
        android:id="@+id/iv_more"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|right"
        android:background="@drawable/icon_more"
        android:visibility="gone" />

    <ImageView
        android:id="@+id/iv_new"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="top|right"
        android:background="@drawable/icon_new"
        android:visibility="gone" />

    <com.landptf.view.ButtonM
        android:id="@+id/btm_unread_count"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_gravity="top|right"
        android:textSize="12sp"
        android:visibility="gone"
        landptf:backColor="#ff0000"
        landptf:fillet="true"
        landptf:shape="oval"
        landptf:textColor="@android:color/white" />

</FrameLayout>

這裡面使用了FrameLayout,主要使用了ButtonExtendM上下結構的控制元件加上右上角的三種提示資訊,數量提示,more提示,new提示

3 MenuItemM.java

package com.landptf.view;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.landptf.R;

/**
 * Created by landptf on 2016/11/07.
 * 選單按鈕,例如底部選單的item或者訊息控制元件
 */
public class MenuItemM extends FrameLayout {

    private static final String TAG = MenuItemM.class.getSimpleName();

    /**
     * 定義控制元件
     */
    private ButtonExtendM bemMenu;
    private ImageView ivMore;
    private ImageView ivNew;
    private ButtonM btmUnReadCount;

    private OnClickListener onClickListener = null;

    public interface OnClickListener {
        void onClick(View v);
    }

    /**
     * 設定View的Click事件
     *
     * @param l
     */
    public void setOnClickListener(OnClickListener l) {
        this.onClickListener = l;
        //攔截ButtonExtendM控制元件的點選事件,使其指向this.onclick
        bemMenu.setOnClickListener(new ButtonExtendM.OnClickListener() {
            @Override
            public void onClick(View v) {
                onClickListener.onClick(v);
            }
        });
    }

    public MenuItemM(Context context) {
        super(context);
    }

    public MenuItemM(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MenuItemM(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        //載入佈局
        LayoutInflater.from(context).inflate(R.layout.view_menu_item_m, this, true);
        //初始化控制元件
        bemMenu = (ButtonExtendM) findViewById(R.id.bem_menu);
        ivMore = (ImageView) findViewById(R.id.iv_more);
        ivNew = (ImageView) findViewById(R.id.iv_new);
        btmUnReadCount = (ButtonM) findViewById(R.id.btm_unread_count);
        btmUnReadCount.setGravity(Gravity.CENTER);
        TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.MenuItemM, defStyle, 0);
        if (a != null) {
            //設定背景色
            ColorStateList colorList = a.getColorStateList(R.styleable.MenuItemM_backColor);
            if (colorList != null) {
                int backColor = colorList.getColorForState(getDrawableState(), 0);
                if (backColor != 0) {
                    setBackColor(backColor);
                }
            }
            //設定icon
            Drawable iconDrawable = a.getDrawable(R.styleable.MenuItemM_iconDrawable);
            if (iconDrawable != null) {
                setIconDrawable(iconDrawable);
            }
            //記錄View被按下時的icon的圖片
            Drawable iconDrawablePress = a.getDrawable(R.styleable.MenuItemM_iconDrawablePress);
            if (iconDrawablePress != null) {
                setIconDrawablePress(iconDrawablePress);
            }
            //設定文字的顏色
            ColorStateList textColorList = a.getColorStateList(R.styleable.MenuItemM_textColor);
            if (textColorList != null) {
                int textColor = textColorList.getColorForState(getDrawableState(), 0);
                if (textColor != 0) {
                    setTextColor(textColor);
                }
            }
            //記錄View被按下時文字的顏色
            ColorStateList textColorPressList = a.getColorStateList(R.styleable.MenuItemM_textColorPress);
            if (textColorPressList != null) {
                int textColorPress = textColorPressList.getColorForState(getDrawableState(), 0);
                if (textColorPress != 0) {
                    setTextColorPress(textColorPress);
                }
            }
            //設定顯示的文字內容
            String text = a.getString(R.styleable.MenuItemM_text);
            if (text != null) {
                setText(text);
            }
            //設定文字字型大小
            float textSize = a.getFloat(R.styleable.MenuItemM_textSize, 0);
            if (textSize != 0) {
                setTextSize(textSize);
            }
            //設定更多提示是否顯示
            int visibleMore = a.getInt(R.styleable.MenuItemM_visibleMore, -1);
            if (visibleMore != -1){
                setVisibilityMore(visibleMore);
            }
            //設定new提示是否顯示
            int visibleNew = a.getInt(R.styleable.MenuItemM_visibleNew, -1);
            if (visibleNew != -1){
                setVisibilityNew(visibleNew);
            }
            //設定訊息未讀數量
            int unReadCount = a.getInt(R.styleable.MenuItemM_unReadCount, -1);
            if (unReadCount != -1){
                setUnReadCount(unReadCount);
            }
            a.recycle();
        }

        setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onClickListener != null) {
                    onClickListener.onClick(v);
                }
            }
        });
    }

    /**
     * 設定為被選中狀態
     *   @param state in MotionEvent.ACTION_DOWN or MotionEvent.ACTION_UP
     */
    public void setPressState(int state){
        if (state != MotionEvent.ACTION_DOWN && state != MotionEvent.ACTION_UP){
            Log.w(TAG, "無效引數");
            return;
        }
        bemMenu.setPressState(state);
    }

    /**
     * 設定View的背景色
     *
     * @param backColor
     */
    public void setBackColor(int backColor) {
        bemMenu.setBackColor(backColor);
    }

    /**
     * 設定icon的圖片
     *
     * @param iconDrawable
     */
    public void setIconDrawable(Drawable iconDrawable) {
        bemMenu.setIconDrawable(iconDrawable);
    }

    /**
     * 設定View被按下時的icon的圖片
     *
     * @param iconDrawablePress
     */
    public void setIconDrawablePress(Drawable iconDrawablePress) {
        bemMenu.setIconDrawablePress(iconDrawablePress);
    }

    /**
     * 設定文字的顏色
     *
     * @param textColor
     */
    public void setTextColor(int textColor) {
        if (textColor == 0) return;
        bemMenu.setTextColor(textColor);
    }

    /**
     * 設定View被按下時文字的顏色
     *
     * @param textColorPress
     */
    public void setTextColorPress(int textColorPress) {
        if (textColorPress == 0) return;
        bemMenu.setTextColorPress(textColorPress);
    }

    /**
     * 設定顯示的文字內容
     *
     * @param text
     */
    public void setText(CharSequence text) {
        bemMenu.setText(text);
    }

    /**
     * 獲取顯示的文字
     *
     * @return
     */
    public String getText() {
        return bemMenu.getText();
    }

    /**
     * 設定文字字型大小
     *
     * @param size
     */
    public void setTextSize(float size) {
        bemMenu.setTextSize(size);
    }

    /**
     * 設定更多提示是否顯示
     *   如果顯示則先重置new和未讀數量圖示
     * @param visibleMore
     */
    public void setVisibilityMore(int visibleMore) {
        if (visibleMore == VISIBLE) {
            resetTip();
        }
        ivMore.setVisibility(visibleMore);
    }

    /**
     * 設定New提示是否顯示
     *   如果顯示則先重置更多和未讀數量圖示
     * @param visibleNew
     */
    public void setVisibilityNew(int visibleNew) {
        if (visibleNew == VISIBLE) {
            resetTip();
        }
        ivNew.setVisibility(visibleNew);
    }

    /**
     * 設定未讀數量
     *   如果小於等於0,表示隱藏
     *   如果大於99,則將其隱藏,同時顯示更多的提示
     *   如果在0-99區間,則隱藏更多和new圖示
     * @param unReadCount
     */
    public void setUnReadCount(int unReadCount){
        if (unReadCount <= 0){
            btmUnReadCount.setVisibility(GONE);
            //如果先設定100(此時會顯示ivMore),再設定0,因此此處應將ivMore同時置為GONE
            if (ivMore.getVisibility() == VISIBLE){
                ivMore.setVisibility(GONE);
            }
            return;
        }
        if (unReadCount > 99){
            setVisibilityMore(VISIBLE);
            return;
        }
        resetTip();
        btmUnReadCount.setVisibility(VISIBLE);
        btmUnReadCount.setText(unReadCount + "");
    }

    /**
     * 重置提示資訊
     */
    private void resetTip(){
        setVisibilityMore(GONE);
        setVisibilityNew(GONE);
        setUnReadCount(0);
    }

}

程式碼有點長,邏輯比較簡單,本身自定義控制元件的過程都是類似的,比較多的是對外提供的介面。

特別要注意的是使用時大小要設定為自定義,如果指定了大小或者match_parent,則子控制元件將居於左上角,無法居中。

4 最後簡單看下如何使用

<com.landptf.view.MenuItemM
    android:id="@+id/mim_home_page"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_marginLeft="32dp"
    landptf:iconDrawable="@drawable/icon_home_page"
    landptf:iconDrawablePress="@drawable/icon_home_page_press"
    landptf:textColor="#696969"
    landptf:textColorPress="#303f9f"
    landptf:text="首頁"
    />

這裡面主要使用了以下四個屬性,分別表示預設圖示和按下後顯示的圖示,以及文字顏色和按下後的文字顏色

landptf:iconDrawable="@drawable/icon_home_page"
landptf:iconDrawablePress="@drawable/icon_home_page_press"
landptf:textColor="#696969"
landptf:textColorPress="#303f9f"
final MenuItemM mimHomePage = (MenuItemM) findViewById(R.id.mim_home_page);
if (mimHomePage != null){
    //預設為選中狀態
    mimHomePage.setPressState(MotionEvent.ACTION_DOWN);
    mimHomePage.setVisibilityMore(View.VISIBLE);
    mimHomePage.setOnClickListener(new MenuItemM.OnClickListener() {
        @Override
        public void onClick(View v) {
            //按下後隱藏提示資訊
            mimHomePage.setVisibilityMore(View.GONE);
        }
    });
}

好了,就介紹到這裡了,更多的使用方法可以參考原始碼MenuItemMTestActivity.java。
全部程式碼已託管到開源中國的碼雲上,歡迎下載,地址:https://git.oschina.net/landptf/landptf.git

相關推薦

Android定義控制元件系列()—底部選單()

今天我們封裝一個底部的選單欄,這個大多數的應用都會用到,因此我們來自定義,方便以後專案的使用。 該控制元件的實現將分上下篇來介紹,先來看一個選單欄的子控制元件–MenuItemM,這個控制元件有什麼用呢?我們來看下一些主流app上的一些控制元件,如:

Android定義控制元件系列八:詳解onMeasure()(二)--利用onMeasure測量來實現圖片拉伸永不變形,解決螢幕適配問題

        上一篇文章詳細講解了一下onMeasure/measure方法在Android自定義控制元件時的原理和作用,參看博文:Android自定義控制元件系列七:詳解onMeasure()方法中如何測量一個控制元件尺寸(一),今天就來真正實踐一下,讓這兩個方法大顯神威來幫我們搞定圖片的螢幕適配問題。

Android定義控制元件系列:詳解onMeasure()方法中如何測量一個控制元件尺寸(一)

轉載請註明出處:http://blog.csdn.net/cyp331203/article/details/45027641 今天的任務就是詳細研究一下protected void onMeasure(int widthMeasureSpec, int he

Android定義控制元件系列七:詳解onMeasure()方法中如何測量一個控制元件尺寸(一)

自定義view/viewgroup要重寫的幾個方法:onMeasure(),onLayout(),onDraw()。(不熟悉的話可以檢視專欄的前幾篇文章:)。         今天的任務就是詳細研究一下protected void onMeasure(int wid

Android定義控制元件系列一:如何測量控制元件尺寸

測量控制元件尺寸(寬度、高度)是開發自定義控制元件的第一步,只有確定尺寸後才能開始畫(利用canvas在畫布上畫,我們所使用的控制元件實際上都是這樣畫上去的)。當然,這個尺寸是需要根據控制元件的各個部分計算出來的,比如:padding、文字大小,間距等。非容器控制元件的onM

android 定義控制元件種方式

如果說要按型別來劃分的話,自定義View的實現方式大概可以分為三種,自繪控制元件、組合控制元件、以及繼承控制元件。那麼下面我們就來依次學習一下,每種方式分別是如何自定義View的。 一、自繪控制元件 PS: 這篇《自定義View系列》部落格,博主

Android定義控制元件系列 十:利用新增定義佈局來搞定觸控事件的分發,解決組合介面中特定控制元件響應特定方向的事件

        這個例子是比較有用的,基本上可以說,寫完這一次,以後很多情況下,直接拿過來addView一下,然後再addInterceptorView一下,就可以輕輕鬆鬆的達到組合介面中特定控制元件來響應特定方向的觸控事件了。         在寫Android應用

Android定義控制元件系列案例【四】

案例效果: 模擬器上執行有些鋸齒,真機上和預期一樣好 案例分析: 看效果,第一直覺肯定是Android原生態控制元件中沒有這樣的控制元件實現這種效果,自然想到應該需要自定義控制元件了,沒錯,這就是通過自定義控制元件來繪製的一個圓環進度條。仔細分析發現這個效果的進度條應該

Android定義控制元件系列一:Android如何實現老版優酷客戶端三級環形選單

轉載連結:http://blog.csdn.net/cyp331203/article/details/40423727 先來看看效果: 一眼看上去好像還挺炫的,感覺比較複雜。。。實際上並不難,下面我們來看看如何實現: 基本素

Android定義控制元件系列案例【一】

Android自定義控制元件的重要性就不多說了,總之是技術進階,面試常見,高薪必備。 本篇博文的目的很簡單,就是希望通過自定義控制元件來解決一個常見需求點,從而感受一下自定義控制元件的魅力與強大。 案

android定義控制元件系列教程----繼承ViewGroup實現帶阻力效果的可回彈的SrollView

package com.example.scolview; import android.content.Context; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracke

Android定義控制元件系列二:定義開關按鈕(一)

這一次我們將會實現一個完整純粹的自定義控制元件,而不是像之前的組合控制元件一樣,拿系統的控制元件來實現;計劃分為三部分:自定義控制元件的基本部分,和自定義控制元件的自定義屬性; 下面就開始第一部分的編寫,本次以一個定義的開關按鈕為例,下面就開始吧: 先看看效果,一個點選開

Android定義控制元件系列(二)—icon+文字的多種效果實現

今天給大家帶來一個很簡單但是很常用的控制元件ButtonExtendM,在開發中我們經常會用到圖片加文字的組合控制元件,像這樣: 以上圖片都是從微信上擷取的。(暫時沒有找到icon在下,文字在上的例子) 下面我們通過一個控制元件來實現上下左右全部

Android定義控制元件開發系列)——仿支付寶六位支付密碼輸入頁面

        在移動互聯領域,有那麼幾家龍頭一直是我等學習和追求的目標,比如支付寶、微信、餓了麼、酷狗音樂等等,大神舉不勝舉,他們設計的介面、互動方式已經培養了中國(有可能會是世界)民眾的操作習慣:舉個小例子,對話方塊“確定”按鈕的左右位置就很有學問,如果大家都是左邊取消

Android定義控制元件】炫酷的底部導航欄

https://github.com/WakeHao/NavBar 基本使用 使用這個控制元件,只需要簡單的幾部 引入該控制元件到你的專案中 compile 'com.chen.wakehao.library:bottom-navigation-bar:1.0.0'

android定義控制元件() 增加內容 定義屬性 format詳解

1. reference:參考某一資源ID。     (1)屬性定義:             <declare-styleable name = "名稱">                    <attr name = "backgroun

Android定義控制元件開發系列(一)——第一次動手做定義控制元件

        Android系統提供的控制元件多種多樣,以至於很多初學者經常忘了還有這樣那樣的控制元件沒用過甚至沒聽過。儘管如此,但是系統控制元件大多比較死板,而且不夠美觀,很多多樣化的顯示或是互動

android-定義控制元件

自定義控制元件兩種方式 1、繼承ViewGroup 例如:ViewGroup , LinearLayout, FrameLayout, RelativeLayout等。 2、繼承View 例如:View, TextView, ImageView, Button等。 View的測量

Android 定義控制元件-星級評分

在學習自定義控制元件時需要一些例子來練練手,本文這個控制元件就是在這種環境下產生的(可能有BUG); 這個控制元件設計的特點: 1,可以任意修改星星數量 2,可以星星大小會隨控制元件大小而縮小,在控制元件足夠大的情況可以任意設定星星大小 3,滑動監聽,根據滑動距離選擇星級 4,可

[Android定義控制元件] Android定義控制元件

轉載自: http://blog.163.com/[email protected]/blog/static/103242241201382210910473/ 開發自定義控制元件的步驟: 1、瞭解View的工作原理  2、