先上效果圖,如果這是你想要的效果可以往下看。不是的話也可以借鑑一下。
1、自定義view的基本流程
1)measure();
主要作用是測量view的寬高
2)layout();
主要作用是計算子view的位置,一般是自定義viewgroup時纔會用上。(這裏可以忽略)
3)draw();
主要作用是繪製view,切記由於該方法會多次執行,切勿在這個方法裏面過多的創建對象,以免引起內存泄漏。
2、核心思想
1)從效果圖可以看出stepview是有多個步驟的,那麼我們可以使用stepbean來封裝每一步的屬性值。具體內容如下:
/**
* 步驟view的數據基類
*/
public class StepBean {
private String name;
private String time;
private int icon;//圖片icon
private int lineColor;
private int nameColor;
private int timeColor;
private boolean isIng = false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(int lineColor) {
this.lineColor = lineColor;
}
public boolean isIng() {
return isIng;
}
public void setIng(boolean ing) {
isIng = ing;
}
public int getTimeColor() {
return timeColor;
}
public void setTimeColor(int timeColor) {
this.timeColor = timeColor;
}
public int getNameColor() {
return nameColor;
}
public void setNameColor(int nameColor) {
this.nameColor = nameColor;
}
}
2)stepview會傳入一個List<StepBean>來決定繪製多少步。下面我們來看看具體實現:代碼裏面的註解也很詳細,無非是畫圓畫線和繪製圖片。
/**
* 自定義步驟view
*/
public class StepView extends View {
/**
* 需要顯示的步驟節點集合
*/
private List<StepBean> stepBeanList = new ArrayList<>();
private int mCurrViewWidth;//當前控件寬度
private int mCurrViewHeight;//當前控件高度
private Paint mPaint;//節點畫筆
private TextPaint mTextPaint;//文字描述的畫筆
private Paint mLinePaint;
private int roundSize = 8;//圓的直徑
private int haloRoundSize = 0;//外圓的直徑
private int haloThickness = 1;//外圓的厚度
private int haloAlpha = 50;//光暈透明度
private int haloWidth = 4;//光暈的寬度
private int textSize = 12;//文字大小
private int lineHeight = 1;//步驟線的高度
private int startX = 0;//X軸開始位置
private int startY = 10;//Y軸開始位置
private int itemWidth = 0;//每一項的寬度
private int roundType = 0;//0空心圓,2光暈圓
//所有位置以圓心爲標準
private int roundX = 0;//大圓心X
private int roundY = 0;//大圓心Y
private int bigRoundSize = 20;//圓的直徑
public StepView(Context context) {
super(context);
}
public StepView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
//dp轉px
roundSize = DensityUtil.dip2px(context, roundSize);
haloWidth = DensityUtil.dip2px(context, haloWidth);
haloRoundSize = roundSize + haloWidth * 2;
bigRoundSize = DensityUtil.dip2px(context, bigRoundSize);
roundY = DensityUtil.dip2px(context, startY) + bigRoundSize / 2;
haloThickness = DensityUtil.dip2px(context, haloThickness);
lineHeight = DensityUtil.dip2px(context, lineHeight);
textSize = DensityUtil.dip2px(context, textSize);
mPaint = new Paint();
mPaint.setAntiAlias(true); //消除鋸齒
mLinePaint = new Paint();
//線高
mLinePaint.setStrokeWidth(lineHeight);
//虛線
DashPathEffect effects = new DashPathEffect(new float[]{lineHeight * 3,
lineHeight * 3}, 0);
mLinePaint.setPathEffect(effects);
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true); //消除鋸齒
mTextPaint.setTextSize(textSize);
mTextPaint.setTypeface(Typeface.SANS_SERIF);
//文字字體加粗
mTextPaint.setFakeBoldText(false);
}
public void setStepBeanList(List<StepBean> stepBeanList) {
if (stepBeanList != null)
this.stepBeanList = stepBeanList;
//計算控件開始位置
if (stepBeanList.size() > 0)
itemWidth = mCurrViewWidth / stepBeanList.size();
roundX = itemWidth / 2 - bigRoundSize / 2;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mCurrViewWidth = getMeasuredWidth();
mCurrViewHeight = getMeasuredHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < stepBeanList.size(); i++) {
// 畫圓
mPaint.setColor(stepBeanList.get(i).getLineColor());
mPaint.setStyle(Paint.Style.FILL); //繪製實心圓
RectF rf2 = new RectF(roundX - roundSize / 2 + itemWidth * i, roundY - roundSize / 2, roundX + roundSize / 2 + itemWidth * i, roundY + roundSize / 2);
canvas.drawOval(rf2, mPaint);
if (roundType == 0) {
// 畫空心圓
mPaint.setStyle(Paint.Style.STROKE); //繪製空心圓
mPaint.setStrokeWidth(haloThickness);//設置空心圓的厚度
} else {
// 畫圓光圈
mPaint.setAlpha(haloAlpha);
}
RectF haloRectF = new RectF(roundX - haloRoundSize / 2 + itemWidth * i, roundY - haloRoundSize / 2, roundX + haloRoundSize / 2 + itemWidth * i, roundY + haloRoundSize / 2);
canvas.drawOval(haloRectF, mPaint);
// 繪製線,最後一個點不用繪製線
if (i < stepBeanList.size() - 1) {
mLinePaint.setColor(stepBeanList.get(i + 1).getLineColor());
int x = roundX + haloRoundSize / 2;//開始位置
canvas.drawLine(x + itemWidth * i, roundY, x + itemWidth * (i + 1) - haloRoundSize, roundY, mLinePaint);
}
//繪製正在進行的圖片
if (stepBeanList.get(i).isIng()) {
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), stepBeanList.get(i).getIcon());
int w = mBitmap.getWidth();
int h = mBitmap.getHeight();
// 指定圖片繪製區域(全圖)
Rect src = new Rect(0, 0, w, h);
// 指定圖片在屏幕上顯示的區域(原圖大小)
Rect dst = new Rect(roundX - w / 2 + itemWidth * i, roundY - h / 2, roundX + w / 2 + itemWidth * i, roundY + h / 2);
canvas.drawBitmap(mBitmap, src, dst, null);
}
// 繪製name文字
mTextPaint.setColor(stepBeanList.get(i).getNameColor());
int tx = roundX - getTextWidth(stepBeanList.get(i).getName(), mTextPaint) / 2 + itemWidth * i;
int ty = roundY + bigRoundSize + getTextHeight(stepBeanList.get(i).getName(), mTextPaint);
canvas.drawText(stepBeanList.get(i).getName(), tx, ty, mTextPaint);
// 繪製time文字
mTextPaint.setColor(stepBeanList.get(i).getTimeColor());
tx = roundX - getTextWidth(stepBeanList.get(i).getTime(), mTextPaint) / 2 + itemWidth * i;
ty = ty + getTextHeight(stepBeanList.get(i).getTime(), mTextPaint) * 2;
canvas.drawText(stepBeanList.get(i).getTime(), tx, ty, mTextPaint);
}
}
//獲取文字寬度
private int getTextWidth(String text, Paint paint) {
Rect rect = new Rect(); // 文字所在區域的矩形
paint.getTextBounds(text, 0, text.length(), rect);
return rect.width();
}
//獲取文字高度
private int getTextHeight(String text, Paint paint) {
Rect rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
return rect.height();
}
}
3)這裏需要注意一下,由於我這邊的需求的步驟不多,所以我這邊是按:屏幕的寬度/步驟總數 = 每一步的寬度;如果你的步驟過多的話會出現每一步的寬度很小,可以考慮採用滑動顯示的方式來實現。
3、如何使用:
1)在佈局頁面編寫以下代碼:
<com.library_base.ui.view.stepview.StepView
android:id="@+id/stepView"
android:layout_width="match_parent"
android:layout_height="80dp"/>
2)在Activity裏面使用:
StepView stepView = findViewById(R.id.stepView);
private void setStepBean(String type) {
List<StepBean> stepBeans = new ArrayList<>();
//待覈銷
StepBean stepBean = new StepBean();
stepBean.setName("待覈銷");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getOrderTime())));
stepBean.setNameColor(Color.parseColor("#333333"));
stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
stepBean.setLineColor(Color.parseColor("#51B3F1"));
stepBeans.add(stepBean);
//覈銷中
stepBean = new StepBean();
stepBean.setName("覈銷中");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getOrderTime())));
stepBean.setLineColor(Color.parseColor("#51B3F1"));
stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
if (type.equals("0")) {
stepBean.setNameColor(Color.parseColor("#51B3F1"));
stepBean.setIng(true);
stepBean.setIcon(R.mipmap.check_state_img);
} else {
stepBean.setNameColor(Color.parseColor("#333333"));
}
stepBeans.add(stepBean);
//覈銷完成
stepBean = new StepBean();
stepBean.setTimeColor(Color.parseColor("#CCCCCC"));
if (type.equals("0")) {
stepBean.setName("覈銷完成");
stepBean.setTime("");
stepBean.setNameColor(Color.parseColor("#333333"));
stepBean.setLineColor(Color.parseColor("#D8D8D8"));
} else if (type.equals("1")) {
//覈銷不通過
stepBean.setName("覈銷不通過");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getVerificationTime())));
stepBean.setNameColor(Color.parseColor("#FF4503"));
stepBean.setLineColor(Color.parseColor("#FF4503"));
stepBean.setIng(true);
stepBean.setIcon(R.mipmap.check_state_un);
} else if (type.equals("2")) {
//覈銷通過
stepBean.setName("覈銷通過");
stepBean.setTime(CommonUtils.getDateToString2(Long.parseLong(detailObject.getVerificationTime())));
stepBean.setNameColor(Color.parseColor("#51B3F1"));
stepBean.setLineColor(Color.parseColor("#51B3F1"));
stepBean.setIng(true);
stepBean.setIcon(R.mipmap.check_state_ed);
}
stepBeans.add(stepBean);
stepView.setStepBeanList(stepBeans);
}
3)這裏使用了單位轉換的工具類
public class DensityUtil {
/**
* 根據手機的分辨率從 dp 的單位 轉成爲 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根據手機的分辨率從 px(像素) 的單位 轉成爲 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 獲取屏幕寬度
*
* @param context
* @return
*/
public static int getWidth(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
return wm.getDefaultDisplay().getWidth();
}
/**
* 獲取屏幕高度
*
* @param context
* @return
*/
public static int getHeight(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
return wm.getDefaultDisplay().getHeight();
}
/**
* 獲取圖片的寬高
*
* @param context
* @param r
* @return
*/
public static int getImageWidth(Context context, int r) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), r);
return bitmap.getWidth();
}
}
至此一個簡單的自定義stepview就實現了。