【Android 自定义 View】-->验证码输入框

概述

先描述一下具体需求吧,我们在项目中可能会遇到修改用户名及密码的需求,为保证一定的完全性,服务端一般会接入短信验证码的功能。我们需要将接受到的验证码返回给服务端进行验证。可能会有以下的界面让用户输入验证码:
在这里插入图片描述

实现

1. 特性

  • 支持设置框数量
  • 支持设置框的风格样式
  • 支持根据状态区分框颜色
  • 基于EditText实现,更优雅

2. 效果图
在这里插入图片描述
3. 属性
在这里插入图片描述
4. 代码

  • attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SplitEditText">
        <attr name="setStrokeWidth" format="dimension"/>
        <attr name="setBorderColor" format="color"/>
        <attr name="setInputBorderColor" format="color"/>
        <attr name="setFocusBorderColor" format="color"/>
        <attr name="setBoxBackgroundColor" format="color"/>
        <attr name="setBorderCornerRadius" format="dimension"/>
        <attr name="setBorderSpacing" format="dimension"/>
        <attr name="setMaxLength" format="integer"/>
        <attr name="setBorderStyle" format="enum">
            <enum name="box" value="0"/>
            <enum name="line" value="1"/>
        </attr>
        <attr name="setTextStyle" format="enum">
            <enum name="plain_text" value="0"/>
            <enum name="cipher_text" value="1"/>
        </attr>
        <attr name="setCipherMask" format="string"/>
        <attr name="setFakeBoldText" format="boolean"/>

    </declare-styleable>
</resources>
  • SplitEditText.java
public class SplitEditText extends AppCompatEditText {
    /**
     * 画笔
     */
    private Paint mPaint;

    /**
     * 画笔宽度
     */
    private float mStrokeWidth;

    /**
     * 边框颜色
     */
    private int mBorderColor = 0xFF666666;
    /**
     * 输入的边框颜色
     */
    private int mInputBorderColor = 0xFF1E90FF;
    /**
     * 焦点的边框颜色
     */
    private int mFocusBorderColor;

    /**
     * 框的背景颜色
     */
    private int mBoxBackgroundColor;

    /**
     * 框的圆角大小
     */
    private float mBorderCornerRadius;

    /**
     * 框与框之间的间距大小
     */
    private float mBorderSpacing;

    /**
     * 输入框宽度
     */
    private float mBoxWidth;

    /**
     * 输入框高度
     */
    private float mBoxHeight;

    /**
     * 允许输入的最大长度
     */
    private int mMaxLength = 6;

    /**
     * 文本长度
     */
    private int mTextLength;
    /**
     * 路径
     */
    private Path mPath;

    private RectF mRectF;
    private float[] mRadiusFirstArray;
    private float[] mRadiusLastArray;

    /**
     * 边框风格
     */
    private @BorderStyle int mBorderStyle = BorderStyle.BOX;

    /**
     * 边框风格
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({BorderStyle.BOX, BorderStyle.LINE})
    public @interface BorderStyle {
        /**
         * 框
         */
        int BOX = 0;
        /**
         * 线
         */
        int LINE = 1;
    }

    /**
     * 文本风格
     */
    private @TextStyle int mTextStyle = TextStyle.PLAIN_TEXT;


    /**
     * 文本风格
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({TextStyle.PLAIN_TEXT, TextStyle.CIPHER_TEXT})
    public @interface TextStyle {
        /**
         * 明文
         */
        int PLAIN_TEXT = 0;
        /**
         * 密文
         */
        int CIPHER_TEXT = 1;
    }

    /**
     * 密文掩码
     */
    private String mCipherMask;

    /**
     * 是否是粗体
     */
    private boolean isFakeBoldText;

    private static final String DEFAULT_CIPHER_MASK = "*";

    private boolean isDraw;

    private OnTextInputListener mOnTextInputListener;

    public SplitEditText(@NonNull Context context) {
        this(context,null);
    }

    public SplitEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,android.R.attr.editTextStyle);
    }

    public SplitEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }


    private void init(@NonNull Context context, @Nullable AttributeSet attrs){

        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        mStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1f,displayMetrics);
        mBorderSpacing = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,8f,displayMetrics);
        setPadding(0,0,0,0);

        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.SplitEditText);
        final int count = a.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = a.getIndex(i);
            if(attr == R.styleable.SplitEditText_setStrokeWidth){
                mStrokeWidth = a.getDimension(attr,mStrokeWidth);
            }else if (attr == R.styleable.SplitEditText_setBorderColor){
                mBorderColor = a.getColor(attr,mBorderColor);
            }else if (attr == R.styleable.SplitEditText_setInputBorderColor){
                mInputBorderColor = a.getColor(attr,mInputBorderColor);
            }else if (attr == R.styleable.SplitEditText_setFocusBorderColor){
                mFocusBorderColor = a.getColor(attr,mFocusBorderColor);
            }else if (attr == R.styleable.SplitEditText_setBoxBackgroundColor){
                mBoxBackgroundColor = a.getColor(attr,mBoxBackgroundColor);
            }else if (attr == R.styleable.SplitEditText_setBorderCornerRadius){
                mBorderCornerRadius = a.getDimension(attr,mBorderCornerRadius);
            }else if (attr == R.styleable.SplitEditText_setBorderSpacing){
                mBorderSpacing = a.getDimension(attr,mBorderSpacing);
            }else if (attr == R.styleable.SplitEditText_setMaxLength){
                mMaxLength = a.getInt(attr,mMaxLength);
            }else if (attr == R.styleable.SplitEditText_setBorderStyle){
                mBorderStyle = a.getInt(attr,mBorderStyle);
            }else if (attr == R.styleable.SplitEditText_setTextStyle){
                mTextStyle = a.getInt(attr,mTextStyle);
            }else if (attr == R.styleable.SplitEditText_setCipherMask){
                mCipherMask = a.getString(attr);
            }else if (attr == R.styleable.SplitEditText_setFakeBoldText){
                isFakeBoldText = a.getBoolean(attr,false);
            }
        }

        a.recycle();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.CENTER);

        mPath = new Path();
        mRadiusFirstArray = new float[8];
        mRadiusLastArray = new float[8];
        mRectF = new RectF(0,0,0,0);

        if(TextUtils.isEmpty(mCipherMask)){
            mCipherMask = DEFAULT_CIPHER_MASK;
        }else if(mCipherMask.length() > 1){
            mCipherMask = mCipherMask.substring(0,1);
        }

        setBackground(null);
        setCursorVisible(false);
        setFilters(new InputFilter[]{new InputFilter.LengthFilter(mMaxLength)});
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        int width = w - getPaddingLeft() - getPaddingRight();
        int height = h - getPaddingTop() - getPaddingBottom();
        updateSizeChanged(width,height);
    }

    private void updateSizeChanged(int width,int height){
        //如果框与框之间的间距小于0或者总间距大于控件可用宽度则将间距重置为0
        if(mBorderSpacing < 0 || (mMaxLength - 1) * mBorderSpacing > width){
            mBorderSpacing = 0;
        }
        //计算出每个框的宽度
        mBoxWidth = (width - (mMaxLength - 1) * mBorderSpacing) / mMaxLength - mStrokeWidth;
        mBoxHeight = height - mStrokeWidth;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //移除super.onDraw(canvas);不绘制EditText相关的
        //绘制边框
        drawBorders(canvas);
    }

    private void drawBorders(Canvas canvas){
        isDraw = true;
        //遍历绘制未输入文本的框边界
        for(int i = mTextLength; i < mMaxLength; i++){
            drawBorder(canvas,i,mBorderColor);
        }

        int color = mInputBorderColor != 0 ? mInputBorderColor : mBorderColor;
        //遍历绘制已输入文本的框边界
        for(int i = 0; i < mTextLength; i++){
            drawBorder(canvas,i,color);
        }

        //绘制焦点框边界
        if(mTextLength < mMaxLength && mFocusBorderColor != 0 && isFocused()){
            drawBorder(canvas,mTextLength,mFocusBorderColor);
        }
    }

    private void drawBorder(Canvas canvas,int position,int borderColor){
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setFakeBoldText(false);
        mPaint.setColor(borderColor);

        //计算出对应的矩形
        float left = getPaddingLeft() + mStrokeWidth / 2 + (mBoxWidth + mBorderSpacing) * position;
        float top = getPaddingTop() + mStrokeWidth / 2;
        mRectF.set(left,top,left + mBoxWidth,top + mBoxHeight);

        //边框风格
        switch (mBorderStyle){
            case BorderStyle.BOX:
                drawBorderBox(canvas,position,borderColor);
                break;
            case BorderStyle.LINE:
                drawBorderLine(canvas);
                break;
        }
        if(mTextLength > position && !TextUtils.isEmpty(getText())){
            drawText(canvas,position);
        }
    }

    private void drawText(Canvas canvas,int position){
        mPaint.setStrokeWidth(0);
        mPaint.setColor(getCurrentTextColor());
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setTextSize(getTextSize());
        mPaint.setFakeBoldText(isFakeBoldText);
        float x = mRectF.centerX();
        //y轴坐标 = 中心线 + 文字高度的一半 - 基线到文字底部的距离(也就是bottom)
        float y = mRectF.centerY() + (mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top) / 2 - mPaint.getFontMetrics().bottom;
        switch (mTextStyle){
            case TextStyle.PLAIN_TEXT:
                canvas.drawText(String.valueOf(getText().charAt(position)),x,y,mPaint);
                break;
            case TextStyle.CIPHER_TEXT:
                canvas.drawText(mCipherMask,x,y,mPaint);
                break;
        }
    }

    /**
     * 绘制框风格
     * @param canvas
     * @param position
     */
    private void drawBorderBox(Canvas canvas,int position,int borderColor){
        if(mBorderCornerRadius > 0){//当边框带有圆角时
            if(mBorderSpacing == 0){//当边框之间的间距为0时,只需要开始一个和最后一个框有圆角
                if(position == 0 || position == mMaxLength - 1){
                    if(mBoxBackgroundColor != 0){
                        mPaint.setStyle(Paint.Style.FILL);
                        mPaint.setColor(mBoxBackgroundColor);
                        canvas.drawPath(getRoundRectPath(mRectF,position == 0),mPaint);
                    }
                    mPaint.setStyle(Paint.Style.STROKE);
                    mPaint.setColor(borderColor);
                    canvas.drawPath(getRoundRectPath(mRectF,position == 0),mPaint);
                }else{
                    if(mBoxBackgroundColor != 0){
                        mPaint.setStyle(Paint.Style.FILL);
                        mPaint.setColor(mBoxBackgroundColor);
                        canvas.drawRect(mRectF,mPaint);
                    }
                    mPaint.setStyle(Paint.Style.STROKE);
                    mPaint.setColor(borderColor);
                    canvas.drawRect(mRectF,mPaint);
                }
            }else{
                if(mBoxBackgroundColor != 0){
                    mPaint.setStyle(Paint.Style.FILL);
                    mPaint.setColor(mBoxBackgroundColor);
                    canvas.drawRoundRect(mRectF,mBorderCornerRadius,mBorderCornerRadius,mPaint);
                }
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setColor(borderColor);
                canvas.drawRoundRect(mRectF,mBorderCornerRadius,mBorderCornerRadius,mPaint);
            }
        }else{
            if(mBoxBackgroundColor != 0){
                mPaint.setStyle(Paint.Style.FILL);
                mPaint.setColor(mBoxBackgroundColor);
                canvas.drawRect(mRectF,mPaint);
            }
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(borderColor);
            canvas.drawRect(mRectF,mPaint);
        }
    }

    /**
     * 绘制线风格
     * @param canvas
     */
    private void drawBorderLine(Canvas canvas){
        float y = getPaddingTop() + mBoxHeight;
        canvas.drawLine(mRectF.left,y,mRectF.right,y,mPaint);
    }

    private Path getRoundRectPath(RectF rectF,boolean isFirst){
        mPath.reset();
        if(isFirst){
            //左上角
            mRadiusFirstArray[0] = mBorderCornerRadius;
            mRadiusFirstArray[1] = mBorderCornerRadius;
            //左下角
            mRadiusFirstArray[6] = mBorderCornerRadius;
            mRadiusFirstArray[7] = mBorderCornerRadius;
            mPath.addRoundRect(rectF,mRadiusFirstArray, Path.Direction.CW);
        }else{
            //右上角
            mRadiusLastArray[2] = mBorderCornerRadius;
            mRadiusLastArray[3] = mBorderCornerRadius;
            //右下角
            mRadiusLastArray[4] = mBorderCornerRadius;
            mRadiusLastArray[5] = mBorderCornerRadius;
            mPath.addRoundRect(rectF,mRadiusLastArray, Path.Direction.CW);
        }
        return mPath;
    }


    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        mTextLength = text.length();
        refreshView();
        //改变监听
        if(mOnTextInputListener != null){
            mOnTextInputListener.onTextInputChanged(text.toString(),mTextLength);
            if(mTextLength == mMaxLength){
                mOnTextInputListener.onTextInputCompleted(text.toString());
            }
        }
    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        super.onSelectionChanged(selStart, selEnd);
        if (selStart == selEnd) {
            setSelection(getText() == null ? 0 : getText().length());
        }
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        //焦点改变时刷新状态
        refreshView();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        isDraw = false;
    }

    public int getBorderColor() {
        return mBorderColor;
    }

    public int getInputBorderColor() {
        return mInputBorderColor;
    }

    public int getFocusBorderColor() {
        return mFocusBorderColor;
    }

    public int getBoxBackgroundColor() {
        return mBoxBackgroundColor;
    }

    public float getBorderCornerRadius() {
        return mBorderCornerRadius;
    }

    public float getBorderSpacing() {
        return mBorderSpacing;
    }

    @BorderStyle
    public int getBorderStyle() {
        return mBorderStyle;
    }


    public void setBorderColor(int borderColor) {
        this.mBorderColor = borderColor;
        refreshView();
    }

    public void setInputBorderColor(int inputBorderColor) {
        this.mInputBorderColor = inputBorderColor;
        refreshView();
    }

    public void setFocusBorderColor(int focusBorderColor) {
        this.mFocusBorderColor = focusBorderColor;
        refreshView();
    }

    public void setBoxBackgroundColor(int boxBackgroundColor) {
        this.mBoxBackgroundColor = boxBackgroundColor;
        refreshView();
    }

    public void setBorderCornerRadius(float borderCornerRadius) {
        this.mBorderCornerRadius = borderCornerRadius;
        refreshView();
    }

    public void setBorderSpacing(float borderSpacing) {
        this.mBorderSpacing = borderSpacing;
        refreshView();
    }

    public void setBorderStyle(@TextStyle int borderStyle) {
        this.mBorderStyle = borderStyle;
        refreshView();
    }

    @TextStyle
    public int getTextStyle() {
        return mTextStyle;
    }

    public void setTextStyle(@TextStyle int textStyle) {
        this.mTextStyle = textStyle;
        refreshView();
    }

    public String getCipherMask() {
        return mCipherMask;
    }

    /**
     * 是否粗体
     * @param fakeBoldText
     */
    public void setFakeBoldText(boolean fakeBoldText) {
        isFakeBoldText = fakeBoldText;
        refreshView();
    }

    /**
     * 设置密文掩码 不设置时,默认为{@link #DEFAULT_CIPHER_MASK}
     * @param cipherMask
     */
    public void setCipherMask(String cipherMask) {
        this.mCipherMask = cipherMask;
        refreshView();
    }

    /**
     * 刷新视图
     */
    private void refreshView(){
        if(isDraw){
            invalidate();
        }
    }

    /**
     * 设置文本输入监听
     * @param onTextInputListener
     */
    public void setOnTextInputListener(OnTextInputListener onTextInputListener) {
        this.mOnTextInputListener = onTextInputListener;
    }

    public static abstract class OnSimpleTextInputListener implements OnTextInputListener{

        @Override
        public void onTextInputChanged(String text, int length) {

        }

    }

    /**
     * 文本输入监听
     */
    public interface OnTextInputListener{
        /**
         * Text改变监听
         * @param text
         * @param length
         */
        void onTextInputChanged(String text,int length);

        /**
         * Text输入完成
         * @param text
         */
        void onTextInputCompleted(String text);
    }
}

  • 使用
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_marginLeft="30dp"
    android:layout_marginRight="30dp"
    tools:context=".MainActivity">

    <com.hjq.bar.TitleBar
        android:layout_width="match_parent"
        android:layout_height="60dp"
        app:title="验证码"/>

    <com.hkt.textsurfacedemo.view.SplitEditText
        android:id="@+id/set_number"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:inputType="number"/>

    <com.hkt.textsurfacedemo.view.SplitEditText
        android:id="@+id/set_box"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:inputType="number"
        android:layout_marginTop="20dp"
        app:setFakeBoldText="true"
        app:setBorderStyle="box"
        app:setTextStyle="plain_text"
        app:setBoxBackgroundColor="@color/tab_gray"
        app:setBorderCornerRadius="10dp"/>

    <com.hkt.textsurfacedemo.view.SplitEditText
        android:id="@+id/set_box3"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:inputType="number"
        android:layout_marginTop="20dp"
        app:setMaxLength="6"
        app:setBorderStyle="line"/>
</LinearLayout>
	//设置监听
    splitEditText.setOnTextInputListener(new SplitEditText.OnTextInputListener(){

        @Override
        public void onTextInputChanged(String text, int length) {
            //TODO 文本输入改变
        }

        @Override
        public void onTextInputCompleted(String text) {
            //TODO 文本输入完成
        }
    });

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
<!--index.wxml--> <view class="top-box"> <view>Hi</view> <view class="next-text">欢迎使用!</view> </view> <!-- 登录、注册 --> <view class="center-box"> <view class="nav"> <view class="left {{current==1?'select':''}}" bindtap="click" data-code="1"> <text>登录</text> </view> <view class="right {{current==0?'select':''}}" bindtap="click" > <text>注册</text> </view> </view> <!-- 登录 --> <view class="input-box" hidden="{{current==0}}"> <view class="wei-input"> <icon type="waiting" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入手机号/登录名"/> </view> <view class="wei-input"> <icon type="success" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入登录密码"/> </view> <view class="forget"> <text>忘记密码?</text> </view> </view> <!-- 注册 --> <view class="input-box" hidden="{{current==1}}"> <view class="wei-input"> <icon type="waiting" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入手机号"/> </view> <view class="wei-input"> <icon type="waiting" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入6位验证码"/> <text class="input-code" bindtap="getCode">{{codeText}}</text> </view> <view class="wei-input"> <icon type="success" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入密码"/> </view> <view class="wei-input"> <icon type="success" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请确认密码"/> </view> </view> <view class="sumbit-btn"> <button class="button" style="background-color: #33ccff;font-size: 30rpx;" type="primary">立即{{current==1?'登录':'注册'}}</button> </view> </view> <!-- 重影 --> <view class="shadow shadow-1"></view><view class="shadow shadow-2"></view> <!-- 说明 -->这段代码怎么实现注册页面和登陆页面的切换,如果不能,给出解决代码
05-24
这段代码是一个小程序的页面布局代码,其中使用了一个变量 current 来控制显示登录还是注册页面。具体的实现逻辑是,当 current 为 0 时显示注册页面,为 1 时显示登录页面。同时,通过绑定 click 事件来切换页面。 以下是完整的代码实现: <!--index.wxml--> <view class="top-box"> <view>Hi</view> <view class="next-text">欢迎使用!</view> </view> <!-- 登录、注册 --> <view class="center-box"> <view class="nav"> <view class="left {{current==1?'select':''}}" bindtap="click" data-code="1"> <text>登录</text> </view> <view class="right {{current==0?'select':''}}" bindtap="click" data-code="0"> <text>注册</text> </view> </view> <!-- 登录 --> <view class="input-box" hidden="{{current==0}}"> <view class="wei-input"> <icon type="waiting" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入手机号/登录名"/> </view> <view class="wei-input"> <icon type="success" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入登录密码"/> </view> <view class="forget"> <text>忘记密码?</text> </view> </view> <!-- 注册 --> <view class="input-box" hidden="{{current==1}}"> <view class="wei-input"> <icon type="waiting" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入手机号"/> </view> <view class="wei-input"> <icon type="waiting" color="#44ADFB" size="16"></icon> <<input class="input" auto-focus placeholder="请输入6位验证码"/> <text class="input-code" bindtap="getCode">{{codeText}}</text> </view> <view class="wei-input"> <icon type="success" color="#44ADFB" size="16"></icon> <input class="input" auto-focus placeholder="请输入密码"/> </view> <view class="wei-input"> <icon type="success" color="#44ADFB" size="16"></icon> <<input class="input" auto-focus placeholder="请确认密码"/> </view> </view> <view class="sumbit-btn"> <button class="button" style="background-color: #33ccff;font-size: 30rpx;" type="primary">立即{{current==1?'登录':'注册'}}</button> </view> </view> <!-- 重影 --> <view class="shadow shadow-1"></view> <view class="shadow shadow-2"></view> <!-- 说明 --> <!-- js代码 --> <script> Page({ data: { current: 0, // 当前显示的页面,0为注册,1为登录 codeText: '获取验证码', }, click: function(e) { this.setData({ current: e.target.dataset.code }) }, getCode: function() { // 获取验证码的逻辑 } }) </script>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin-Dev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值