Android 开发订单流程view实例详解
先看看最终效果图:
怎么样,效果还是很不错的吧?群里有人说切四张图的、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,defStyleAttr); obtainStyledAttrs(context,defStyleAttr); initViews(); } /** * 获取我们的自定义属性 * @param context * @param attrs * @param defStyleAttr */ private void obtainStyledAttrs(Context context,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],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,endX,linePaint); //draw progress line float progressX = startX + (endX - startX) * mProgress; linePaint.setColor(mReachedColor); canvas.drawLine(startX,progressX,linePaint); //给移动圆点一个RadialGradient颜色梯度效果 thumbPaint.setShader(new RadialGradient(progressX,mThumbRadius,new int[]{Color.WHITE,D_REACH_COLOR,Color.YELLOW},null,Shader.TileMode.REPEAT)); canvas.drawCircle(progressX,thumbPaint); }
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_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 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,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())); } }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!