美文网首页UIandroid自定义
仿支付宝大额充值到账流程-VerticalStepView

仿支付宝大额充值到账流程-VerticalStepView

作者: Jaesoon | 来源:发表于2018-04-05 19:09 被阅读951次

    银行类App,最近需要开发一个大额充值的功能。中间涉及到一个解释大额充值到账流程的功能。好像没有合适的控件,于是自己撸了一个StepView。废话不多说,show you my code。嗯,规矩我懂,先给你看看图。


    demo.png

    Java Code:

    package xin.smartlink.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.DashPathEffect;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.support.annotation.Nullable;
    import android.text.TextPaint;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.view.View;
    
    import java.util.List;
    
    /**
     * Created by Jaesoon on 2018/4/4.
     */
    public class VerticalStepView extends View {
        private ItemDrawable[] itemDrawables;
        int textSize = 15;
        int descriptionTextSize = 12;
        int drawableSize = 30;
        int drawableMarginText = 10;
        int spacing = 30;
        private int mTextColor = Color.BLACK;
        private int mDescriptionTextColor = Color.GRAY;
        private int mProgressLineColor = Color.GRAY;
        private TextPaint mPaint;
        private List<Item> contentItems;
    
        public VerticalStepView(Context context) {
            super(context);
        }
    
        public VerticalStepView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init(context, attrs);
        }
    
        public VerticalStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context, attrs);
        }
    
        private void init(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalStepView);
            int n = a.getIndexCount();
            for (int i = 0; i < n; i++) {
                int attr = a.getIndex(i);
                if (attr == R.styleable.VerticalStepView_textSize) {
                    textSize = a.getDimensionPixelSize(attr, textSize);
                } else if (attr == R.styleable.VerticalStepView_drawableSize) {
                    drawableSize = a.getDimensionPixelSize(attr, drawableSize);
                } else if (attr == R.styleable.VerticalStepView_spacing) {
                    spacing = a.getDimensionPixelSize(attr, spacing);
                } else if (attr == R.styleable.VerticalStepView_drawableMarginText) {
                    drawableMarginText = a.getDimensionPixelSize(attr, drawableMarginText);
                } else if (attr == R.styleable.VerticalStepView_textColor) {
                    mTextColor = a.getColor(attr, mTextColor);
                } else if (attr == R.styleable.VerticalStepView_progressLineColor) {
                    mProgressLineColor = a.getColor(attr, mProgressLineColor);
                } else if (attr == R.styleable.VerticalStepView_descriptionTextColor) {
                    mDescriptionTextColor = a.getColor(attr, mDescriptionTextColor);
                } else if (attr == R.styleable.VerticalStepView_descriptionTextSize) {
                    descriptionTextSize = a.getDimensionPixelSize(attr, descriptionTextSize);
                }
            }
            a.recycle();
            final Resources res = getResources();
            mPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
            mPaint.density = res.getDisplayMetrics().density;
            mPaint.setColor(mTextColor);
            mPaint.setAntiAlias(true);
            mPaint.setTextSize(textSize);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            layoutItem();
            if (itemDrawables != null) {
                int height = getWantHeight();
                if (height > getMeasuredHeight()) {
                    requestLayout();
                    invalidate();
                    return;
                }
                for (int i = 0; i < itemDrawables.length; i++) {
                    ItemDrawable textDrawable = itemDrawables[i];
                    textDrawable.draw(canvas);
                    if (i > 0) {
                        textDrawable.getBounds();
                        drawConnectLine(canvas, textDrawable.getBounds().left + drawableSize / 2, itemDrawables[i - 1].getBounds().top + drawableSize + drawableSize / 10, textDrawable.getBounds().top - drawableSize / 10);
                    }
                }
            }
        }
    
        private int getWantHeight() {
            int height = 0;
            if (itemDrawables != null) {
                for (ItemDrawable itemDrawable : itemDrawables) {
                    height += itemDrawable.getIntrinsicHeight();
                    height += spacing;
                }
                height -= spacing;
                height += getPaddingTop() + getPaddingBottom();
            }
            return height;
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获取宽度的测试模式
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);//获取宽度的测试值
            int width;
            //如果宽度的测试模式等于EXACTLY,就直接赋值
            if (widthMode == MeasureSpec.EXACTLY) {
                width = widthSize;
            } else {
                width = 800;//使用我们自己在代码中定义的宽度
                //如果宽度的测试模式等于AT_MOST,取测量值和计算值的最小值
                if (widthMode == MeasureSpec.AT_MOST) {
                    width = Math.min(width, widthSize);
                }
            }
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取高度的测试模式
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);//获取高度的测试值
            int height = 0;
            //如果高度的测试模式等于EXACTLY,就直接赋值
            if (heightMode == MeasureSpec.EXACTLY) {
                height = heightSize;
            } else {
                //计算出整个View的高度
                if (itemDrawables != null) {
                    height = getWantHeight();
                } else {
                    height = 200;
                }
                //如果高度的测试模式等于AT_MOST,取测量值和计算值的最小值
                if (heightMode == MeasureSpec.AT_MOST) {
                    height = Math.min(height, heightSize);
                }
            }
            setMeasuredDimension(width, height);//来存储测量的宽,高值
        }
    
        private void drawConnectLine(Canvas canvas, int startX, int startY, int endY) {
            Paint p = new Paint();
            p.setAntiAlias(true);
            p.setColor(mProgressLineColor);
            p.setStyle(Paint.Style.STROKE);
            p.setStrokeWidth(3.0f);
            DashPathEffect dashPathEffect = new DashPathEffect(new float[]{10, 10}, 0);
            p.setPathEffect(dashPathEffect);
    
            Path path = new Path();
            path.moveTo(startX, startY);
            path.lineTo(startX, endY);
            canvas.drawPath(path, p);
        }
    
        private void layoutItem() {
            if (contentItems == null || contentItems.isEmpty()) {
                return;
            }
            int left = getPaddingLeft(), top = getPaddingTop();
            itemDrawables = new ItemDrawable[contentItems.size()];
            for (int i = 0; i < contentItems.size(); i++) {
                ItemDrawable itemDrawable = new ItemDrawable();
                itemDrawables[i] = itemDrawable;
                itemDrawable.setmTextColor(mTextColor);
                itemDrawable.setmDescriptionTextColor(mDescriptionTextColor);
                itemDrawable.setContent(contentItems.get(i).getIcon(),
                        contentItems.get(i).getContent(), contentItems.get(i).getDescription(), mPaint, textSize, descriptionTextSize,
                        getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                        drawableSize,
                        drawableMarginText
                );
                itemDrawable.setBounds(new Rect(left, top, left + itemDrawable.getIntrinsicWidth(), top + itemDrawable.getIntrinsicHeight()));
                top += itemDrawable.getIntrinsicHeight();
                top += getSpacing();
            }
        }
    
        public int getTextSize() {
            return textSize;
        }
    
        public void setTextSize(int textSize) {
            this.textSize = textSize;
        }
    
        public int getDrawableSize() {
            return drawableSize;
        }
    
        public void setDrawableSize(int drawableSize) {
            this.drawableSize = drawableSize;
        }
    
        public int getSpacing() {
            return spacing;
        }
    
        public void setSpacing(int spacing) {
            this.spacing = spacing;
        }
    
        public List<Item> getContentItems() {
            return contentItems;
        }
    
        public void setContentItems(List<Item> contentItems) {
            this.contentItems = contentItems;
            invalidate();
        }
    
        private class ItemDrawable {
            private TextPaint mPaint;
            private String content = "";
            private String[] arrContent;
            private String description = "";
            private String[] arrDescription;
            private Rect bounds = new Rect();
            private Drawable icon;
            private int intrinsicWidth;
            private int intrinsicHeight;
            private int iconSize;
            private int iconMarginTextWidth;
            private int fontHeight;
            private int descriptionFontHeight;
            private int textSize;
            private int descriptionTextSize;
            private int mTextColor = Color.BLACK;
            private int mDescriptionTextColor = Color.GRAY;
    
            public int getmTextColor() {
                return mTextColor;
            }
    
            public void setmTextColor(int mTextColor) {
                this.mTextColor = mTextColor;
            }
    
            public int getmDescriptionTextColor() {
                return mDescriptionTextColor;
            }
    
            public void setmDescriptionTextColor(int mDescriptionTextColor) {
                this.mDescriptionTextColor = mDescriptionTextColor;
            }
    
            public String getContent() {
                return content;
            }
    
            /**
             * @param icon                左侧的图标
             * @param content             内容
             * @param width               总宽度
             * @param iconMarginTextWidth 文本与icon的距离
             * @param iconSize            icon宽度
             * @param p                   画笔
             */
            public void setContent(Drawable icon, String content, String description, TextPaint p, int textSize, int descriptionTextSize, int width, int iconSize, int iconMarginTextWidth) {
                this.mPaint = p;
                this.icon = icon;
                this.content = content;
                this.description = description;
                this.textSize = textSize;
                this.descriptionTextSize = descriptionTextSize;
                this.iconSize = iconSize;
                this.iconMarginTextWidth = iconMarginTextWidth;
    
                intrinsicWidth = width;
                intrinsicHeight = 0;
    
                Paint.FontMetricsInt fontMetrics = p.getFontMetricsInt();
                if (!TextUtils.isEmpty(description)) {
                    p.setTextSize(descriptionTextSize);
                    arrDescription = autoSplit(description, p, width - iconMarginTextWidth - iconSize);
                    descriptionFontHeight = fontMetrics.bottom - fontMetrics.top;
                    intrinsicHeight += arrDescription.length * descriptionFontHeight;
                }
                p.setTextSize(textSize);
                arrContent = autoSplit(content, p, width - iconMarginTextWidth - iconSize);
                fontMetrics = p.getFontMetricsInt();
                fontHeight = fontMetrics.bottom - fontMetrics.top;
                intrinsicHeight += arrContent.length * fontHeight;
                intrinsicHeight = Math.max(intrinsicHeight, iconSize);
            }
    
            public Rect getBounds() {
                return bounds;
            }
    
            public void setBounds(Rect bounds) {
                this.bounds = bounds;
                int top;
                top = fontHeight > iconSize ? bounds.top + (fontHeight - iconSize) / 2 : bounds.top;
                icon.setBounds(bounds.left,
                        top,
                        bounds.left + iconSize,
                        top + iconSize);
            }
    
            public void draw(Canvas canvas) {
                int left, top;
                left = bounds.left;
                //绘制Icon
                icon.draw(canvas);
    
                //绘制文字
                left += iconSize + iconMarginTextWidth;
                top = fontHeight > iconSize ? bounds.top : bounds.top + (iconSize - fontHeight) / 2;
                mPaint.setTextSize(textSize);
                mPaint.setColor(mTextColor);
                Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
                Rect targetRect = new Rect(bounds.left, top, bounds.right, top + fontHeight);
                int baseline = targetRect.top + (targetRect.bottom - targetRect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
                for (String line : arrContent) {
                    if (!TextUtils.isEmpty(line)) {
                        canvas.drawText(line, left, baseline, mPaint);
                        baseline += fontHeight;
                    }
                }
                mPaint.setTextSize(descriptionTextSize);
                mPaint.setColor(mDescriptionTextColor);
                baseline -= fontHeight;
                baseline += descriptionFontHeight;
                if (arrDescription != null)
                    for (String line : arrDescription) {
                        if (!TextUtils.isEmpty(line)) {
                            canvas.drawText(line, left, baseline, mPaint);
                            baseline += descriptionFontHeight;
                        }
                    }
            }
    
    
            /**
             * 自动分割文本
             *
             * @param content 需要分割的文本
             * @param p       画笔,用来根据字体测量文本的宽度
             * @param width   最大的可显示像素(一般为控件的宽度)
             * @return 一个字符串数组,保存每行的文本
             */
            public String[] autoSplit(String content, TextPaint p, float width) {
    
                if (width <= 0) {
                    return null;
                }
                int length = content.length();
                Rect bounds = new Rect();
                p.getTextBounds(content, 0, content.length(), bounds);
                float textWidth = bounds.width();
                if (textWidth <= width) {
                    return new String[]{content};
                }
    
                int start = 0, end = 1, i = 0;
                int lines = (int) Math.ceil(textWidth / width); //计算行数
                String[] lineTexts = new String[lines];
                while (start < length && end < length) {
                    if (p.measureText(content, start, end) >= width) {
                        lineTexts[i++] = content.substring(start, end - 1);
                        start = end - 1;
                        end--;
                    }
                    end++;
                }
                if (i < lineTexts.length)
                    lineTexts[i++] = content.substring(start, end);
                return lineTexts;
            }
    
            public int getIntrinsicWidth() {
                return intrinsicWidth;
            }
    
            public void setIntrinsicWidth(int intrinsicWidth) {
                this.intrinsicWidth = intrinsicWidth;
            }
    
            public int getIntrinsicHeight() {
                return intrinsicHeight;
            }
    
            public void setIntrinsicHeight(int intrinsicHeight) {
                this.intrinsicHeight = intrinsicHeight;
            }
    
            public int getTextWidth(Paint paint, String str) {
                int w = 0;
                if (str != null && str.length() > 0) {
                    int len = str.length();
                    float[] widths = new float[len];
                    paint.getTextWidths(str, widths);
                    for (int j = 0; j < len; j++) {
                        w += (int) Math.ceil(widths[j]);
                    }
                }
                return w;
            }
        }
    
    
        /**
         * 每一步对应的内容
         */
        public static class Item {
            /**
             * 内容
             */
            private String content;
            /**
             * 描述
             */
            private String description;
            /**
             * 左侧的图标
             */
            private Drawable icon;
    
            /**
             * 内容
             */
            public String getContent() {
                return content;
            }
    
            /**
             * 内容
             */
            public void setContent(String content) {
                this.content = content;
            }
    
            /**
             * 描述
             */
            public String getDescription() {
                return description;
            }
    
            /**
             * 描述
             */
            public void setDescription(String description) {
                this.description = description;
            }
    
            /**
             * 左侧的图标
             */
            public Drawable getIcon() {
                return icon;
            }
    
            /**
             * 左侧的图标
             */
            public void setIcon(Drawable icon) {
                this.icon = icon;
            }
        }
    }
    

    xml code:

        <declare-styleable name="VerticalStepView">
            <attr name="drawableSize" format="dimension" />
            <attr name="drawableMarginText" format="dimension" />
            <attr name="textSize" format="dimension" />
            <attr name="descriptionTextSize" format="dimension" />
            <attr name="textColor" format="color" />
            <attr name="progressLineColor" format="color" />
            <attr name="descriptionTextColor" format="color" />
            <attr name="spacing" format="dimension" />
        </declare-styleable>
    

    图片资源:


    icon_large_amount_recharge_bank.png icon_large_amount_recharge_finish.png icon_large_amount_recharge_start.png

    Maybe you would ask how to use it?
    1. 首先在layout文件中编写如下xml

    <xin.smartlink.view.VerticalStepView
            android:id="@+id/vertical_step_view"
            android:layout_width="280dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:background="@color/white"
            android:padding="10dp"
            app:descriptionTextColor="@color/gray"
            app:descriptionTextSize="15sp"
            app:drawableMarginText="8dp"
            app:drawableSize="25dp"
            app:progressLineColor="#61a5db"
            app:spacing="25dp"
            app:textColor="@color/db_text_color_general"
            app:textSize="16sp" />
    

    2. 在Java文件中,设置steps(内容、图片和解释(可选))

    private VerticalStepView vertical_step_view;
    
    vertical_step_view = (VerticalStepView) findViewById(R.id.vertical_step_view);
    
    VerticalStepView.Item item = new VerticalStepView.Item();
    item.setContent(String.format("通过银行APP或网银转账至%s大额充值专用账户", getString(R.string.custom_bank_name)));
    item.setIcon(getResources().getDrawable(R.drawable.icon_large_amount_recharge_start));
    items.add(item);
    
    item = new VerticalStepView.Item();
    item.setContent("核实转账,等待银行处理");
    item.setIcon(getResources().getDrawable(R.drawable.icon_large_amount_recharge_bank));
    items.add(item);
    
    item = new VerticalStepView.Item();
    item.setContent("充值到账");
    item.setIcon(getResources().getDrawable(R.drawable.icon_large_amount_recharge_finish));
    item.setDescription("完成银行转账后,一般2个工作日内充值到账,具体以银行时间为准");
    items.add(item);
    
    vertical_step_view.setContentItems(items);
    

    参数说明:

    参数 说明
    drawableSize 左侧的icon大小(dp、sp、px)
    drawableMarginText 左侧的icon与文字内容的间隔(dp、sp、px)
    textSize 内容栏文字大小(dp、sp、px)
    descriptionTextSize 解释栏文字大小(dp、sp、px)
    textColor 内容栏文字颜色(color,reference)
    descriptionTextColor 解释栏文字颜色(color,reference)
    progressLineColor 进度连接线颜色(color,reference)
    spacing 内容栏之间的间隔(dp、sp、px)

    TODO:

    1. 动画
    2. 可设置阶段

    欢迎转载,请留下作者信息
    Author:Jaesoon
    E-mail:jayyuz@163.com
    简书主页:https://www.jianshu.com/u/561d203e9590

    相关文章

      网友评论

      本文标题:仿支付宝大额充值到账流程-VerticalStepView

      本文链接:https://www.haomeiwen.com/subject/zqedhftx.html