StatusLayout:顯示不同狀態(tài)的布局

StatusLayout

首先附上github項目地址;https://github.com/csming1995/statuslayout

之前看過很多網(wǎng)上已有的做法享幽,大多都已經(jīng)將狀態(tài)都涵蓋了铲掐;這樣的做法,可能很難包裹所有的業(yè)務(wù)需求值桩;

于是摆霉,突發(fā)奇想,是否能夠提供給使用者更自由的使用方式奔坟;比如携栋,提供給使用者自定義某狀態(tài)布局,甚至自定義狀態(tài)及布局的自由咳秉;


這是一個復(fù)雜度不太高婉支,但是代碼設(shè)計感比較強一點的開源庫~;

先看一下源碼澜建;

public class StatusLayout extends FrameLayout{
    private static final String TAG = "StatusLayout.FrameLayout";


    /**
     * DEFAULT EMPTY NET_ERROR 默認的三種狀態(tài)
     * DEFAULT 為用戶第一次使用該組件時指定的屬性狀態(tài)
     */
    private static final int DEFAULT = 1;
    private static final int EMPTY = 2;
    private static final int NET_ERROR = 3;
    //rivate static final int LOADING = 3;


    /**
     * 屬性值
     */
    private String mInitMessage;
    private Drawable mInitImage;
    private String mInitStrInBtn;

    /**
     * Map 用鍵值對存儲 狀態(tài)-視圖
     * List 用于存儲子控件,即內(nèi)容
     */
    private Map<Integer, View> mMapMessageViews;
    private List<View> mNormalViews;

    private LayoutInflater mLayoutInflater;


    /**
     * 默認頁
     * 空數(shù)據(jù)頁
     * 網(wǎng)絡(luò)錯誤頁
     */
    private LinearLayout mDefaultView;//默認頁
    private LinearLayout mDefaultEmptyMessageView;
    private LinearLayout mDefaultNetErrorView;

    private Context mContext;

    public StatusLayout(Context context){
        this(context, null);
    }

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

    public StatusLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init(attrs);
    }


    /**
     * 一些初始化工作
     * 初始化DefaultView
     */

    private void init(AttributeSet attrs){
        if (null == mNormalViews) mNormalViews = new ArrayList<>();

        if (null == mMapMessageViews) mMapMessageViews = new HashMap<>();

        if (null == mLayoutInflater){
            mLayoutInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        TypedArray mValueArray = mContext.obtainStyledAttributes(attrs, R.styleable.StatusLayoutValue);

        mInitMessage = mValueArray.getString(R.styleable.StatusLayoutValue_attr_message);
        mInitImage = mValueArray.getDrawable(R.styleable.StatusLayoutValue_attr_image_src);
        mInitStrInBtn = mValueArray.getString(R.styleable.StatusLayoutValue_attr_str_btn);

        setEmptyMessageView();
        setNetErrorMessageView();
        setDefaultView(mInitMessage, mInitImage, mInitStrInBtn);
        mValueArray.recycle();

    }

    /**
     * 加載完布局后 使默認視圖顯示
     */
    @Override
    protected void onFinishInflate(){
        super.onFinishInflate();
        showDefaultView();
    }

    /**
     * 測量
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 通過addView函數(shù)在被調(diào)用時,對child View進行初始化
     * 獲取子控件信息
     * @param child
     * @param params
     */
    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        super.addView(child, params);
        for(int i = 0; i < getChildCount(); i ++){
            mNormalViews.add(getChildAt(i));
        }
    }

    public void showDefaultView(){
        showStatusView(DEFAULT);
    }

    public void showEmptyMessageView(){
        showStatusView(EMPTY);
    }

    public void showNetErrorView(){
        showStatusView(NET_ERROR);
    }

    /**
     * 設(shè)置為有數(shù)據(jù)狀態(tài)
     * 使當前View的子View顯示
     * 子View為RecyclerView
     * @see #setContentView(boolean)
     */
    public void showNormalView(){
        hiddenStatusViews();
        setContentView(true);
    }

    /**
     * 設(shè)置子View的顯示或隱藏狀態(tài)
     * 子View存儲于一個list中
     * @param isShown
     */
    private void setContentView(boolean isShown){
        if (isShown){
            for (View v : mNormalViews){
                v.setVisibility(VISIBLE);
            }
        }else {
            for (View v : mNormalViews){
                v.setVisibility(GONE);
            }
        }
    }

    /**
     * 無參調(diào)用的設(shè)置網(wǎng)絡(luò)錯誤頁
     * 用于內(nèi)部調(diào)用
     */
    private void setNetErrorMessageView(){
        if (null == mDefaultNetErrorView){
            mDefaultNetErrorView = (LinearLayout)mLayoutInflater.inflate(R.layout.status_layout_net_error_message, null);
        }
        mMapMessageViews.put(NET_ERROR, mDefaultNetErrorView);
    }

    /**
     * 有參調(diào)用的設(shè)置網(wǎng)絡(luò)錯誤頁
     * 提供給外部使用者
     * @param message
     * @param image
     */
    public void setNetErrorMessageView(String message, Drawable image){
        if(null == mDefaultNetErrorView){
            mDefaultNetErrorView = (LinearLayout)mLayoutInflater.inflate(R.layout.status_layout_net_error_message, null);
        }else {
            mDefaultNetErrorView = (LinearLayout)mMapMessageViews.get(NET_ERROR);
        }

        TextView mTvNetError = (TextView)mDefaultNetErrorView.findViewById(R.id.tv_net_error_view);
        ImageView mIvNetError = (ImageView)mDefaultNetErrorView.findViewById(R.id.iv_net_error_view);
        if (null != message){
            mTvNetError.setText(message);
            mTvNetError.setVisibility(VISIBLE);
        }else {
            mTvNetError.setVisibility(GONE);
        }
        if (null != image){
            mIvNetError.setImageDrawable(image);
            mIvNetError.setVisibility(VISIBLE);
        }else {
            mIvNetError.setVisibility(GONE);
        }

        mMapMessageViews.put(NET_ERROR, mDefaultNetErrorView);
    }

    /**
     * 無參調(diào)用空數(shù)據(jù)頁面
     * 用于內(nèi)部調(diào)用
     */

    private void setEmptyMessageView(){
        if (null == mDefaultEmptyMessageView) {
            mDefaultEmptyMessageView = (LinearLayout)mLayoutInflater.inflate(R.layout.status_layout_empty_message, null);
        }
        mMapMessageViews.put(EMPTY, mDefaultEmptyMessageView);
    }

    public void setEmptyMessageView(int messageId, int imageId, int messageInBtnId){
        String messageInBtn = mContext.getString(messageInBtnId);
        setEmptyMessageView(messageId, imageId, messageInBtn);
    }

    public void setEmptyMessageView(int messageId, int imageId, String messageInBtn){
        Drawable image = ContextCompat.getDrawable(mContext, imageId);
        setEmptyMessageView(messageId, image, messageInBtn);
    }

    public void setEmptyMessageView(int messageId, Drawable image, String messageInBtn){
        String message = mContext.getString(messageId);
        setEmptyMessageView(message, image, messageInBtn);
    }

    /**
     * 有參調(diào)用設(shè)置空數(shù)據(jù)頁
     * 提供給外部使用者
     * @param message
     * @param image
     * @param messageInBtn
     */

    public void setEmptyMessageView(String message, Drawable image, String messageInBtn){
        if(null == mDefaultEmptyMessageView){
            mDefaultEmptyMessageView = (LinearLayout)mLayoutInflater.inflate(R.layout.status_layout_empty_message, null);
        }else {
            mDefaultEmptyMessageView = (LinearLayout)mMapMessageViews.get(EMPTY);
        }

        TextView mTvEmpty = (TextView)mDefaultEmptyMessageView.findViewById(R.id.tv_empty_view);
        ImageView mIvEmpty = (ImageView)mDefaultEmptyMessageView.findViewById(R.id.iv_empty_view);
        Button mBtnEmpty = (Button)mDefaultEmptyMessageView.findViewById(R.id.btn_empty_view);
        if (null != message){
            mTvEmpty.setText(message);
            mTvEmpty.setVisibility(VISIBLE);
        }else {
            mTvEmpty.setVisibility(GONE);
        }
        if (null != image){
            mIvEmpty.setImageDrawable(image);
            mIvEmpty.setVisibility(VISIBLE);
        }else {
            mIvEmpty.setVisibility(GONE);
        }
        if (null != messageInBtn) {
            mBtnEmpty.setText(message);
            mBtnEmpty.setVisibility(VISIBLE);
        }else {
            mBtnEmpty.setVisibility(GONE);
        }
        mMapMessageViews.put(EMPTY, mDefaultEmptyMessageView);
    }
    /**
     * 有參調(diào)用 設(shè)置默認頁
     * 用于內(nèi)部使用
     * @param message
     * @param image
     * @param messageInBtn
     */
    private void setDefaultView(String message, Drawable image, String messageInBtn){
        if(null == mDefaultView){
            mDefaultView = (LinearLayout)mLayoutInflater.inflate(R.layout.status_layout_default_message, null);
        }else {
            mDefaultView = (LinearLayout)mMapMessageViews.get(DEFAULT);
        }

        TextView mTvDefault = (TextView)mDefaultView.findViewById(R.id.tv_default_view);
        ImageView mIvDefault = (ImageView)mDefaultView.findViewById(R.id.iv_default_view);
        Button mBtnDefault = (Button)mDefaultView.findViewById(R.id.btn_default_view);
        if (null != message){
            mTvDefault.setText(message);
            mTvDefault.setVisibility(VISIBLE);
        }else {
            mTvDefault.setVisibility(GONE);
        }
        if (null != image){
            mIvDefault.setImageDrawable(image);
            mIvDefault.setVisibility(VISIBLE);
        }else {
            mIvDefault.setVisibility(GONE);
        }
        if (null != messageInBtn) {
            mBtnDefault.setText(message);
            mBtnDefault.setVisibility(VISIBLE);
        }else {
            mBtnDefault.setVisibility(GONE);
        }
        mMapMessageViews.put(DEFAULT, mDefaultView);
    }

    /**
     * 外部添加狀態(tài)
     * 若狀態(tài)與已有狀態(tài)碰撞
     * 跳出錯誤
     * @param key
     * @param view
     * @throws IllegalNumException
     */
    public void addStatus(int key, View view) throws IllegalNumException {
        if(1 == key||2 == key||3 == key) {
            throw new IllegalNumException();
        }
        mMapMessageViews.put(key, view);
    }

    /**
     * 顯示指定狀態(tài)頁
     * 并將其他頁面隱藏
     * 用于內(nèi)部以及外部電泳
     * @param key
     */
    public void showStatusView(int key){
        setContentView(false);
        View mMessageView = mMapMessageViews.get(key);
        hiddenStatusViews();
        addView(mMessageView);
        mMessageView.setVisibility(VISIBLE);
    }

    /**
     * 隱藏mMapMessageViews的所有頁面
     */

    private void hiddenStatusViews(){
        for (View v : mMapMessageViews.values()){
            removeView(v);
        }
    }

}

  • 首先是: 這三個方法初始化了三種基本布局向挖;這三個方法用于定義了每一種布局的默認狀態(tài)下的文字及圖片;首先在初始化的時候調(diào)用炕舵;
  • 他們最終是將初始化后的布局何之,加入mMapMessageViews中保存;mMapMessageViews的鍵值對為:狀態(tài)-布局咽筋;我們后面在顯示的時候溶推,將會從這個map中,通過狀態(tài)key奸攻,獲取對應(yīng)的布局蒜危;
public void setEmptyMessageView();
public void setEmptyMessageView(int messageId, int imageId, int messageInBtnId);
public void setEmptyMessageView(int messageId, int imageId, String messageInBtn);
public void setEmptyMessageView(int messageId, Drawable image, String messageInBtn);
public void setEmptyMessageView(String message, Drawable image, String messageInBtn);

public void setNetErrorMessageView();
public void setNetErrorMessageView(String message, Drawable image);

private void setDefaultView(String message, Drawable image, String messageInBtn);
  • 然后: 在onFinishInflate()布局加載完成后,先顯示defaultView
    @Override
    protected void onFinishInflate(){
        super.onFinishInflate();
        showDefaultView();
    }
  • showDefaultView()方法舞箍,和其他的showXxx()方法一樣:
public void showDefaultView(){
        showStatusView(DEFAULT);
    }

最終調(diào)用的是showStatusView()這個方法舰褪;而showStatusView()方法皆疹,傳入一個key疏橄,然后從mMapMessageViews中獲取對應(yīng)的布局,并隱藏其他布局略就,最后顯示當前布局捎迫;

public void showStatusView(int key){
        setContentView(false);
        View mMessageView = mMapMessageViews.get(key);
        hiddenStatusViews();
        addView(mMessageView);
        mMessageView.setVisibility(VISIBLE);
    }

然后這里有一個setContentView()函數(shù),他的意義在于表牢,控制子布局的顯示與隱藏窄绒;

因為,我們的布局崔兴,在有數(shù)據(jù)狀態(tài)下彰导,應(yīng)該顯示的是其子布局的內(nèi)容蛔翅;

例如:一個RecyclerView;

<com.csm.Component.StatusLayout
        android:id="@+id/statuslayout_demo"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:attr_message="@string/str_there_has_nothing"
        app:attr_image_src="@mipmap/ic_launcher">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv_demo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@mipmap/ic_launcher_round"/>
    </com.csm.Component.StatusLayout>

那么調(diào)用setContentView()位谋,就可以控制其子布局的顯示與隱藏山析;主要在于遍歷布局下的所有子布局,然后設(shè)置他們的顯示隱藏掏父;

 /**
     * 設(shè)置子View的顯示或隱藏狀態(tài)
     * 子View存儲于一個list中
     * @param isShown
     */
    private void setContentView(boolean isShown){
        if (isShown){
            for (View v : mNormalViews){
                v.setVisibility(VISIBLE);
            }
        }else {
            for (View v : mNormalViews){
                v.setVisibility(GONE);
            }
        }
    }

以上就是布局內(nèi)容的顯示部分笋轨;


然后,關(guān)鍵的赊淑,如何提供給使用者自定義狀態(tài)及對應(yīng)布局的邏輯爵政,主要是維護了一個map,以及幾種狀態(tài)值;

private Map<Integer, View> mMapMessageViews;

/**
     * DEFAULT EMPTY NET_ERROR 默認的三種狀態(tài)
     * DEFAULT 為用戶第一次使用該組件時指定的屬性狀態(tài)
     */
    private static final int DEFAULT = 1;
    private static final int EMPTY = 2;
    private static final int NET_ERROR = 3;

以上三種是默認值陶缺;

如果使用者需要自定義狀態(tài)及布局钾挟,則只能定義除了這三個數(shù)字以外的數(shù)字;

為此饱岸,我特意編寫了一個Exception類型:如果使用者自定義的key是1/2/3的話等龙,則拋出錯誤;


/**
 * Created by csm on 2017/7/7.
 */

public class IllegalNumException extends Exception {
    public IllegalNumException(){}

    public IllegalNumException(String gripe){
        super(gripe);
    }

    @Override
    public void printStackTrace(){
        super.printStackTrace();
        System.out.print("You can't choice 1,2,3 as your status key");
    }
}

該開源庫已經(jīng)上傳到github上了伶贰;

https://github.com/csming1995/statuslayout

各種求star蛛砰;


二維碼~
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市黍衙,隨后出現(xiàn)的幾起案子泥畅,更是在濱河造成了極大的恐慌,老刑警劉巖琅翻,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件位仁,死亡現(xiàn)場離奇詭異,居然都是意外死亡方椎,警方通過查閱死者的電腦和手機聂抢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棠众,“玉大人琳疏,你說我怎么就攤上這事≌⒛茫” “怎么了空盼?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長新荤。 經(jīng)常有香客問我揽趾,道長,這世上最難降的妖魔是什么苛骨? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任篱瞎,我火速辦了婚禮苟呐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俐筋。我一直安慰自己掠抬,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布校哎。 她就那樣靜靜地躺著两波,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闷哆。 梳的紋絲不亂的頭發(fā)上腰奋,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音抱怔,去河邊找鬼劣坊。 笑死,一個胖子當著我的面吹牛屈留,可吹牛的內(nèi)容都是我干的局冰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼灌危,長吁一口氣:“原來是場噩夢啊……” “哼康二!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起勇蝙,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤沫勿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后味混,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體产雹,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年翁锡,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔓挖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡馆衔,死狀恐怖瘟判,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哈踱,我是刑警寧澤荒适,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布梨熙,位于F島的核電站开镣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咽扇。R本人自食惡果不足惜邪财,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一陕壹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧树埠,春花似錦糠馆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绊袋,卻和暖如春毕匀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背癌别。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工皂岔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人展姐。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓躁垛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親圾笨。 傳聞我的和親對象是個殘疾皇子教馆,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)擂达,斷路器活玲,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件谍婉、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,033評論 4 62
  • 剛才看了電影《時時刻刻》舒憾,喟嘆大于感慨,不似當年拙劣模仿女主人公的一言一行穗熬,因為真理生根之后镀迂,世界的潮流便只是潮流...
    橋小一閱讀 312評論 0 0
  • 2016年6月1日,星期三唤蔗,晴探遵,30℃--20℃ 這是兒子有記憶以來的第一個兒童節(jié)。 于是妓柜,爸爸決定帶他去動植物園...
    東梅閱讀 113評論 0 0