自己擼一個StepView

自定義StepViews
就是模仿網(wǎng)上那個挺熱門的一系列步驟的view(看了一下圖片效果,自己嘗試寫一個肩祥,鍛煉自己的自定義view)
先上圖


device-2017-02-22-171533.png

首先明確一下這個view是干嘛的

1混狠、用于顯示步驟
2、分為橫向贡避、縱向
3予弧、我定義的存在點擊交互
4、這個view主要是用畫筆畫出來的皇筛,畫圓坠七,路徑,虛線
5拄踪、涉及到view的測繪
6拳魁、涉及到path潘懊,以及DashPathEffect
目前想到的就是這么多,等開始寫了遇到了在網(wǎng)上補充

開工
首先新建一個view叫做 StepViews ->extends View
實現(xiàn)他的構造方法

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

public StepViews(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public StepViews(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
}

一個參數(shù)的是在java代碼中new出來的
兩個參數(shù)的是在布局中救恨,第二個是樣式释树,
通過this,讓所有構造方法都調用第三種擎淤,這樣方便管理嘴拢,查看view的源碼寂纪,view也是通過這種方式,方便了Google的工
程師不斷更新抢腐,添加view的構造方法而不會影響之前的代碼

創(chuàng)建一個init方法用于初始化

private void init(Context context, AttributeSet attrs) {
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.StepViews);
    mOrientation = ta.getInt(R.styleable.StepViews_orientation, 1);
    ta.recycle();

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mLinePath = new Path();
}

這里進行獲取view的屬性襟交,以及對畫筆,路徑的初始化啼染,畫筆最好在這里初始化焕梅,不要在onDraw方法中(會導致內(nèi)存抖動)
贞言,

接下來就是onmeasure方法了,因為這是我第一次寫自定義view的文章,這里講的會比較細弟蚀,也很有可能出錯酗失,非常歡迎您指出,共同成長
首先要明確onMeasure是要干什么
onMeasure是要對view進行測量設置view的寬高
對于傳遞過來的參數(shù)有兩個widthMeasureSpec捶闸,heightMeasureSpec
這是1個32位整型拖刃,高兩位表示的測量模式(為什么是兩位兑牡?因為有三種測量模式。后面30位是測量值)
這兩個參數(shù)傳遞過來硬耍,告訴view的測量模式是什么边酒,他自己測繪的寬高是多少
但是有人就會說了 這不是view都給我們測繪好了,我們還測量什么坯认?
其實不是這樣的氓涣,通過查看view的源碼發(fā)現(xiàn)

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}


 public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

這里發(fā)現(xiàn)view 對AT_MOST和EXACTLY進行穿透處理劳吠,所以AT_MOST和EXACTLY得到的結果是一樣,這樣就不行了淳附,我們自己設置自己的view是wrap_content蠢古,但是得出來的卻是match_parent,肯定不可以的
這時我們就需要處理AT_MOST這種測繪模式洽糟,
對于其他的測繪模式堕战,大家肯定也都在別的博客中了解,也很明白浇雹。我開始自定義view時最不明白就是AT_MOST,這里就著重講解一下我理解的AT_MOST
AT_MOST
我理解是view要多大 就給多大屿讽,但是只要包裹住view就好伐谈,就像是給view覆層修身膜一樣,這樣 就明確了抠蚣,在處理的AT_MOST的時候就是要一個剛好包裹上view的膜就可以了
所以我們就可以開工了履澳,首先是你要明白你這個view心中最小是多少minValue怀跛,然后是跟測繪出來的ViewSize吻谋,取出最小值就行了
下面就是我onmeasure的過程(過程中處理了一下對當minValue值大于屏幕寬度的時候對minValue進行一個縮小),至于是否帶上padding(我認為在處理測繪的時候现横,Padding是處理里面的偏向,不應該放在onMeasure中處理骇两,應該放在onDraw中)

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mSteps.size() == 0) {
        setMeasuredDimension(0, 0);
    } else {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (heightMode == MeasureSpec.EXACTLY) {
            mHeight = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            mHeight = Math.min(mCircleRadius * 2, heightSize);
        } else {
            mHeight = heightSize;
        }

        int desireWidth = (mCircleRadius * mSteps.size() + mLineLenth * (mSteps.size() - 1)) * 2;
        if (widthMode == MeasureSpec.EXACTLY) {
            if (desireWidth > widthSize) {
                float v = desireWidth * 1f / widthSize;
                mCircleRadius = (int) (mCircleRadius * 1f / v);
                mLineLenth = (int) (mLineLenth * 1f / v);
            }
            mWidth = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            if (desireWidth > mScreenWidth) {
                float v = desireWidth * 1f / mScreenWidth;
                mCircleRadius = (int) (mCircleRadius * 1f / v);
                mLineLenth = (int) (mLineLenth * 1f / v);
                desireWidth = (mCircleRadius * mSteps.size() + mLineLenth * (mSteps.size() - 1)) * 2;
            }
            mWidth = Math.min(desireWidth, widthSize);
        } else {
            mWidth = widthSize;
        }
        setMeasuredDimension(mWidth, mHeight);
    }

}

接下來就是onDraw方法 onDraw就是讓你在這個view的圖紙上畫圖低千,它給你提供一個canvas
先上代碼栋操,這個是畫這個view的方法

@Override
protected void onDraw(Canvas canvas) {

    if (mStepCount == 0) {
        super.onDraw(canvas);
    } else {
        int realWidth = mWidth - getPaddingLeft() - getPaddingRight();
        int averageWidth = realWidth / mStepCount;
        int realHeight = mHeight - getPaddingTop() - getPaddingBottom();
        float x = averageWidth / 2;
        float y = mCircleRadius + 4;

        float textX = 0;
        float textY = mCircleRadius * 2 + 8;
        for (int i = 0; i < mStepCount; i++) {
            Rect rect = new Rect();
            mPaint.getTextBounds(mSteps.get(i), 0, mSteps.get(i).length(), rect);

            if (i < mCurrentSteps) {
                mPaint.setColor(FINISH_COLOR);
                mPaint.setPathEffect(null);
            } else if (i == mCurrentSteps) {
                mPaint.setColor(CURRENT_COLOR);
                mPaint.setPathEffect(mDashPathEffect);
            } else {
                mPaint.setColor(LAST_COLOR);
                mPaint.setPathEffect(mDashPathEffect);
            }
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(x, y, mCircleRadius, mPaint);

            mPaint.setColor(Color.WHITE);
            canvas.drawText(mSteps.get(i), x - rect.width() / 2, textY + rect.height(), mPaint);
            mPaint.setStyle(Paint.Style.STROKE);

            if (i < mStepCount - 1) {
                float moveStart = x + mCircleRadius;
                float lineStart = x + averageWidth - mCircleRadius;
                mLinePath.reset();
                mLinePath.moveTo(moveStart, y);
                mLinePath.lineTo(lineStart, y);
                mPaint.setColor(Color.WHITE);
                canvas.drawPath(mLinePath, mPaint);
            }
            x = x + averageWidth;
            textX = textX + averageWidth / 2;
        }
    }

}

首先明白 你要畫什么矾芙?(畫圓圈剔宪,直線壹无,虛線,文字) 要怎么畫地淀?(我直接就先橫著畫了岖是,先打算完成這一版,然后開始寫支持豎著畫的)
明白了自己的目的那么就開工
首先要學會偷懶烈疚,你發(fā)現(xiàn)你傳來的list里面什么都沒有聪轿,那么你就什么也不用畫了,這就是一個if判斷的作用灯抛。
然后你就開始正式的畫
第一:首先你要知道你畫布的寬高,注意這里是畫布的寬高素邪,所以你要去除你上下左右的padding猪半,所以就是
int realWidth = mWidth - getPaddingLeft() - getPaddingRight();
int averageWidth = realWidth / mStepCount;
int realHeight = mHeight - getPaddingTop() - getPaddingBottom();

然后你需要將你的寬度按照有多少步來進行分塊磨确,這樣你畫的每個圈 就是在這個塊里声邦,這樣畫起來比較簡單。
接下來就是畫圈 文字

for (int i = 0; i < mStepCount; i++) {
            Rect rect = new Rect();
            mPaint.getTextBounds(mSteps.get(i), 0, mSteps.get(i).length(), rect);

            if (i < mCurrentSteps) {
                mPaint.setColor(FINISH_COLOR);
                mPaint.setPathEffect(null);
            } else if (i == mCurrentSteps) {
                mPaint.setColor(CURRENT_COLOR);
                mPaint.setPathEffect(mDashPathEffect);
            } else {
                mPaint.setColor(LAST_COLOR);
                mPaint.setPathEffect(mDashPathEffect);
            }
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(x, y, mCircleRadius, mPaint);

            mPaint.setColor(Color.WHITE);
            canvas.drawText(mSteps.get(i), x - rect.width() / 2, textY + rect.height(), mPaint);
            mPaint.setStyle(Paint.Style.STROKE);

            if (i < mStepCount - 1) {
                float moveStart = x + mCircleRadius;
                float lineStart = x + averageWidth - mCircleRadius;
                mLinePath.reset();
                mLinePath.moveTo(moveStart, y);
                mLinePath.lineTo(lineStart, y);
                mPaint.setColor(Color.WHITE);
                canvas.drawPath(mLinePath, mPaint);
            }
            x = x + averageWidth;
            textX = textX + averageWidth / 2;
}

這里要說明一下虛線的實現(xiàn),new DashPathEffect(new float[]{8, 4}, 0); 前面的數(shù)組的意思是實線多長骗炉,虛線多長蛇受,第二個參數(shù)是第一條實線的偏移量。
這樣就實現(xiàn)了虛線

這就是我stepView的實現(xiàn)過程.
接下來我要實現(xiàn)stepview里那樣 有對號的乍丈,我之前的想法是畫出來對號把将,但是我發(fā)現(xiàn)太他么的墨跡了,所以后來我就無恥的查看了一下stepview的代碼请垛,嘿嘿嘿洽议,發(fā)現(xiàn)是圖片做的绞铃,所以接下里我也打算
1.寫一版本圖片做成的StepView (已經(jīng)寫好了地址是https://github.com/bolevw/LBViews/blob/master/app/src/main/java/com/test/lbviews/views/ImageStepViews.java
2.然后是發(fā)現(xiàn)了一個 DashPathEffect的動畫效果,也寫一個文章講一下
3.屬性動畫實現(xiàn)的圓形進度條

未來的打算荚坞,
1.寫一些git的教程
2.一定寫一些自定義view的教程,viewgroup教程各淀,把這個吃透诡挂,向著中級android開發(fā)進軍
3.寫一些頸椎保養(yǎng)的文章,

最后感謝各位讀完我這么挫的第一篇文章
這是這個view的git的地址 https://github.com/bolevw/LBViews/blob/master/app/src/main/java/com/test/lbviews/views/StepViews.java
最后真的希望大家多給意見奴璃,指導共同成長城豁,QQ 634109509, 郵箱bolevw@gmail.com

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唱星,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子攒盈,更是在濱河造成了極大的恐慌哎榴,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驼壶,居然都是意外死亡热凹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門纪铺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碟渺,“玉大人,你說我怎么就攤上這事芜繁。” “怎么了蔬捷?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵周拐,是天一觀的道長凰兑。 經(jīng)常有香客問我聪黎,道長备恤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任喉镰,我火速辦了婚禮惭笑,結果婚禮上沉噩,老公的妹妹穿的比我還像新娘。我一直安慰自己蚜厉,他們只是感情好畜眨,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布康聂。 她就那樣靜靜地躺著,像睡著了一般恬汁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悬垃,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天尝蠕,我揣著相機與錄音看彼,去河邊找鬼。 笑死靖榕,一個胖子當著我的面吹牛茁计,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播践剂,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逊脯,長吁一口氣:“原來是場噩夢啊……” “哼竣贪!你這毒婦竟也來了?” 一聲冷哼從身側響起匕争,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤汗捡,失蹤者是張志新(化名)和其女友劉穎畏纲,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艘蹋,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡票灰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冯键。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庸汗。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蚯舱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陈肛,到底是詐尸還是另有隱情兄裂,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布谈撒,位于F島的核電站,受9級特大地震影響畅涂,放射性物質發(fā)生泄漏港华。R本人自食惡果不足惜道川,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一午衰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冒萄,春花似錦臊岸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至崖技,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迎献,已是汗流浹背瞎访。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吁恍,地道東北人扒秸。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓播演,卻偏偏與公主長得像,于是被迫代替她去往敵國和親伴奥。 傳聞我的和親對象是個殘疾皇子写烤,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

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