前言:最近在做 rn、h5 然后时不时的再来一点 android 的需求,尼玛~~感觉不要不要的啊,思维都混淆了都,不管咋样,多学点技术还是不错的,所以还是加油吧!
在群里看到小伙伴发了这么一个需求,先看看最终效果图:
怎么样,效果还是很不错的吧?群里有人说切四张图的、recycleview 的、各种的都有啊,但是最简单的就是通过自定义 view 来实现了~接下来让我们来实现下这个(订单流程 view)。
首先我们定义好我们的自定义属性:
attrs.xml
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <declare-styleable name="ProcessView">
- <!--滑动圆点的半径-->
- <attr name="thumb_radius" format="dimension"/>
- <!--到达的颜色-->
- <attr name="color_reached" format="color"/>
- <!--未到达的颜色-->
- <attr name="color_unreached" format="color"/>
- <!--textsize的大小-->
- <attr name="textsize" format="dimension"/>
- <!--text的颜色-->
- <attr name="text_color" format="color"/>
- <!--线的宽度-->
- <attr name="line_width" format="dimension"/>
- <!--状态文字数组-->
- <attr name="texts" format="reference"/>
- </declare-styleable>
- </resources>
然后就是我们的老套路了,创建一个叫 ProcessView 的类继承 view,然后定义好我们需要的属性:
- /**
- * Created by leo on 17/3/27.
- */
- public class ProcessView extends View {
- /**
- * 默认线宽度
- */
- private static final float D_LINE_WIDTH = 3;
- /**
- * 默认滑动圆点半径
- */
- private static final float D_THUMB_RADIUS = 10;
- /**
- * 默认textsize
- */
- private static final float D_TEXT_SIZE = 13f;
- private static final int D_REACH_COLOR = 0xFFF1AE0D;
- private static final int D_UNREACH_COLOR = Color.WHITE;
- private static final int D_TEXT_COLOR = Color.WHITE;
- private Paint linePaint;
- private TextPaint textPaint;
- private Paint thumbPaint;
- private float mTextSize = xx2px(TypedValue.COMPLEX_UNIT_SP, D_TEXT_SIZE);
- private float mLineWidth = xx2px(TypedValue.COMPLEX_UNIT_DIP, D_LINE_WIDTH);
- private float mThumbRadius = xx2px(TypedValue.COMPLEX_UNIT_DIP, D_THUMB_RADIUS);
- private int mReachedColor = D_REACH_COLOR;
- private int mUnreachedColor = D_UNREACH_COLOR;
- private int mTextColor = D_TEXT_COLOR;
- //当前进度
- private float mProgress = 0.0f;
- //所有的状态文字
- private String[] texts;
- public ProcessView(Context context) {
- this(context, null);
- }
- public ProcessView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public ProcessView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- obtainStyledAttrs(context, attrs, defStyleAttr);
- initViews();
- }
- /**
- * 获取我们的自定义属性
- * @param context
- * @param attrs
- * @param defStyleAttr
- */
- private void obtainStyledAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProcessView, defStyleAttr, 0);
- texts = a.hasValue(R.styleable.ProcessView_texts) ?
- getResources().getStringArray(a.getResourceId(R.styleable.ProcessView_texts, 0)) : texts;
- mLineWidth = a.hasValue(R.styleable.ProcessView_line_width) ?
- a.getDimensionPixelSize(R.styleable.ProcessView_line_width, 0) : mLineWidth;
- mThumbRadius = a.hasValue(R.styleable.ProcessView_thumb_radius) ?
- a.getDimensionPixelSize(R.styleable.ProcessView_thumb_radius, 0) : mThumbRadius;
- mTextSize = a.hasValue(R.styleable.ProcessView_textsize) ?
- a.getDimensionPixelSize(R.styleable.ProcessView_text_color, 0) : mTextSize;
- mReachedColor=a.hasValue(R.styleable.ProcessView_color_reached)?
- a.getColor(R.styleable.ProcessView_color_reached,D_REACH_COLOR):D_REACH_COLOR;
- mUnreachedColor=a.hasValue(R.styleable.ProcessView_color_unreached)?
- a.getColor(R.styleable.ProcessView_color_unreached,D_UNREACH_COLOR):D_UNREACH_COLOR;
- mTextColor=a.hasValue(R.styleable.ProcessView_text_color)?
- a.getColor(R.styleable.ProcessView_text_color,D_TEXT_COLOR):D_TEXT_COLOR;
- a.recycle();
- }
- /**
- * 初始化一些对象
- */
- private void initViews() {
- linePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- linePaint.setStyle(Paint.Style.FILL);
- textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- thumbPaint = new Paint(linePaint);
- textPaint.setTextSize(mTextSize);
- textPaint.setColor(mTextColor);
- linePaint.setStrokeWidth(mLineWidth);
- }
- }
然后就是重写我们的 onmeasure 方法了,我们这里就不考虑控件的高度为 wrap_content 这种情况了,所以我们只需要测量高度就可以了:
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int heightM = MeasureSpec.getMode(heightMeasureSpec);
- int defaultW = MeasureSpec.getSize(widthMeasureSpec);
- int defaultH = MeasureSpec.getSize(heightMeasureSpec);
- int resultW, resultH;
- resultW = defaultW;
- resultH = getDefaultHeight(defaultH, heightM);
- setMeasuredDimension(resultW, resultH);
- }
- private int getDefaultHeight(int height, int mode) {
- int result;
- if (mode == MeasureSpec.EXACTLY) {
- result = height;
- } else {
- //获取文字的高度
- float textH = (textPaint.getFontMetrics().bottom - textPaint.getFontMetrics().top);
- //高度=圆半径+2.2*线条宽度(也就是竖线高度)+文字高度*1.3(也就是空隙高度)+0.5*文字高度
- result = (int) (mThumbRadius + mLineWidth * 2.2f + textH * 1.3f + 0.5 * textH);
- }
- return result;
- }
接着就是我们的核心方法 onDraw() 了,代码很简单都有注释,我就不一一解释了:
- @Override protected void onDraw(Canvas canvas) {
- //画底部的竖线跟文字
- drawFoot(canvas);
- //画移动的小圆点跟进度条
- drawProgressAndThumb(canvas);
- }
- /**
- * 画底部的竖线跟文字
- */
- private void drawFoot(Canvas canvas) {
- //设置底部竖线宽度(底部的竖线会比进度条的要小一点)
- float lineWidth = mLineWidth * 0.8f;
- linePaint.setStrokeWidth(mLineWidth * 0.8f);
- //起始位置(也就是"订单已提交"的"已"字位置)
- float startX = textPaint.measureText(texts[0]) / 2;
- //结束的文字的位置("已送达"的"送"字位置)
- float endTextW = textPaint.measureText(texts[texts.length - 1]) / 2;
- //绘制的终点位置
- float endX = getMeasuredWidth() - endTextW;
- //线条的总长度
- float lineW = (endX - startX) / (texts.length - 1);
- //竖线的高度
- float lineH = mLineWidth * 2.2f;
- //竖线的终点位置
- float lineY = mThumbRadius + mLineWidth / 2;
- //循环画出竖线跟文字
- for (int i = 0; i < texts.length; i++) {
- canvas.save();
- //每画一条竖线让画布水平平移linew个宽度
- canvas.translate(i * lineW, 0);
- //如果当前进度>竖线所在的位置,就改变竖线的颜色
- linePaint.setColor(i * lineW >= mProgress * (endX - startX) ? mUnreachedColor: mReachedColor);
- float endX2 = i == texts.length - 1 ? startX - lineWidth / 2 : startX + lineWidth / 2;
- canvas.drawLine(endX2, lineY, endX2, lineY + lineH, linePaint);
- //画文字
- textPaint.setTextAlign(Paint.Align.CENTER);
- float textH = (textPaint.getFontMetrics().bottom - textPaint.getFontMetrics().top);
- canvas.drawText(texts[i], endX2, lineY + lineH + textH * 1.3f, textPaint);
- canvas.restore();
- }
- }
- private void drawProgressAndThumb(Canvas canvas) {
- float startX = textPaint.measureText(texts[0]) / 2;
- float endTextW = textPaint.measureText(texts[texts.length - 1]) / 2;
- float endX = getMeasuredWidth() - endTextW;
- float lineY = mThumbRadius;
- linePaint.setStrokeWidth(mLineWidth);
- //draw basic line
- linePaint.setColor(mUnreachedColor);
- canvas.drawLine(startX, lineY, endX, lineY, linePaint);
- //draw progress line
- float progressX = startX + (endX - startX) * mProgress;
- linePaint.setColor(mReachedColor);
- canvas.drawLine(startX, lineY, progressX, lineY, linePaint);
- //给移动圆点一个RadialGradient颜色梯度效果
- thumbPaint.setShader(new RadialGradient(progressX, mThumbRadius, mThumbRadius, new int[] {
- Color.WHITE,
- D_REACH_COLOR,
- Color.YELLOW
- },
- null, Shader.TileMode.REPEAT));
- canvas.drawCircle(progressX, mThumbRadius, mThumbRadius, thumbPaint);
- }
好啦~~然后我们暴露一个方法给外部,修改 progress:
- public void setProgress(float progress) {
- if (progress != mProgress) {
- mProgress = progress;
- if (Looper.myLooper() == Looper.getMainLooper()) {
- invalidate();
- } else {
- postInvalidate();
- }
- }
- }
最后我们就可以跑起来了:
activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:background="#b2000000"
- android:orientation="vertical"
- >
- <android.support.v7.widget.CardView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:cardCornerRadius="5dp"
- app:cardElevation="3dp"
- app:cardBackgroundColor="#FF2384DD"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="10dp"
- >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="商家已接单"
- android:textSize="14.5sp"
- android:textColor="#FFF1AE0D"
- />
- <TextView
- android:layout_marginTop="5dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="订单超过12小时自动完成"
- android:textSize="13sp"
- android:textColor="#fff"
- />
- <View
- android:layout_marginTop="10dp"
- android:layout_marginBottom="10dp"
- android:layout_width="match_parent"
- android:layout_height="2dp"
- android:background="@drawable/bg_line"
- android:layerType="software"
- />
- <com.yasin.processdemo.view.ProcessView
- android:id="@+id/id_process"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:texts="@array/process_states"
- />
- </LinearLayout>
- </android.support.v7.widget.CardView>
- </LinearLayout>
arrays.xml:
- <?xml version="1.0" encoding="utf-8" ?>
- <resources>
- <array name="process_states">
- <item>
- 订单已提交
- </item>
- <item>
- 已付款
- </item>
- <item>
- 商家已接单
- </item>
- <item>
- 已送达
- </item>
- </array>
- </resources>
然后是我们的测试 activity:
- package com.yasin.processdemo;
- import android.animation.ValueAnimator;
- import android.os.Bundle;
- import android.support.v7.app.AppCompatActivity;
- import android.view.animation.AccelerateDecelerateInterpolator;
- import com.yasin.processdemo.view.ProcessView;
- public class MainActivity extends AppCompatActivity {
- private ProcessView mProcessView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mProcessView= (ProcessView) findViewById(R.id.id_process);
- startAni();
- }
- private void startAni() {
- ValueAnimator a = ValueAnimator.ofFloat(0, 1);
- a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float progress = (float) animation.getAnimatedValue();
- mProcessView.setProgress(progress);
- }
- });
- a.setDuration(10000);
- a.setInterpolator(new AccelerateDecelerateInterpolator());
- a.start();
- }
- }
最后附上 processview 的全部代码:
- package com.yasin.processdemo.view;
- import android.content.Context;
- import android.content.res.Resources;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.RadialGradient;
- import android.graphics.Shader;
- import android.os.Looper;
- import android.text.TextPaint;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.util.TypedValue;
- import android.view.View;
- import com.yasin.processdemo.R;
- /**
- * Created by leo on 17/3/27.
- */
- public class ProcessView extends View {
- /**
- * 默认线宽度
- */
- private static final float D_LINE_WIDTH = 3;
- /**
- * 默认滑动圆点半径
- */
- private static final float D_THUMB_RADIUS = 10;
- /**
- * 默认textsize
- */
- private static final float D_TEXT_SIZE = 13f;
- private static final int D_REACH_COLOR = 0xFFF1AE0D;
- private static final int D_UNREACH_COLOR = Color.WHITE;
- private static final int D_TEXT_COLOR = Color.WHITE;
- private Paint linePaint;
- private TextPaint textPaint;
- private Paint thumbPaint;
- private float mTextSize = xx2px(TypedValue.COMPLEX_UNIT_SP, D_TEXT_SIZE);
- private float mLineWidth = xx2px(TypedValue.COMPLEX_UNIT_DIP, D_LINE_WIDTH);
- private float mThumbRadius = xx2px(TypedValue.COMPLEX_UNIT_DIP, D_THUMB_RADIUS);
- private int mReachedColor = D_REACH_COLOR;
- private int mUnreachedColor = D_UNREACH_COLOR;
- private int mTextColor = D_TEXT_COLOR;
- //当前进度
- private float mProgress = 0.0f;
- //所有的状态文字
- private String[] texts;
- public ProcessView(Context context) {
- this(context, null);
- }
- public ProcessView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public ProcessView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- obtainStyledAttrs(context, attrs, defStyleAttr);
- initViews();
- }
- /**
- * 获取我们的自定义属性
- * @param context
- * @param attrs
- * @param defStyleAttr
- */
- private void obtainStyledAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProcessView, defStyleAttr, 0);
- texts = a.hasValue(R.styleable.ProcessView_texts) ?
- getResources().getStringArray(a.getResourceId(R.styleable.ProcessView_texts, 0)) : texts;
- mLineWidth = a.hasValue(R.styleable.ProcessView_line_width) ?
- a.getDimensionPixelSize(R.styleable.ProcessView_line_width, 0) : mLineWidth;
- mThumbRadius = a.hasValue(R.styleable.ProcessView_thumb_radius) ?
- a.getDimensionPixelSize(R.styleable.ProcessView_thumb_radius, 0) : mThumbRadius;
- mTextSize = a.hasValue(R.styleable.ProcessView_textsize) ?
- a.getDimensionPixelSize(R.styleable.ProcessView_text_color, 0) : mTextSize;
- mReachedColor=a.hasValue(R.styleable.ProcessView_color_reached)?
- a.getColor(R.styleable.ProcessView_color_reached,D_REACH_COLOR):D_REACH_COLOR;
- mUnreachedColor=a.hasValue(R.styleable.ProcessView_color_unreached)?
- a.getColor(R.styleable.ProcessView_color_unreached,D_UNREACH_COLOR):D_UNREACH_COLOR;
- mTextColor=a.hasValue(R.styleable.ProcessView_text_color)?
- a.getColor(R.styleable.ProcessView_text_color,D_TEXT_COLOR):D_TEXT_COLOR;
- a.recycle();
- }
- /**
- * 初始化一些对象
- */
- private void initViews() {
- linePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- linePaint.setStyle(Paint.Style.FILL);
- textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- thumbPaint = new Paint(linePaint);
- textPaint.setTextSize(mTextSize);
- textPaint.setColor(mTextColor);
- linePaint.setStrokeWidth(mLineWidth);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int heightM = MeasureSpec.getMode(heightMeasureSpec);
- int defaultW = MeasureSpec.getSize(widthMeasureSpec);
- int defaultH = MeasureSpec.getSize(heightMeasureSpec);
- int resultW, resultH;
- resultW = defaultW;
- resultH = getDefaultHeight(defaultH, heightM);
- setMeasuredDimension(resultW, resultH);
- }
- private int getDefaultHeight(int height, int mode) {
- int result;
- if (mode == MeasureSpec.EXACTLY) {
- result = height;
- } else {
- //获取文字的高度
- float textH = (textPaint.getFontMetrics().bottom - textPaint.getFontMetrics().top);
- //高度=圆半径+2.2*线条宽度(也就是竖线高度)+文字高度*1.3(也就是空隙高度)+0.5*文字高度
- result = (int) (mThumbRadius + mLineWidth * 2.2f + textH * 1.3f + 0.5 * textH);
- }
- return result;
- }
- @Override
- protected void onDraw(Canvas canvas) {
- //画底部的竖线跟文字
- drawFoot(canvas);
- //画移动的小圆点跟进度条
- drawProgressAndThumb(canvas);
- }
- /**
- * 画底部的竖线跟文字
- */
- private void drawFoot(Canvas canvas) {
- //设置底部竖线宽度(底部的竖线会比进度条的要小一点)
- float lineWidth = mLineWidth * 0.8f;
- linePaint.setStrokeWidth(mLineWidth * 0.8f);
- //起始位置(也就是"订单已提交"的"已"字位置)
- float startX = textPaint.measureText(texts[0]) / 2;
- //结束的文字的位置("已送达"的"送"字位置)
- float endTextW = textPaint.measureText(texts[texts.length - 1]) / 2;
- //绘制的终点位置
- float endX = getMeasuredWidth() - endTextW;
- //线条的总长度
- float lineW = (endX - startX) / (texts.length - 1);
- //竖线的高度
- float lineH = mLineWidth * 2.2f;
- //竖线的终点位置
- float lineY = mThumbRadius + mLineWidth / 2;
- //循环画出竖线跟文字
- for (int i = 0; i < texts.length; i++) {
- canvas.save();
- //每画一条竖线让画布水平平移linew个宽度
- canvas.translate(i * lineW, 0);
- //如果当前进度>竖线所在的位置,就改变竖线的颜色
- linePaint.setColor(i * lineW >= mProgress * (endX - startX) ? mUnreachedColor : mReachedColor);
- float endX2 = i == texts.length - 1 ? startX - lineWidth / 2 : startX + lineWidth / 2;
- canvas.drawLine(endX2, lineY, endX2, lineY + lineH, linePaint);
- //画文字
- textPaint.setTextAlign(Paint.Align.CENTER);
- float textH = (textPaint.getFontMetrics().bottom - textPaint.getFontMetrics().top);
- canvas.drawText(texts[i], endX2, lineY + lineH + textH * 1.3f, textPaint);
- canvas.restore();
- }
- }
- private void drawProgressAndThumb(Canvas canvas) {
- float startX = textPaint.measureText(texts[0]) / 2;
- float endTextW = textPaint.measureText(texts[texts.length - 1]) / 2;
- float endX = getMeasuredWidth() - endTextW;
- float lineY = mThumbRadius;
- linePaint.setStrokeWidth(mLineWidth);
- //draw basic line
- linePaint.setColor(mUnreachedColor);
- canvas.drawLine(startX, lineY, endX, lineY, linePaint);
- //draw progress line
- float progressX = startX + (endX - startX) * mProgress;
- linePaint.setColor(mReachedColor);
- canvas.drawLine(startX, lineY, progressX, lineY, linePaint);
- //给移动圆点一个RadialGradient颜色梯度效果
- thumbPaint.setShader(new RadialGradient(progressX, mThumbRadius, mThumbRadius, new int[]{Color.WHITE, D_REACH_COLOR, Color.YELLOW}, null, Shader.TileMode.REPEAT));
- canvas.drawCircle(progressX, mThumbRadius, mThumbRadius, thumbPaint);
- }
- public void setProgress(float progress) {
- if (progress != mProgress) {
- mProgress = progress;
- if (Looper.myLooper() == Looper.getMainLooper()) {
- invalidate();
- } else {
- postInvalidate();
- }
- }
- }
- private float xx2px(int unit, float value) {
- Context c = getContext();
- Resources r;
- if (c == null)
- r = Resources.getSystem();
- else
- r = c.getResources();
- return (TypedValue.applyDimension(
- unit, value, r.getDisplayMetrics()));
- }
- }
到此我们就结束了~~可能还会有一些样式上的小 bug,小伙伴自己去调试哈~~ 欢迎入群!!群号: 511276976
来源: http://www.bubuko.com/infodetail-1998884.html