快速簡單的定制一個時間軸布局(LinearLayout)(2)

先搬運之前的文章吧。。。
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0121/3902.html
Git:
https://github.com/razerdp/UnderLineLinearLayout

這一次要講的是如何實現LineGravity:

在github上維護了幾次,首次提交只實現了垂直的timeline,第二次提交實現了水平的timeline,第三次就是linegravity了。

從解析里面,不難看出這個簡單的timeline根本方案就是直接在draw里面畫出我們所需要的元素。畢竟view什么的歸根到底不過是一個畫布,我們要做的,就是draw something,對吧。

不過因為我們寫的是程序,沒有辦法像我們人類一樣,手到哪里畫哪里,于是乎只好一步一步教程序怎么畫了。

在前兩次提交的時候,我們可以知道,畫垂直的timeline,其參考點為屏幕的左邊,也就是x=0,通過line_margin_side來動態調整我們線離布局左邊的距離。同理,畫水平的timeline,其參考點就是top了(y=0)。

而我們的結點則是通過top(垂直timeline) 和 left(水平timeline)以及子控件的padding來確定點的位置,同時引入line_dynamic_dimen來允許我們手動微調。

那么基本原理說完了,就說這個linegravity,從上面的一段話不難看出,timeline的位置最主要是依賴于參考點(或者說參考邊),那么我們要實現linegravity,就意味著要可以根據不同的值來設置不同的參考邊。

但是因為時間軸的特殊性,我們需要進行一下限制:

  • 如果是垂直的時間軸,那么我們設置linegravity在top或bottom將會毫無意義,因此在這兩個gravity需要給定默認值(left)

  • 同理,水平的時間軸,我們設置linegravity在left或right將會毫無意義,同樣給定默認值(top)

  • 無論是哪種時間軸,linegravity都會支持middle,時間軸將顯示在布局的中間。

以上三個限定將會在下面的代碼說到。

既然我們的條件和需求都定好了,那么我們現在就開始干活~

首先修改一下我們的attrs.xml,引入我們需要的條件:

<resources>
    <declare-styleable name="UnderLineLinearLayout">
        <!--時間軸偏移值-->
        <attr name="line_margin_side" format="dimension"/>
        <!--時間軸動態調整值-->
        <attr name="line_dynamic_dimen" format="dimension"/>
        <!--線寬-->
        <attr name="line_stroke_width" format="dimension"/>
        <!--線的顏色-->
        <attr name="line_color" format="color"/>
        <!--點的大小-->
        <attr name="point_size" format="dimension"/>
        <!--點的顏色-->
        <attr name="point_color" format="color"/>
        <!--圖標-->
        <attr name="icon_src" format="reference"/>
        <!--時間軸的gravity-->
        <!--the gravity of the timeline-->
        <attr name="line_gravity">
            <enum name="Left" value="2"/>
            <enum name="Right" value="4"/>
            <enum name="Middle" value="0"/>
            <enum name="Top" value="1"/>
            <enum name="Bottom" value="3"/>
        </attr>
    </declare-styleable>
</resources>

在attr里面,我把幾個值定義為left/right為偶數,top/bottom為基數是有一定意義的,當然,middle因為是通用的,隨便給個就好。

接下來我們在構造器里面獲取,同時在java定義好對應的數值(代碼只給出增量代碼,非完整代碼,完整代碼請到github查看)

//=============================================================line gravity常量定義
public static final int GRAVITY_LEFT = 2;
public static final int GRAVITY_RIGHT = 4;
public static final int GRAVITY_MIDDLE =0;
public static final int GRAVITY_TOP = 1;
public static final int GRAVITY_BOTTOM = 3;
//line gravity(默認垂直的左邊)
private int lineGravity = GRAVITY_LEFT;

private int rootLeft;
private int rootMiddle;
private int rootRight;
private int rootTop;
private int rootBottom;
//參照點
private int sideRelative;

public UnderLineLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {  
  super(context, attrs, defStyleAttr);  
  ... ...
  lineGravity = attr.getInt(R.styleable.UnderLineLinearLayout_line_gravity, GRAVITY_LEFT);   
  ... ...
  initView(context);
}

OK,構造器我們獲取了對應值之后,接下來就是要到ondraw里面開始我們的計算參考點了。

@Overrideprotected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    calculateSideRelative();
    if (drawLine) {
        drawTimeLine(canvas);
    }
}

private void calculateSideRelative() {
    rootLeft = getLeft();
    rootTop = getTop();
    rootRight = getRight();
    rootBottom = getBottom();
    if (curOrientation == VERTICAL) rootMiddle = (rootLeft + rootRight) >> 1;
    if (curOrientation == HORIZONTAL) rootMiddle = (rootTop + rootBottom) >> 1;
    boolean isCorrect=(lineGravity==GRAVITY_MIDDLE||(lineGravity+curOrientation)%2!=0);
    if (isCorrect){
        switch (lineGravity){
            case GRAVITY_TOP:
                sideRelative=rootTop;
                break;
            case GRAVITY_BOTTOM:
                sideRelative=rootBottom;
                break;
            case GRAVITY_LEFT:
                sideRelative=rootLeft;
                break;
            case GRAVITY_RIGHT:
                sideRelative=rootRight;
                break;
            case GRAVITY_MIDDLE:
                sideRelative=rootMiddle;
                break;
        }
    }else {
        sideRelative=0;
    }
}

在calculateSideRelative方法里面,按照我原來的想法,也是最直接,最粗暴的做法就是 通過switch判斷當前orientation是垂直的還是水平的,然后分兩塊,垂直的就判斷4個linegravity,水平的也判斷4個linegravity,將非法的賦給默認值。。。

但考慮到在ondraw里面執行的方法,不應該進行大量的操作,同時看到系統定義的VERTICAL和HORIZONTAL分別是1和0,于是乎就想到了判斷余數來判斷合法性,這樣做我們不用關心垂直或者水平,只需要關心linegravity,起碼去掉了一半的判斷。

首先我們引入一個布爾值,來判斷當前的linegravity是否合法,判斷規則如下:

  • 當前lineGravity==MIDDLE,無論orientation是什么,都視為合法

  • 當前lineGravity+orientation 模2,余數不為0則合法。

    • 比如:
      • 當前lineGravity=LEFT,orientation=Vertical,而我們的left=2,加上orientation就是等于3,除以2余1,所以合法。
      • 當前lineGravity=LEFT,orientation=Horizontal,加起來等于2,除以2余數為0,所以非法
      • 當然,我們的值可以再設計一下,弄成判斷余數為0則合法也是可以的。
  • 如果是合法狀態,則通過switch判斷當前的lineGravity并給參考點賦值,非法狀態統一給0

以上就是計算參考點的方法了,剩下的就是在draw時的操作了。

為了篇幅,以下僅展示繪制第一個點的操作:

 private void drawFirstChildViewVertical(Canvas canvas) {
        if (getChildAt(0) != null) {
            int top = getChildAt(0).getTop();
            //記錄值
            firstX = sideRelative>=rootMiddle?(sideRelative-lineMarginSide):(sideRelative+lineMarginSide);
            firstY = top + getChildAt(0).getPaddingTop() + lineDynamicDimen;
            //畫一個圓
            canvas.drawCircle(firstX, firstY, pointSize, pointPaint);
        }
    }

其中改變的地方在:

firstX = sideRelative>=rootMiddle?(sideRelative-lineMarginSide):(sideRelative+lineMarginSide);

這個位置引入了我們的sideRelative,也就是前面那個方法計算出來的參考點,那么為什么我們要跟布局的中線比較呢,原因很簡單,比如我們定義了一個子控件的在父控件的gravity=right,然后我們margin,這時候子控件肯定是向左移動而不是向右移動對吧,所以我們定義小于中線的時候,坐標采取參考點+偏移值,大于中線的時候,坐標采取參考點-偏移值以符合我們平時的操作習慣。

以上,就是關于這個簡易時間軸布局實現lineGravity的方法。

因為繼承LinearLayout,動態添加的時候基本靠inflate,所以如果數據量很大的時候就不建議使用了,那時候建議用listview。該控件適用于支付流程的展示或者一些數據量比較小的地方。

最后這是我在簡書的第一篇文章,也是展示的第一個控件,對于這個控件有什么好的建議或者想法或者更好玩的idea,歡迎留言-V-也歡迎github pr

【END】

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

推薦閱讀更多精彩內容