自定義StepViews
就是模仿網(wǎng)上那個挺熱門的一系列步驟的view(看了一下圖片效果,自己嘗試寫一個肩祥,鍛煉自己的自定義view)
先上圖
首先明確一下這個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