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

发表于:2018-4-10 13:23

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Jaesoon    来源:51testing软件测试网采编

#
流程
分享:
  银行类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);
  参数说明:
  TODO:
  动画
  可设置阶段


上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号