年前最后一發:帶分割線的LinearLayout

前言

分割線場景

在開發過程中,界面中條目間都需要我們設置分割線。大多時候,我們都通過如下布局代碼片段設置:

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="#cccccc"/>

如上寫法雖也方便,但會導致出現很多此類重復代碼。另外,當我們需要隱藏其中某一條目之時,分割線同樣也需做隱藏處理,較為繁瑣。
因此,針對此類場景,我在LinearLayout可以設置分割線樣式的屬性的基礎上,加以改進,提煉出使用場景更廣的DividerLinearLayout。

簡介

項目地址:DividerLinearLayout
LinearLayout在Android3.0以上版本支持設置分割線樣式的屬性。關于該屬性的具體使用方法,本文不在敘述。
DividerLinearLayout效果圖如下:

效果圖

有人可能會說,既然LinearLayout本身支持設置分割線,為啥你要多此一舉。因為LinearLayout的分割線,僅支持四種模式。
SHOW_DIVIDER_NONE,無分割線。
SHOW_DIVIDER_BEGINNING,第一個可見child view前添加分割線。
SHOW_DIVIDER_MIDDLE,在兩個可見View中間添加分割線。
SHOW_DIVIDER_END,最后一個可見child view前添加分割線。
LinearLayout的分割線無法同時設置以上所有模式。
另外LinearLayout的分割線是Drawable格式(比較重),大多情況下我們僅僅是要設置一條線而已。
DividerLayout支持的屬性如下:
1.設置分割線顏色。
2.設置分割線的寬度。
3.設置頭分割線是否可見。
4.設置尾分割線是否可見。
5.設置頭分割線的padding。
6.設置尾分割線的padding。
7.支持代碼動態設置以上所有屬性。

源碼片段

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

import com.example.testui.R;

/**
 * Created by chenjiawei on 16/12/8.
 */

public class DividerLinearLayout extends LinearLayout {

    private int mDividerWidth = 0;

    private boolean mHeaderDividerEnable = false;

    private boolean mFooterDividerEnable = false;

    private int mDividerColor;

    private int mHeaderDividerPaddingEnd;

    private int mHeaderDividerPaddingStart;

    private int mFooterDividerPaddingEnd;

    private int mFooterDividerPaddingStart;

    private GradientDrawable mDividerDrawable;


    public DividerLinearLayout(Context context) {
        super(context);
        initDividerDrawable();
    }

    public DividerLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        resolveAttributeSet(attrs);
        adjustPadding();
        initDividerDrawable();
    }

    public DividerLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        resolveAttributeSet(attrs);
        adjustPadding();
        initDividerDrawable();
    }


    private void resolveAttributeSet(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.borderLinearLayout);
        for (int i = 0; i < typedArray.getIndexCount(); i++) {
            int index = typedArray.getIndex(i);
            switch (index) {
                case R.styleable.borderLinearLayout_dividerColor:
                    mDividerColor = typedArray.getColor(index, getResources().getColor(R.color.defaultDividerColor));
                    break;
                case R.styleable.borderLinearLayout_dividerWidth:
                    mDividerWidth = (int) typedArray.getDimension(index, 0);
                    break;
                case R.styleable.borderLinearLayout_footerDividerEnable:
                    mHeaderDividerEnable = typedArray.getBoolean(index, false);
                    break;
                case R.styleable.borderLinearLayout_headerDividerEnable:
                    mFooterDividerEnable = typedArray.getBoolean(index, false);
                    break;
                case R.styleable.borderLinearLayout_headerDividerPaddingStart:
                    mHeaderDividerPaddingStart = (int) typedArray.getDimension(index, 0);
                    break;
                case R.styleable.borderLinearLayout_headerDividerPaddingEnd:
                    mHeaderDividerPaddingEnd = (int) typedArray.getDimension(index, 0);
                    break;
                case R.styleable.borderLinearLayout_footerDividerPaddingStart:
                    mFooterDividerPaddingStart = (int) typedArray.getDimension(index, 0);
                    break;
                case R.styleable.borderLinearLayout_footerDividerPaddingEnd:
                    mFooterDividerPaddingEnd = (int) typedArray.getDimension(index, 0);
                    break;
                default:
                    break;

            }
        }
        typedArray.recycle();
    }

    void initDividerDrawable() {
        setShowDividers(SHOW_DIVIDER_MIDDLE);
        setDividerDrawableAttributeSet();
    }

    void setDividerDrawableAttributeSet() {
        mDividerDrawable = new GradientDrawable();
        mDividerDrawable.setColor(mDividerColor);
        mDividerDrawable.setShape(GradientDrawable.RECTANGLE);
        if (getOrientation() == VERTICAL) {
            mDividerDrawable.setSize(getMeasuredWidth(), mDividerWidth);
        } else {
            mDividerDrawable.setSize(mDividerWidth, getMeasuredHeight());
        }
        setDividerDrawable(mDividerDrawable);
    }

    void adjustPadding() {
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeaderDividerEnable) {
            drawHeaderDivider(canvas);
        }
        if (mFooterDividerEnable) {
            drawFooterDivider(canvas);
        }
    }

    @Override
    public void setPadding(int left, int top, int right, int bottom) {
        if (mHeaderDividerEnable) {
            if (getOrientation() == VERTICAL) {
                top = mDividerWidth + top;
            } else {
                left = mDividerWidth + left;
            }
        }
        if (mFooterDividerEnable) {
            if (getOrientation() == VERTICAL) {
                bottom = mDividerWidth + bottom;
            } else {
                right = mDividerWidth + right;
            }
        }
        super.setPadding(left, top, right, bottom);
    }

    /**
     * Set the visible state of header divider
     *
     * @param visible True if the header divider is Shown, false otherwise
     */
    public void showHeaderDivider(boolean visible) {
        if (mHeaderDividerEnable != visible) {
            resetPadding();
            this.mHeaderDividerEnable = visible;
            adjustPadding();
            postInvalidate();
        }
    }

    /**
     * Set the visible state of footer divider
     *
     * @param visible True if the footer divider is Shown, false otherwise
     */
    public void showFooterDivider(boolean visible) {
        if (mFooterDividerEnable != visible) {
            resetPadding();
            this.mFooterDividerEnable = visible;
            adjustPadding();
            postInvalidate();
        }
    }

    void resetPadding() {
        if (mFooterDividerEnable && mDividerWidth > 0) {
            if (getOrientation() == VERTICAL) {
                super.setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom() - mDividerWidth);
            } else {
                super.setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight() - mDividerWidth, getPaddingBottom());
            }
        }
        if (mHeaderDividerEnable && mDividerWidth > 0) {
            if (getOrientation() == VERTICAL) {
                super.setPadding(getPaddingLeft(), getPaddingTop() - mDividerWidth, getPaddingRight(), getPaddingBottom());
            } else {
                super.setPadding(getPaddingLeft() - mDividerWidth, getPaddingTop(), getPaddingRight(), getPaddingBottom());
            }
        }
    }


    /**
     * Set the width value of divider
     *
     * @param width the divider width in px
     */
    public void setDividerWidth(int width) {
        if (mDividerWidth != width) {
            resetPadding();
            this.mDividerWidth = width;
            adjustPadding();
            setDividerDrawableAttributeSet();
        }
    }

    /**
     * Set the color value of divider
     *
     * @param color the color of divider
     */
    public void setDividerColor(int color) {
        if (mDividerColor != color) {
            this.mDividerColor = color;
            setDividerDrawableAttributeSet();
            postInvalidate();
        }
    }

    /**
     * Set the padding of header divider
     *
     * @param start the start padding in pixels
     * @param end   the end padding in pixels
     */
    public void setHeaderDividerPadding(int start, int end) {
        if (mHeaderDividerPaddingStart != start || mHeaderDividerPaddingEnd != end) {
            this.mHeaderDividerPaddingStart = start;
            this.mHeaderDividerPaddingEnd = end;
            postInvalidate();
        }
    }

    /**
     * Set the padding of footer divider
     *
     * @param start the start padding in pixels
     * @param end   the end padding in pixels
     */
    public void setFooterDividerPadding(int start, int end) {
        if (mFooterDividerPaddingStart != start || mFooterDividerPaddingEnd != end) {
            this.mFooterDividerPaddingStart = start;
            this.mFooterDividerPaddingEnd = end;
            postInvalidate();
        }
    }

    private void drawHeaderDivider(Canvas canvas) {
        View child = findFirstVisibleView();
        if (child != null) {
            if (getOrientation() == VERTICAL) {
                drawHeaderDividerVertical(child, canvas);
            } else {
                drawHeaderDividerHorizontal(child, canvas);
            }
        }
    }

    private void drawHeaderDividerVertical(View child, Canvas canvas) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        int top = child.getTop() - lp.topMargin - mDividerWidth;
        mDividerDrawable.setBounds(mHeaderDividerPaddingStart, top,
                getMeasuredWidth() - mHeaderDividerPaddingEnd, top + mDividerWidth);
        mDividerDrawable.draw(canvas);
    }

    private void drawHeaderDividerHorizontal(View child, Canvas canvas) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        int left = child.getLeft() - lp.leftMargin - mDividerWidth;
        mDividerDrawable.setBounds(left, mHeaderDividerPaddingStart, left + mDividerWidth, getMeasuredHeight() - mHeaderDividerPaddingEnd);
        mDividerDrawable.draw(canvas);
    }

    private void drawFooterDivider(Canvas canvas) {
        View child = findLastVisibleView();
        if (child != null) {
            if (getOrientation() == VERTICAL) {
                drawFooterDividerVertical(child, canvas);
            } else {
                drawFooterDividerHorizontal(child, canvas);
            }
        }
    }

    private void drawFooterDividerVertical(View child, Canvas canvas) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        int bottom = child.getBottom() + lp.bottomMargin + mDividerWidth;
        mDividerDrawable.setBounds(mFooterDividerPaddingStart, bottom - mDividerWidth,
                getMeasuredWidth() - mFooterDividerPaddingEnd, bottom);
        mDividerDrawable.draw(canvas);
    }

    private void drawFooterDividerHorizontal(View child, Canvas canvas) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        int right = child.getRight() + lp.rightMargin + mDividerWidth;
        mDividerDrawable.setBounds(right - mDividerWidth, mFooterDividerPaddingStart, right, getMeasuredHeight() - mFooterDividerPaddingEnd);
        mDividerDrawable.draw(canvas);
    }

    View findFirstVisibleView() {
        if (getChildCount() > 0) {
            for (int i = 0; i < getChildCount(); i++) {
                if (getChildAt(i) != null && getChildAt(i).getVisibility() != GONE) {
                    return getChildAt(i);
                }
            }
        }
        return null;
    }

    View findLastVisibleView() {
        if (getChildCount() > 0) {
            for (int i = getChildCount() - 1; i >= 0; i--) {
                if (getChildAt(i) != null && getChildAt(i).getVisibility() != GONE) {
                    return getChildAt(i);
                }
            }
        }
        return null;
    }
}

最后

歡迎喜歡點贊,謝謝支持!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容