android一种不规则布局的实现方式

一.背景
正准备出去抽根烟的你突然被产品经理叫住了,“快来,快来,我告诉你一个好消息,前几天我不是说要给 app 的菜单入口这一截 UI 美化一下嘛,我昨天突发灵感,已经想好了一个很有特色的样式,这下我们的 UI 一定和别人的不一样,我们要让产品富有品牌表现力”。
在这里插入图片描述
这看起来是挺有特别的,不过感觉有违 android UI 排列常理呀,形状倒是没什么问题,关键是这无缝衔接就有点奇怪了,这形状最简单的就是直接切几个梯形图当背景就实现了,但是想让控件边缘两两完美契合就得让两个控件重叠一部分,那点击时不就会错乱了,那肯定不行。

二.思路分析
实际布局的宽是以较长边决定的,例如上图中,第二个控件和第一个控件有一部分是重叠的,这是为了让控件两两之间看上去是完美契合的。这里就要考虑一个点击区域问题了,上图中红圈部分实际上全部是第二个控件,但是当用户点击 1 的那个梯形右下角区域,应该让第一个控件响应,点击 2 的那个梯形左上角,应该让第二个控件响应。
不仅要控制点击和契合图形,最好还要能支持设置这个梯形控件是正梯形还是倒梯形(下边更宽还是上边更宽),能设置宽度数值,能设置左边或是右边是否垂直;所以总结如下:
1.支持设置宽度数值,支持设置两边是否垂直,这样用固定图片就不太合适了,干脆通过自定义 View 来实现;
2.实现两控件间完美契合,这可以在自定义 View 时让截好的梯形以外的部分透明显示,然后让右边的控件往左 margin 负的上边和下边宽度相差的一半就正好契合了。
3.支持代码动态创建布局,设置背景图片,设置文字。

三、具体实现
第一步先实现这个自定义形状的控件,大概需要这几个参数,上宽,下宽,高度,左边是否垂直,右边是否垂直

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PaintView, defStyleAttr, 0);
topWidth = ta.getDimensionPixelSize(R.styleable.PaintView_topWidth, 100);
bottomWidth = ta.getDimensionPixelSize(R.styleable.PaintView_bottomWidth, 100);
mHeight = ta.getDimensionPixelSize(R.styleable.PaintView_height, 100);
leftRect = ta.getBoolean(R.styleable.PaintView_leftRect, false);
rightRect = ta.getBoolean(R.styleable.PaintView_rightRect, false);

然后规划梯形路径

paint.setStyle(Paint.Style.FILL_AND_STROKE); //设置边框
paint.setStrokeWidth(strokWidth);
paint.setStrokeJoin(Paint.Join.ROUND); //设置圆角
mDrawPath = new Path();

int offset = strokWidth / 2;
if (topWidth > bottomWidth) {
    mDrawPath.moveTo(offset, offset);
    mDrawPath.rLineTo(topWidth, 0);
    mDrawPath.lineTo(bottomWidth + (topWidth - bottomWidth) / (rightRect ? 1 : 2) + offset, mHeight + offset);
    mDrawPath.lineTo((leftRect ? 0 : (topWidth - bottomWidth) / 2) + offset, mHeight + offset);
    mDrawPath.close();
} else {
    mDrawPath.moveTo((leftRect ? 0 : (bottomWidth - topWidth) / 2) + offset, offset);
    mDrawPath.lineTo((bottomWidth - topWidth) / (rightRect ? 1 : 2) + topWidth + offset, offset);
    mDrawPath.lineTo(bottomWidth + offset, mHeight + offset);
    mDrawPath.lineTo(offset, mHeight + offset);
    mDrawPath.close();
}

路径规划好后就开始测量绘制了

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(Math.max(topWidth, bottomWidth) + strokWidth, mHeight + strokWidth);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mDrawPath, paint);
}

下面是布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        app:bottomWidth="80dp"
        app:height="100dp"
        app:pcolor="#d78f8f"
        app:topWidth="50dp"
        app:leftRect="true"/>

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:layout_marginLeft="-14dp"
        android:layout_toRightOf="@id/ldder1"
        app:bottomWidth="50dp"
        app:height="100dp"
        app:pcolor="#bc9747"
        app:topWidth="80dp" />

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:layout_marginLeft="-14dp"
        android:layout_toRightOf="@id/ldder2"
        app:bottomWidth="80dp"
        app:height="100dp"
        app:pcolor="#d78f8f"
        app:topWidth="50dp" />

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:layout_marginLeft="-14dp"
        android:layout_toRightOf="@id/ldder3"
        app:topWidth="80dp"
        app:bottomWidth="50dp"
        app:height="100dp"
        app:rightRect="true"
        app:pcolor="#bc9747"
        />
</RelativeLayout>

然后展示出来就是这个样子的
在这里插入图片描述

可以看到 xml 文件中有个属性是 android:layout_marginLeft="-14dp" 这就是上宽和下宽相减的绝对值的一半得到的,这里 -14 而不是 -15 是为了让控件间留一条缝。
然后再设置点击区域,这个其实有现成的 api 帮我们计算,比想象中要简单的多:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (!isInRect(event)) {
                return false;
            }
        }
        return super.dispatchTouchEvent(event);
    }

    public boolean isInRect(MotionEvent event) {
        RectF rectF = new RectF();
        mDrawPath.computeBounds(rectF, true);
        mRegion.setPath(mDrawPath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
        return mRegion.contains((int) event.getX(), (int) event.getY());
    }

当手指按下时,判断是否是按在 mDrawPath内,如果不在,就不分发事件,这样边缘点击的需求就被完美的实现了。

上面就是先搞定这个不规则 UI 的核心部分逻辑,最后实现一下设置背景和文字的逻辑,可以直接让上面的自定义 View 继承自 RelativeLayout,这样可以直接在布局文件里加图片和文字控件了,就像原生写法一样:

		<com.lcp.customeview.widget.LadderLayout
            android:id="@+id/ldder1"
            style="@style/ladder"
            android:layout_marginLeft="20dp"
            app:bottomWidth="90dp"
            app:leftRect="true"
            app:topWidth="60dp">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@mipmap/ic1"
                android:scaleType="centerCrop" />

            <TextView
                android:id="@+id/textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:text="西湖风光"
                android:textColor="#333333" />
        </com.lcp.customeview.widget.LadderLayout>

然后这里有几个较上面需要修改的逻辑:
1.把画笔裁剪模式设置成 DST_IN,把整个子 View 裁切成目标路径的样式;

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

2.如果根 View 不设置背景属性,draw() 方法不会运行,除非主动设置下面的属性:

setWillNotDraw(false);

3.测量子控件,修改 ImageView 宽高并赋值 imageView 和 textView,以方便外面修改,例如把这个 ImageView 提供到外面让 Glide 去给它加载图片(此逻辑是专门针对自己的业务逻辑,其它实际场景不一定要这样处理):

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = Math.max(topWidth, bottomWidth) ;
        int measuredHeight = mHeight ;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 为每一个子控件测量大小
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            if (childView instanceof ImageView) {
                imageView = (ImageView) childView;
                ViewGroup.LayoutParams layoutParams = childView.getLayoutParams();
                layoutParams.width = measuredWidth;
                layoutParams.height = measuredHeight;
            }

            if (childView instanceof TextView) {
                textView = (TextView) childView;
            }
        }
        setMeasuredDimension(measuredWidth, measuredHeight);
    }

    public ImageView getImageView() {
        return imageView;
    }

    public TextView getTextView() {
        return textView;
    }

4.Xfermode 模式需要设置离屏缓冲,不然效果不会是你预期的:

    @Override
    public void draw(Canvas canvas) {
        int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.drawPath(mDrawPath, paint);
        canvas.restoreToCount(saved);
    }

5.api 27以上修改了绘制方式,PorterDuff.Mode.DST_IN 不是预期的方式了,有多种方式处理,例如:
a: 用 PorterDuff.Mode.DST_OUT 模式画,同时把 path 的 FillType 反一下,注意在别的用到 path 处要反转回来

    @Override
    public void draw(Canvas canvas) {
        int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
            paint.setXfermode(porterDuffXfermodeIn);
        } else {
            paint.setXfermode(porterDuffXfermodeOut);
            if (!mDrawPath.isInverseFillType()) {
                mDrawPath.toggleInverseFillType();
            }
        }
        canvas.drawPath(mDrawPath, paint);
        canvas.restoreToCount(saved);
    }
 b: 创建一个空的 path, 然后和目标切一下,发现不少人这样解决
        paint.setStyle(Paint.Style.FILL);
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
            paint.setXfermode(porterDuffXfermodeIn);
        } else {
            paint.setXfermode(porterDuffXfermodeOut);

            final Path path = new Path();
            path.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
            path.op(mDrawPath, Path.Op.DIFFERENCE);
            canvas.drawPath(path, paint);
        }

这种处理方式也能达到效果,但是 paint 不能设置成 Paint.Style.FILL_AND_STROKE 模式,这样圆角就没了,如果不要求圆角可以用这种方式处理,注意这个 path 尽量别在 onDraw() 里创建。

好了,就这几条需要注意的,然后运行一把就是下面的效果了:
在这里插入图片描述
下面是布局示例和代码创建示例:

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.lcp.customeview.widget.LadderLayout
            android:id="@+id/ldder1"
            style="@style/ladder"
            android:layout_marginLeft="20dp"
            app:bottomWidth="90dp"
            app:leftRect="true"
            app:topWidth="60dp">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@mipmap/ic1"
                android:scaleType="centerCrop" />

            <TextView
                android:id="@+id/textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:text="西湖风光"
                android:textColor="#333333" />
        </com.lcp.customeview.widget.LadderLayout>

        <com.lcp.customeview.widget.LadderLayout
            android:id="@+id/ldder2"
            style="@style/ladder"
            android:layout_marginLeft="-14dp"
            android:layout_toRightOf="@id/ldder1"
            app:bottomWidth="60dp"
            app:topWidth="90dp">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@mipmap/ic2"
                android:scaleType="centerCrop" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:text="印象杭州"
                android:textColor="#333333"
                android:textSize="12sp" />
        </com.lcp.customeview.widget.LadderLayout>

        <com.lcp.customeview.widget.LadderLayout
            android:id="@+id/ldder3"
            style="@style/ladder"
            android:layout_marginLeft="-14dp"
            android:layout_toRightOf="@id/ldder2"
            app:bottomWidth="80dp"
            app:topWidth="50dp">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@mipmap/ic3"
                android:scaleType="centerCrop" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:text="世界时钟"
                android:textColor="#333333"
                android:textSize="12sp" />
        </com.lcp.customeview.widget.LadderLayout>

        <com.lcp.customeview.widget.LadderLayout
            android:id="@+id/ldder4"
            style="@style/ladder"
            android:layout_marginLeft="-14dp"
            android:layout_toRightOf="@id/ldder3"
            app:bottomWidth="50dp"
            app:rightRect="true"
            app:topWidth="80dp">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@mipmap/ic4"
                android:scaleType="centerCrop" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:text="天气"
                android:textColor="#333333"
                android:textSize="12sp" />
        </com.lcp.customeview.widget.LadderLayout>
    </RelativeLayout>

代码创建:

    private void addView() {
        LadderLayout ladderLayout = getLadderLayout(150, 240, 300, true, false);
        RelativeLayout relativeLayout = new RelativeLayout(getActivity());
        relativeLayout.setFocusableInTouchMode(true);
        relativeLayout.setOnFocusChangeListener(this);
        relativeLayout.setClickable(true);
        RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 300);
        relativeLayout.addView(ladderLayout, layoutParams2);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.leftMargin = 200;
        layoutParams.topMargin = 600;
        rootview.addView(relativeLayout, layoutParams);

        LadderLayout ladderLayout11 = getLadderLayout(240, 150, 300, false, false);
        RelativeLayout relativeLayout11 = new RelativeLayout(getActivity());
        relativeLayout11.setFocusableInTouchMode(true);
        relativeLayout11.setOnFocusChangeListener(this);
        relativeLayout11.setClickable(true);
        RelativeLayout.LayoutParams layoutParams12 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 300);
        relativeLayout11.addView(ladderLayout11, layoutParams12);

        RelativeLayout.LayoutParams layoutParams13 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams13.leftMargin = 440 - (240 - 150) / 2 + 5;
        layoutParams13.topMargin = 600;
        rootview.addView(relativeLayout11, layoutParams13);

        LadderLayout ladderLayout21 = getLadderLayout(150, 240, 300, false, true);
        RelativeLayout relativeLayout21 = new RelativeLayout(getActivity());
        relativeLayout21.setFocusableInTouchMode(true);
        relativeLayout21.setOnFocusChangeListener(this);
        relativeLayout21.setClickable(true);
        RelativeLayout.LayoutParams layoutParams22 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 300);
        relativeLayout21.addView(ladderLayout21, layoutParams22);

        RelativeLayout.LayoutParams layoutParams23 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams23.leftMargin = 680 - ((240 - 150) / 2) * 2 + 5 * 2;
        layoutParams23.topMargin = 600;
        rootview.addView(relativeLayout21, layoutParams23);
    }

完整自定义 View:

/**
 * Created by Aislli on 2017/12/26 0026.
 */
public class LadderLayout extends RelativeLayout {

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Region mRegion;
    private Path mDrawPath;
    private int topWidth;
    private int bottomWidth;
    private int mHeight;
    private final int strokWidth = 20;
    private boolean leftRect;
    private boolean rightRect;
    private PorterDuffXfermode porterDuffXfermodeIn;
    private PorterDuffXfermode porterDuffXfermodeOut;

    public void setViewSize(int topWidth, int bottomWidth, int mHeight) {
        this.topWidth = topWidth;
        this.bottomWidth = bottomWidth;
        this.mHeight = mHeight;
    }

    public void setLeftRect(boolean leftRect) {
        this.leftRect = leftRect;
    }

    public void setRightRect(boolean rightRect) {
        this.rightRect = rightRect;
    }

    public LadderLayout(Context context) {
        this(context, null);
    }

    public LadderLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LadderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LadderLayout, defStyleAttr, 0);
        topWidth = ta.getDimensionPixelSize(R.styleable.LadderLayout_topWidth, 100);
        bottomWidth = ta.getDimensionPixelSize(R.styleable.LadderLayout_bottomWidth, 100);
//        mHeight = ta.getLayoutDimension(R.styleable.LadderLayout_android_layout_height, "layout_height");
        mHeight = ta.getLayoutDimension(R.styleable.LadderLayout_android_layout_height, 100);
        leftRect = ta.getBoolean(R.styleable.LadderLayout_leftRect, false);
        rightRect = ta.getBoolean(R.styleable.LadderLayout_rightRect, false);
        ta.recycle();

        init();
    }

    public void init() {
        mRegion = new Region();
        mDrawPath = new Path();

        int offset = strokWidth / 2;//偏移stroke的一半以正好显示出圆角
        int tTopWidth = topWidth - strokWidth;//归还偏移的尺寸
        int tBottomWidth = bottomWidth - strokWidth;
        int tHeight = mHeight - strokWidth;
        if (topWidth > bottomWidth) {
            mDrawPath.moveTo(offset, offset);
            mDrawPath.rLineTo(tTopWidth, 0);
            mDrawPath.lineTo(tBottomWidth + (tTopWidth - tBottomWidth) / (rightRect ? 1 : 2) + offset, tHeight + offset);
            mDrawPath.lineTo((leftRect ? 0 : (tTopWidth - tBottomWidth) / 2) + offset, tHeight + offset);
            mDrawPath.close();
        } else {
            mDrawPath.moveTo((leftRect ? 0 : (tBottomWidth - tTopWidth) / 2) + offset, offset);
            mDrawPath.lineTo((tBottomWidth - tTopWidth) / (rightRect ? 1 : 2) + tTopWidth + offset, offset);
            mDrawPath.lineTo(tBottomWidth + offset, tHeight + offset);
            mDrawPath.lineTo(offset, tHeight + offset);
            mDrawPath.close();
        }
    }

    {
        porterDuffXfermodeIn = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        porterDuffXfermodeOut = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
        setWillNotDraw(false);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);//设置STROKE才能设置成圆角
        paint.setStrokeWidth(strokWidth);
        paint.setStrokeJoin(Paint.Join.ROUND);
    }

    ImageView imageView;
    TextView textView;

    public ImageView getImageView() {
        return imageView;
    }

    public TextView getTextView() {
        return textView;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = Math.max(topWidth, bottomWidth);
        int measuredHeight = mHeight;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 为每一个子控件测量大小
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            if (childView instanceof ImageView) {
                imageView = (ImageView) childView;
                ViewGroup.LayoutParams layoutParams = childView.getLayoutParams();
                layoutParams.width = measuredWidth;
                layoutParams.height = measuredHeight;
            }

            if (childView instanceof TextView) {
                textView = (TextView) childView;
            }
        }
        setMeasuredDimension(measuredWidth, measuredHeight);
    }

    @Override
    public void draw(Canvas canvas) {
        int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) {
            paint.setXfermode(porterDuffXfermodeIn);
        } else {
            paint.setXfermode(porterDuffXfermodeOut);
            if (!mDrawPath.isInverseFillType()) {
                mDrawPath.toggleInverseFillType();
            }
        }
        canvas.drawPath(mDrawPath, paint);
        canvas.restoreToCount(saved);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (!isInRect(event)) {
                return false;
            }
        }
        return super.dispatchTouchEvent(event);
    }

    public boolean isInRect(MotionEvent event) {
        if (mDrawPath.isInverseFillType()) {
            mDrawPath.toggleInverseFillType();
        }
        RectF rectF = new RectF();
        mDrawPath.computeBounds(rectF, true);
        mRegion.setPath(mDrawPath, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
        return mRegion.contains((int) event.getX(), (int) event.getY());
    }
}

源码

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值