SystemUI下拉通知栏的源码分析,屏蔽通知栏,监听通知栏下拉动作

我得项目需求:下拉不显示通知栏,而是刷新view,下给出我得需求解决方法,后面是参考文章的全部内容及原文地址;

文件位置Z:\myandroid\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\PanelView.java

方法:

1,首先屏蔽通知栏,

在  public void setExpandedHeightInternal(float h) {
h = 0;//这个是通知栏下拉的高度,设置为0就不显示,不影响下拉动作的监听
        if (Float.isNaN(h)) {
            // If a NaN gets in here, it will freeze the Animators.
            if (DEBUG_NAN) {
                Log.v(TAG, "setExpandedHeightInternal: warning: h=NaN, using 0 instead",
                        new Throwable());
            }
            h = 0;
        }


        float fh = getFullHeight();
        if (fh == 0) {
            // Hmm, full height hasn't been computed yet
        }


        if (h < 0) h = 0;
        if (!(mRubberbandingEnabled && (mTracking || mRubberbanding)) && h > fh) h = fh;


        mExpandedHeight = h;


        if (DEBUG) logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");


        requestLayout();
//        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
//        lp.height = (int) mExpandedHeight;
//        setLayoutParams(lp);


        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
    }

2,下拉时监听,做自己想做的事

 protected void onFinishInflate() {
        super.onFinishInflate();
        mHandleView = findViewById(R.id.handle);


        loadDimens();


        if (DEBUG) logf("handle view: " + mHandleView);
        if (mHandleView != null) {
            mHandleView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    int pointerIndex = event.findPointerIndex(mTrackingPointer);
                    if (pointerIndex < 0) {
                        pointerIndex = 0;
                        mTrackingPointer = event.getPointerId(pointerIndex);
                    }
                    final float y = event.getY(pointerIndex);
                    final float rawDelta = event.getRawY() - event.getY();
                    final float rawY = y + rawDelta;
                    if (DEBUG) logf("handle.onTouch: a=%s p=[%d,%d] y=%.1f rawY=%.1f off=%.1f",
                            MotionEvent.actionToString(event.getAction()),
                            mTrackingPointer, pointerIndex,
                            y, rawY, mTouchOffset);
                    PanelView.this.getLocationOnScreen(mAbsPos);


                    switch (event.getActionMasked()) {
                        case MotionEvent.ACTION_DOWN:
                            mTracking = true;
                            mHandleView.setPressed(true);
                            postInvalidate(); // catch the press state change
                            mInitialTouchY = y;
                            mVelocityTracker = FlingTracker.obtain();
                            trackMovement(event);
                            mTimeAnimator.cancel(); // end any outstanding animations
                            mBar.onTrackingStarted(PanelView.this);
                            mTouchOffset = (rawY - mAbsPos[1]) - mExpandedHeight;
                            if (mExpandedHeight == 0) {
                                mJustPeeked = true;
                                runPeekAnimation();
                            }
                            break;


                        case MotionEvent.ACTION_POINTER_UP:
                            final int upPointer = event.getPointerId(event.getActionIndex());
                            if (mTrackingPointer == upPointer) {
                                // gesture is ongoing, find a new pointer to track
                                final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
                                final float newY = event.getY(newIndex);
                                final float newRawY = newY + rawDelta;
                                mTrackingPointer = event.getPointerId(newIndex);
                                mTouchOffset = (newRawY - mAbsPos[1]) - mExpandedHeight;
                                mInitialTouchY = newY;
                            }
                            break;


                        case MotionEvent.ACTION_MOVE:
                            final float h = rawY - mAbsPos[1] - mTouchOffset;
                            if (h > mPeekHeight) {
                                if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
                                    mPeekAnimator.cancel();
                                }
                                mJustPeeked = false;
                            }
                            if (!mJustPeeked) {
                                PanelView.this.setExpandedHeightInternal(h);
                                mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
                            }


                            trackMovement(event);
                            break;


                        case MotionEvent.ACTION_UP:
Log.i("PanelView", "MotionEvent.ACTION_UP");//在up时添加自己要做的事
Intent intent = new Intent();
intent.setAction("RefreshView");
getContext().sendBroadcast(intent);

                        case MotionEvent.ACTION_CANCEL:
                            mFinalTouchY = y;
                            mTracking = false;
                            mTrackingPointer = -1;
                            mHandleView.setPressed(false);
                            postInvalidate(); // catch the press state change
                            mBar.onTrackingStopped(PanelView.this);
                            trackMovement(event);


原文地址:http://blog.csdn.net/garment1991/article/details/50437571

文章内容如下:

super_status_bar.xml是systemUI的一个总的布局文件。
下面是super_status_bar.xml的源码:
<com.android.systemui.statusbar.phone.StatusBarWindowView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:focusable="true"
    android:descendantFocusability="afterDescendants"
    android:fitsSystemWindows="true"
    android:background="@android:color/transparent"
    >

    <include layout="@layout/status_bar"
        android:layout_width="match_parent"
        android:layout_height="@*android:dimen/status_bar_height"
        />
    <com.android.systemui.statusbar.phone.PanelHolder
        android:id="@+id/panel_holder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        <include layout="@layout/status_bar_expanded"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
        <ViewStub android:id="@+id/quick_settings_stub"
            android:layout="@layout/quick_settings"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    </com.android.systemui.statusbar.phone.PanelHolder>

</com.android.systemui.statusbar.phone.StatusBarWindowView>

从上面的源码可以知道,systemUI布局大概分为三个部分
第一:status_bar,这部分是状态栏。
第二:status_bar_expanded,这部分是下拉通知栏。
第三:quick_settings_stub,这部分是下拉快捷设置栏。
其中,本文中是重点分析下拉通知栏。

*************************************************分割线**************************************
下面是下拉通知栏的布局文件status_bar_expended.xml:
<com.android.systemui.statusbar.phone.NotificationPanelView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
    android:id="@+id/notification_panel"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:background="@drawable/notification_panel_bg"
    android:paddingTop="@dimen/notification_panel_padding_top"
    android:layout_marginStart="@dimen/notification_panel_margin_left"
    >

    <View
        android:id="@+id/handle"
        android:layout_width="match_parent"
        android:layout_height="@dimen/close_handle_height"
        android:background="@drawable/status_bar_close"
        android:visibility="invisible"
        />

    <include
        layout="@layout/carrier_label"
        android:layout_height="@dimen/carrier_label_height"
        android:layout_width="match_parent"
        android:layout_marginBottom="@dimen/close_handle_height"
        android:layout_gravity="bottom"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/close_handle_underlap"
        android:orientation="vertical"
        android:animateLayoutChanges="false"
        >

        <include layout="@layout/status_bar_expanded_header"
            android:layout_width="match_parent"
            android:layout_height="@dimen/notification_panel_header_height"
            />

        <TextView
            android:id="@+id/emergency_calls_only"
            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:padding="4dp"
            android:gravity="center"
            android:visibility="gone"
            />

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            >
            <ViewStub android:id="@+id/flip_settings_stub"
                android:layout="@layout/flip_settings"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
    
            <ScrollView
                android:id="@+id/scroll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fadingEdge="none"
                android:overScrollMode="ifContentScrolls"
                >
                <com.android.systemui.statusbar.policy.NotificationRowLayout
                    android:id="@+id/latestItems"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    systemui:rowHeight="@dimen/notification_row_min_height"
                    />
            </ScrollView>
        </FrameLayout>
    </LinearLayout>
</com.android.systemui.statusbar.phone.NotificationPanelView>

其中,代码段: <include layout="@layout/status_bar_expanded_header"
            android:layout_width="match_parent"
            android:layout_height="@dimen/notification_panel_header_height"
            />
该代码段是下拉通知栏的顶部的布局,包括时间,日期和一个清除通知消息的按钮。


代码段:<com.android.systemui.statusbar.policy.NotificationRowLayout
                    android:id="@+id/latestItems"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    systemui:rowHeight="@dimen/notification_row_min_height"
                    />
该代码段是用于存放下拉通知栏里面的各种通知消息。


下面主要分析com.android.systemui.statusbar.phone.NotificationPanelView下拉通知栏的实现类的源码:
public class NotificationPanelView extends PanelView {};由类定义的代码可以知道该类是继承PanelView类,PanelView类则是继承于FrameLayout。
下拉菜单栏的主要实现都是在NotificationPanelView的父类PanelView中实现。
看以下的代码段:
protected void onFinishInflate() {
        super.onFinishInflate();
        mHandleView = findViewById(R.id.handle);

        loadDimens();

        if (DEBUG) logf("handle view: " + mHandleView);
        if (mHandleView != null) {
            mHandleView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                            Log.i("Garment30", "onTouch");
                    int pointerIndex = event.findPointerIndex(mTrackingPointer);
                    if (pointerIndex < 0) {
                        pointerIndex = 0;
                        mTrackingPointer = event.getPointerId(pointerIndex);
                    }
                    final float y = event.getY(pointerIndex);
                    final float rawDelta = event.getRawY() - event.getY();
                    final float rawY = y + rawDelta;
                    if (DEBUG) logf("handle.onTouch: a=%s p=[%d,%d] y=%.1f rawY=%.1f off=%.1f",
                            MotionEvent.actionToString(event.getAction()),
                            mTrackingPointer, pointerIndex,
                            y, rawY, mTouchOffset);
                    PanelView.this.getLocationOnScreen(mAbsPos);

                    switch (event.getActionMasked()) {
                        case MotionEvent.ACTION_DOWN:
                            Log.i("Garment30", "ACTION_DOWN");
                            mTracking = true;
                            mHandleView.setPressed(true);
                            postInvalidate(); // catch the press state change
                            mInitialTouchY = y;
                            mVelocityTracker = FlingTracker.obtain();
                            trackMovement(event);
                            mTimeAnimator.cancel(); // end any outstanding animations
                            mBar.onTrackingStarted(PanelView.this);
                            mTouchOffset = (rawY - mAbsPos[1]) - mExpandedHeight;
                            if (mExpandedHeight == 0) {
                                mJustPeeked = true;
                                runPeekAnimation();
                            }
                            break;

                        case MotionEvent.ACTION_POINTER_UP:
                            Log.i("Garment30", "ACTION_POINTER_UP");
                            final int upPointer = event.getPointerId(event.getActionIndex());
                            if (mTrackingPointer == upPointer) {
                                // gesture is ongoing, find a new pointer to track
                                final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
                                final float newY = event.getY(newIndex);
                                final float newRawY = newY + rawDelta;
                                mTrackingPointer = event.getPointerId(newIndex);
                                mTouchOffset = (newRawY - mAbsPos[1]) - mExpandedHeight;
                                mInitialTouchY = newY;
                            }
                            break;

                        case MotionEvent.ACTION_MOVE:
                            Log.i("Garment30", "ACTION_MOVE");

                                                Log.i("Garment28", "PanelView---ACTION_MOVE");
                            final float h = rawY - mAbsPos[1] - mTouchOffset;
                            if (h > mPeekHeight) {
                                                Log.i("Garment28", "PanelView---h > mPeekHeight");
                                if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
                                    mPeekAnimator.cancel();
                                }
                                mJustPeeked = false;
                            }
                            if (!mJustPeeked) {
                                                            Log.i("Garment28", "PanelView---!mJustPeeked--h:"+h);
                                                            //下面两段代码需要同时使用
                              PanelView.this.setExpandedHeightInternal(h);//屏蔽了这段代码,下拉菜单无法随手的移动一起下拉,松手后菜单移动到默认的长度
                              mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);//屏蔽了这段代码,下拉通知栏显示为透明,松手后才正常
                            }

                            trackMovement(event);
                            break;

                        case MotionEvent.ACTION_UP:
                        case MotionEvent.ACTION_CANCEL:
                            Log.i("Garment30", "ACTION_CANCEL");
                            mFinalTouchY = y;
                            mTracking = false;
                            mTrackingPointer = -1;
                            mHandleView.setPressed(false);
                            postInvalidate(); // catch the press state change
                            mBar.onTrackingStopped(PanelView.this);
                            trackMovement(event);

                            float vel = 0, yVel = 0, xVel = 0;
                            boolean negative = false;

                            if (mVelocityTracker != null) {
                                // the velocitytracker might be null if we got a bad input stream
                                mVelocityTracker.computeCurrentVelocity(1000);

                                yVel = mVelocityTracker.getYVelocity();
                                negative = yVel < 0;

                                xVel = mVelocityTracker.getXVelocity();
                                if (xVel < 0) {
                                    xVel = -xVel;
                                }
                                if (xVel > mFlingGestureMaxXVelocityPx) {
                                    xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
                                }

                                vel = (float)Math.hypot(yVel, xVel);
                                if (vel > mFlingGestureMaxOutputVelocityPx) {
                                    vel = mFlingGestureMaxOutputVelocityPx;
                                }

                                mVelocityTracker.recycle();
                                mVelocityTracker = null;
                            }

                            // if you've barely moved your finger, we treat the velocity as 0
                            // preventing spurious flings due to touch screen jitter
                            final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY);
                            if (deltaY < mFlingGestureMinDistPx
                                    || vel < mFlingExpandMinVelocityPx
                                    ) {
                                vel = 0;
                            }

                            if (negative) {
                                vel = -vel;
                            }

                            if (DEBUG) logf("gesture: dy=%f vel=(%f,%f) vlinear=%f",
                                    deltaY,
                                    xVel, yVel,
                                    vel);

                            fling(vel, true);    //松手后下拉通知栏回弹的动画

                            break;
                    }
                    return true;
                }});
        }
    }
其中,mHandleView = findViewById(R.id.handle);代码用于初始化下拉通知栏最下面的那一条直线,可以知道该view的触摸事件用于控制下拉通知栏的拉动时的高度的变化。
在MotionEvent.ACTION_MOVE事件中PanelView.this.setExpandedHeightInternal(h);这段代码实现了拉动过程在中下拉通知栏的大小的改变,传入的h变量就是下拉通知栏的高度。看该方法的实现:
public void setExpandedHeightInternal(float h) {
        if (Float.isNaN(h)) {
            // If a NaN gets in here, it will freeze the Animators.
            if (DEBUG_NAN) {
                Log.v(TAG, "setExpandedHeightInternal: warning: h=NaN, using 0 instead",
                        new Throwable());
            }
            h = 0;
        }

        float fh = getFullHeight();
        if (fh == 0) {
            // Hmm, full height hasn't been computed yet
        }

        if (h < 0) h = 0;
        if (!(mRubberbandingEnabled && (mTracking || mRubberbanding)) && h > fh) h = fh;

        mExpandedHeight = h; //更新最新的通知栏的长度,屏蔽了这段代码,通知栏无法拉出

        if (DEBUG) logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
        Log.i("Garment30", "PanelView---setExpandedHeightInternal--h:"+h);
        requestLayout(); //触发OnMeasure()和onLayout()进行View的重绘, 屏蔽这段代码,下拉通知栏无法显示,初始时h=0,默认状态时,h=729,最大为1919
//        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
//        lp.height = (int) mExpandedHeight;
//        setLayoutParams(lp);

        mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh);
    }
在setExpandedHeightInternal()函数中,mExpandedHeight = h;这段代码用于更新需要重画的最新的下拉菜单栏的高度。
 requestLayout();该函数触发了OnMeasure()和onLayout()函数,对该View进行重新测量并重新布局。

当手松开的时候,通知栏会有一个回弹的动作,看到fling(vel, true);这段代码:
public void fling(float vel, boolean always) {
        if (DEBUG) logf("fling: vel=%.3f, this=%s", vel, this);
        mVel = vel;

        if (always||mVel != 0) {
            animationTick(0); // begin the animation  开始执行回弹的动画
        }
    }
其中animationTick(0);是回弹动画的实现方法:
 private void animationTick(long dtms) {
        if (!mTimeAnimator.isStarted()) {
            // XXX HAX to work around bug in TimeAnimator.end() not resetting its last time
            mTimeAnimator = new TimeAnimator();
            mTimeAnimator.setTimeListener(mAnimationCallback);

            if (mPeekAnimator != null) mPeekAnimator.cancel();

            mTimeAnimator.start();

            mRubberbanding = mRubberbandingEnabled // is it enabled at all?
                    && mExpandedHeight > getFullHeight() // are we past the end?
                    && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture?
            if (mRubberbanding) {
                mClosing = true;
            } else if (mVel == 0) {
                // if the panel is less than halfway open, close it
                mClosing = (mFinalTouchY / getFullHeight()) < 0.5f;
            } else {
                mClosing = mExpandedHeight > 0 && mVel < 0;
            }
        } else if (dtms > 0) {
            final float dt = dtms * 0.001f;                  // ms -> s
            if (DEBUG) logf("tick: v=%.2fpx/s dt=%.4fs", mVel, dt);
            if (DEBUG) logf("tick: before: h=%d", (int) mExpandedHeight);

            final float fh = getFullHeight();
            boolean braking = false;
            if (BRAKES) {
                if (mClosing) {
                    braking = mExpandedHeight <= mCollapseBrakingDistancePx;
                    mAccel = braking ? 10*mCollapseAccelPx : -mCollapseAccelPx;
                } else {
                    braking = mExpandedHeight >= (fh-mExpandBrakingDistancePx);
                    mAccel = braking ? 10*-mExpandAccelPx : mExpandAccelPx;
                }
            } else {
                mAccel = mClosing ? -mCollapseAccelPx : mExpandAccelPx;
            }

            mVel += mAccel * dt;

            if (braking) {
                if (mClosing && mVel > -mBrakingSpeedPx) {
                    mVel = -mBrakingSpeedPx;
                } else if (!mClosing && mVel < mBrakingSpeedPx) {
                    mVel = mBrakingSpeedPx;
                }
            } else {
                if (mClosing && mVel > -mFlingCollapseMinVelocityPx) {
                    mVel = -mFlingCollapseMinVelocityPx;
                } else if (!mClosing && mVel > mFlingGestureMaxOutputVelocityPx) {
                    mVel = mFlingGestureMaxOutputVelocityPx;
                }
            }

            float h = mExpandedHeight + mVel * dt;

            if (mRubberbanding && h < fh) {
                h = fh;
            }

            if (DEBUG) logf("tick: new h=%d closing=%s", (int) h, mClosing?"true":"false");
            Log.i("Garment30", "PanelView---animationTick--h:"+h);  //end at 729

            setExpandedHeightInternal(h);

            mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);

            if (mVel == 0
                    || (mClosing && mExpandedHeight == 0)
                    || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
                post(mStopAnimator);       //回弹动画停止
            }
        } else {
            Log.v(TAG, "animationTick called with dtms=" + dtms + "; nothing to do (h="
                    + mExpandedHeight + " v=" + mVel + ")");
        }
    }
可以看到,setExpandedHeightInternal(h); mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);该方法中有调用这两个方法对下拉菜单栏的长度进行更新的操作。mTimeAnimator = new TimeAnimator();mTimeAnimator.setTimeListener(mAnimationCallback);也初始化了一个TimeAnimator对象,并设置了监听器。
private final TimeListener mAnimationCallback = new TimeListener() {
        @Override
        public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
            animationTick(deltaTime);
        }
    };
该监听器中的回调函数调用了animationTick(deltaTime)下拉通知栏的回弹动画的方法,所以我们松开手后通知栏会有一个回弹的动作。
当满足一定条件时,回弹动画停止。
if (mVel == 0|| (mClosing && mExpandedHeight == 0)|| ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
                post(mStopAnimator);       //回弹动画停止
            }

------------------------------------分割线----------------------------------------------------
注意:下拉快捷设置栏的实现和下拉通知栏的实现是差不多,只是装载的内容不同。所以也可以通过类似的方法来分析下拉快速设置栏。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android中,如果想要自定义下拉通知的颜色,可以通过修改SystemUI的相关设置来实现。 首先,为了修改SystemUI的颜色,需要获取相应的权限。我们可以在AndroidManifest.xml文件中添加如下代码: ```xml <uses-permission android:name="android.permission.STATUS_BAR"/> ``` 接下来,在我们的项目中创建一个名为values的文件夹,并在其中创建一个名为colors.xml的文件。在这个文件中,我们可以定义我们想要使用的颜色。例如,我们可以定义一个名为notification_background的颜色,用于设置下拉通知的背景颜色。代码如下: ```xml <resources> <color name="notification_background">#FF0000</color> </resources> ``` 然后,我们需要修改SystemUI的源代码,以更新背景颜色。具体来说,我们需要找到StatusBar类中的updateResources方法,并在该方法中添加以下代码: ```java Context context = mContext.createPackageContext("com.example.notificationtest", Context.CONTEXT_IGNORE_SECURITY); // 替换为自己的包名 int color = context.getResources().getColor(R.color.notification_background); mBackgroundView.setBackgroundColor(color); ``` 最后,我们需要重新编译并安装我们的应用程序。一旦安装完成,我们就可以看到下拉通知的背景颜色已经根据我们在colors.xml中定义的颜色进行了自定义。 以上是通过修改SystemUI的方式来自定义下拉通知的颜色。请注意,这种方式需要具备系统级权限,因此只适用于特定的Android设备。在实际开发中,请确保在使用这种方式之前了解并遵守相关的法规和政策,以避免违规行为。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值