Android的业务逻辑

1.基于监听的事件处理机制

基于监听的事件处理机制,事件源(组件)和时间监听器是分离的,当事件源发生特定事件后,该事件交给事件监听器来处理。其优点是模型分工明确,事件源、事件监听器的两个类分来实现,具有很好的可维护性。

事件监听机制中由事件源,事件,事件监听器三类对象组成处理流程如下:
1. 为某个事件源(组件)设置一个监听器,用于监听用户操作;
2. 用户的操作,触发了事件源(组件)的监听器;
3. 生成了对应的事件对象;
4. 将这个事件源对象作为参数传给事件监听器;
5. 事件监听器对事件对象进行判断,执行对应的事件处理器(对应事件的处理方法);

常用的使用方式:

1. 直接使用匿名类

btnshow = (Button) findViewById(R.id.btnshow);    
btnshow.setOnClickListener(new OnClickListener() {    
	//重写点击事件的处理方法onClick()    
	@Override    
	public void onClick(View v) {    
	//显示Toast信息    
    Toast.makeText(getApplicationContext(), "你点击了按钮", Toast.LENGTH_SHORT).show();    
    }    
});    

2. 使用内部类

protected void onCreate(Bundle savedInstanceState) {    
	super.onCreate(savedInstanceState);    
	setContentView(R.layout.activity_main);    
	btnshow = (Button)findViewById(R.id.btnshow);    
	//直接new一个内部类对象作为参数    
	btnshow.setOnClickListener(new BtnClickListener());    
}     
    //定义一个内部类,实现View.OnClickListener接口,并重写onClick()方法    
class BtnClickListener implements View.OnClickListener    
{    
	@Override    
	public void onClick(View v) {    
    	Toast.makeText(getApplicationContext(), "按钮被点击了", Toast.LENGTH_SHORT).show();   
    }    
}    

3. 直接使用Activity作为事件监听器

protected void onCreate(Bundle savedInstanceState) {    
	super.onCreate(savedInstanceState);    
	setContentView(R.layout.activity_main);    
            
	btnshow = (Button) findViewById(R.id.btnshow);    
        //直接写个this    
	btnshow.setOnClickListener(this);    
}    
//重写接口中的抽象方法    
@Override    
public void onClick(View v) {    
	Toast.makeText(getApplicationContext(), "点击了按钮", Toast.LENGTH_SHORT).show();         
}         

4. 直接绑定到标签

public void myclick(View source)    
{    
	Toast.makeText(getApplicationContext(), "按钮被点击了", Toast.LENGTH_SHORT).show();    
}    
<Button     
	android:layout_width="wrap_content"    
	android:layout_height="wrap_content"    
	android:text="按钮"    
	android:onClick="myclick"/>   

Android为不同的组件通过了不同的监听接口:

View.onClickListener				单击事件的事件监听器必须实现的接口。
View.onCreateContextMenuListener	创建上下文菜单事件的事件监听器必须实现的接口。
View.onFocusChangedListener			焦点改变事件的事件监听器必须实现的接口。
View.onKeyListener					按键事件的时间监听器必须实现的接口。
View.onLangClickListener			长单击事件的事件监听器必须实现的接口。
View.onTouchListener				触摸屏事件的事件监听器必须实现的接口。

2.基于回调的事件处理机制

回调方法

回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。
调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。

基于回调的事件处理机制简单来说就是不用监听器去实现组件,而是组件(事件源)自身的特定方法去处理事件。也就是说事件源和事件监听器是统一的,当时间源发生特定事件后,该事件还是事件源本身负责处理。

所谓的回调,在实现具有通用性质的应用架构时非常常见:
对于一个具有通用架构的程序来说,程序架构完成整个程序的通用功能、流程,但在某个特定的点上,需要一段业务相关的代码——通用的程序架构无法实现这段代码,那么程序架构会在这个点上留一个“空”。
对于Java程序来说,程序架构在这个点上的留的“空”,可以以一下两种方式存在。

1.以接口形式存在:该接口由开发者实现,实现该接口时会实现该接口的方法,那么通用的程序架构就会调用该方法来完成相关业务的处理。
2.以抽象方法(也可以说是非抽象方法)的形式存在:这就是Activity的实现形式,这些特定的点上的方法已经被定义了,如onCreate,onActivityResult等方法,开发者可以选择性地重写这些方法,通用的程序架构就会回调这些方法来完成相关业务的处理。

几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于标识该处理方法是否能完全处理该事件:

1.如果处理事件的回调方法返回true,表明该处理方法已完全处理该事件,该事件不会传播出去。
2.如果事件处理的回调方法返回false,表明该处理方法并未完全处理事件,该事件会传播到外层组件去处理。
某组件上发生的事情不仅激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity。

例如,当某个组件上发生某个按键按下的事件时,Android系统最先触发的应该是该按键上绑定的监听器,接着才触发该组件个的事件回调方法,然后还会传播到该组件所在的Activity。当然前提是事件处理方法返回的是false。

参考:https://blog.csdn.net/csxwc/article/details/9351173

组件的回调方法:

boolean onKeyDown(int keyCode,KenyEvent enent)		当用户在该组件上按下某个键时触发的方法。
boolean onKeyLongPress(int keyCode,KeyEvent event)	当用户在该组件上长按某个按键时触发的方法。
boolean onKeyShortcut(int keyCode,KeyEvent event)	当一个键盘快捷键事件触发时 触发的方法。
boolean onKeyUp(int keyCode,KeyEvent event)			当用户在该组件上松开某个按键时触发的方法。
boolean onTouchEvent(MotionEvent event)				当用户子在该组件上触发触摸屏事件时 触发的方法。
boolean onTrackballEvent(MotionEvent event)			当用户在该组件上触发轨迹球屏事件时触发该方法。

3.Handler消息传递机制

为什么要使用Handler?

Android的APP中的用户点击某些事件源,有时候事件的反馈需要更新UI,而在Android中规定只能由主线程才能操作UI。那就不用子线程了,只让主线程更新UI不就完了吗?这样不行的,由于主线程可能会进行一些耗时的操作,比如文件读取,网络访问等,这些任务最好让子线程去操作,不然程序会出现卡顿。而让子线程去更新UI的话,如果多个子线程同时进行更新UI,UI的状态时不可知的,会有线程的安全问题,因此android规定只能由主线程更新UI。而Handler机制就是子线程对主线程发送消息用的,比如子线程通知主线程去更新一个界面的图片。

一说通信,那么一般会有两个部分,一个是信息的发送,一个是信息的接受处理。

在Handler的使用中,通常就是new一个Handler对象,然后重写它的handleMessage方法。

比如实现一个图片的定时更新:

public class MainActivity extends Activity {  
  
    //定义切换的图片的数组id  
    int imgids[] = new int[]{  
        R.drawable.s_1, R.drawable.s_2,R.drawable.s_3,  
        R.drawable.s_4,R.drawable.s_5,R.drawable.s_6,  
        R.drawable.s_7,R.drawable.s_8  
    };  
    int imgstart = 0;  
      
    final Handler myHandler = new Handler()  
    {  
      @Override  
      //重写handleMessage方法,根据msg中what的值判断是否执行后续操作  
      public void handleMessage(Message msg) {  
        if(msg.what == 0x123)  
           {  
            imgchange.setImageResource(imgids[imgstart++ % 8]);  
           }  
        }  
    }; 
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        final ImageView imgchange = (ImageView) findViewById(R.id.imgchange);  
         
        //使用定时器,每隔200毫秒让handler发送一个空信息  
        new Timer().schedule(new TimerTask() {            
            @Override  
            public void run() {  
                myHandler.sendEmptyMessage(0x123);  
                  
            }  
        }, 0,200);  
        //Timer().schedule(TimerTask task, Date when, long period);三个参数的意思分别是:
        /*
        1 ,task:所安排的时间线程
        2,when:第一次执行的时间
        3, period:间隔的执行时间
         */
    }  
} 

4.TouchListener、OnTouchEvent、多点触碰

什么是触碰事件

触碰事件就是我们每对屏幕的一次操作(最常见就是点击,移动,和松开)都会产生触摸事件,每次产生的事件都会包含大量的信息(触摸点相对于当前View的x,y值,相对当前容器的x,y值,当前触摸的类型等)

这些信息都被封装在一个MotionEvent类中,我们可以通过这个类提供的方法获取到。

基于监听的TouchListener

设置图片的TouchListener,示例:

imgtouch = (ImageView)findViewById(R.id.imgtouch);  
imgtouch.setOnTouchListener(new View.OnTouchListener() {  
	@Override  
    public boolean onTouch(View v, MotionEvent event) {
    	Toast.makeText(getApplicationContext(),"XXXX",Toast.LENGTH_LONG).show();
    		return true;
   		}
    });  

onTouch(View v, MotionEvent event):
这里面的参数依次是触发触摸事件的组件,触碰事件event 封装了触发事件的详细信息,同样包括事件的类型、触发时间等信息。比如event.getX(),event.getY()

我们也可以对触摸的动作类型进行判断,使用event.getAction( )再进行判断;如:
event.getAction == MotionEvent.ACTION_DOWN:按下事件
event.getAction == MotionEvent.ACTION_MOVE:移动事件
event.getAction == MotionEvent.ACTION_UP:弹起事件

基于回调的onTouchEvent方法

首先我们需要明确的是,具有事件传递能力的是View,ViewGroup,Activity

事件传递规则:

ViewGroup接收到事件后进行事件的分派,如果自己需要处理这个事件,则进行拦截;如果不处理,则传递给子View进行处理,然后由子view进行分派,拦截和处理。可类比于:上级接到任务后进行任务分派,如果上级自己处理这个任务,则自己处理;如果不想处理,则把这个任务丢给下级进行处理…

参考:https://segmentfault.com/a/1190000004981942

那么对应的三个重要的方法:

1. dispatchTouchEvent分发:
此方法用来处理事件的分发,当事件传递给当前view时,首先就是通过调用此方法来进行传递的。
如果当前view锁包含的子view的dispatchTouchEvent方法或者当前view的onTouchEvent处理了事件,通常返回true,表示事件已消费。如果没有处理则返回false。

2. onInterceptTouchEvent拦截:
此方法用来判断某个事件是否需要拦截,如果某个view拦截了此事件,那么同一个时间序列中,此方法不会被再次调用。

3. onTouchEvent处理:
用来处理事件,如果事件被消耗了,通常就返回true,
如果不做处理,则发挥false,并且在同一个时间序列中,当前view不会再接受到事件。

如果是ViewGroup容器,则以上三个方法都有,但是如果是View组件,只包含第一第三个,因为没有子组件只能由自己处理,不需要拦截。

参考:https://blog.csdn.net/qq475703980/article/details/92385368

事件传递如图:

图片来源于:https://segmentfault.com/a/1190000004981942,侵删

伪代码:

public boolean dispatchTouchEvent(MotionEvent ev) {
	boolean consume = false;
	if(onInterceptTouchEvent(ev)) {
		consume = onTouchEvent(ev);
	} else {
		consume = child.dispatchTouchEvent(ev);
	}	
	return consume;
}

来源于:https://blog.csdn.net/qq475703980/article/details/92385368

如果实现简单的触碰事件处理,那么此方式更多用于自定义View,只需要重写onTouchEvent方法,而这种触摸事件是基于回调的,如果我们返回的值是false的话,那么事件会继续向外传播,由外面的容器或者Activity进行处理。

例如get到触碰的位置坐标:

public class TouchView extends View{  
    public float X = 50;  
    public float Y = 50;  
    
    public TouchView(Context context,AttributeSet set)  
    {  
        super(context,set);  
    }
    @Override  
    public boolean onTouchEvent(MotionEvent event) {  
        this.X = event.getX();  
        this.Y = event.getY();  
        return true; 
    }  
}

多点触碰

用的最多的就是图片的放大缩小了吧,一般设置2~4个点足够。
我们也可以对触摸的动作类型进行判断,这里有两个多点专用的操作:

  • MotionEvent.ACTION_POINTER_DOWN当屏幕上已经有一个点被按住,此时再按下其他点时触发。
  • MotionEvent.ACTION_POINTER_UP当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)。

大概流程:

  • 当我们一个手指触摸屏幕——触发ACTION_DOWN事件
  • 接着有另一个手指也触摸屏幕——触发ACTION_POINTER_DOWN事件,如果还有其他手指触摸,继续触发
  • 有一个手指离开屏幕——触发ACTION_POINTER_UP事件,继续有手指离开,继续触发
  • 当最后一个手指离开屏幕——触发ACTION_UP事件
  • 而且在整个过程中,ACTION_MOVE事件会一直不停地被触发

我们可以通过event.getX(int)或者event.getY(int)来获得不同触摸点的位置: 比如event.getX(0)可以获得第一个接触点的X坐标,event.getX(1)获得第二个接触点的X坐标这样… 另外,我们还可以在调用MotionEvent对象的getPointerCount()方法判断当前有多少个手指在触摸。

单指拖动图片,双指缩放图片示例:

private ImageView img_test;

    // 縮放控制
    private Matrix matrix = new Matrix();
    private Matrix savedMatrix = new Matrix();

    // 不同状态的表示:
    private static final int NONE = 0;
    private static final int DRAG = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;

    // 定义第一个按下的点,两只接触点的重点,以及出事的两指按下的距离:
    private PointF startPoint = new PointF();
    private PointF midPoint = new PointF();
    private float oriDis = 1f;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img_test = (ImageView) this.findViewById(R.id.img_test);
        img_test.setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ImageView view = (ImageView) v;
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        // 单指
        case MotionEvent.ACTION_DOWN:
            matrix.set(view.getImageMatrix());
            savedMatrix.set(matrix);
            startPoint.set(event.getX(), event.getY());
            mode = DRAG;
            break;
        // 双指
        case MotionEvent.ACTION_POINTER_DOWN:
            oriDis = distance(event);
            if (oriDis > 10f) {
                savedMatrix.set(matrix);
                midPoint = middle(event);
                mode = ZOOM;
            }
            break;
        // 手指放开
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_POINTER_UP:
            mode = NONE;
            break;
        // 单指滑动事件
        case MotionEvent.ACTION_MOVE:
            if (mode == DRAG) {
                // 是一个手指拖动
                matrix.set(savedMatrix);
                matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
            } else if (mode == ZOOM) {
                // 两个手指滑动
                float newDist = distance(event);
                if (newDist > 10f) {
                    matrix.set(savedMatrix);
                    float scale = newDist / oriDis;
                    matrix.postScale(scale, scale, midPoint.x, midPoint.y);
                }
            }
            break;
        }
        // 设置ImageView的Matrix
        view.setImageMatrix(matrix);
        return true;
    }

    // 计算两个触摸点之间的距离
    private float distance(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }

    // 计算两个触摸点的中点
    private PointF middle(MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        return new PointF(x / 2, y / 2);
    }

5.监听EditText的内容变化

监听EditText的内容变化

EditText设置监听器,不过这时不是OnClickListener,而是TextWatcher,我们可以调用EditText.addTextChangeListener(mTextWatcher)EditText设置内容变化监听。

TextWatcher类需要实现三个方法:

  1. public void beforeTextChanged(CharSequence s, int start,int count, int after);内容变化前
  2. public void onTextChanged(CharSequence s, int start, int before, int count);内容变化中
  3. public void afterTextChanged(Editable s);内容变化后
    一般用的多的是最后一个方法,比如限制字数,限制输入内容等。

实现一个自定义EditText,输入内容后右面出现一个×,点击可清空内容:

public class DelEditText extends EditText {

    private Drawable imgClear;
    private Context mContext;

    public DelEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        init();
    }

    private void init() {
        imgClear = mContext.getResources().getDrawable(R.drawable.delete_gray);
        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                setDrawable();
            }
        });
    }


    //绘制删除图片
    private void setDrawable(){
        if (length() < 1)
            setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
        else
            setCompoundDrawablesWithIntrinsicBounds(null, null, imgClear, null);
    }


    //当触摸范围在右侧时,触发删除方法,隐藏叉叉
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(imgClear != null && event.getAction() == MotionEvent.ACTION_UP)
        {
            int eventX = (int) event.getRawX();
            int eventY = (int) event.getRawY();
            Rect rect = new Rect();
            getGlobalVisibleRect(rect);
            rect.left = rect.right - 100;
            if (rect.contains(eventX, eventY))
                setText("");
        }
        return super.onTouchEvent(event);
    }


    @Override
    protected void finalize() throws Throwable {
        super.finalize();
    }

}

activity_main.xml

<demo.com.jay.buttondemo.DelEditText
        android:id="@+id/edit_search"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:layout_margin="10dp"
        android:background="@drawable/bg_frame_search"
        android:hint="带删除按钮的EditText~"
        android:maxLength="20"
        android:padding="5dp"
        android:singleLine="true" />

背景色
bg_frame_search.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="@color/background_white" />
    <corners android:radius="5dp" />
    <stroke android:width="1px" android:color="@color/frame_search"/>
</shape>

颜色资源
color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="reveal_color">#FFFFFF</color>
    <color name="bottom_color">#3086E4</color>
    <color name="bottom_bg">#40BAF8</color>
    <color name="frame_search">#ADAEAD</color>
    <color name="background_white">#FFFFFF</color>
    <color name="back_red">#e75049</color>
</resources>

6.Gestures手势

Android提供手势检测,并为手势提供了相应的监听器。

Android中手势交互的执行顺序:
1. 手指触碰屏幕时,触发MotionEvent事件;
2. 该事件被OnTouchListener监听,可在它的onTouch()方法中获得该MotionEvent对象;
3. 通过GestureDetector转发MotionEvent对象给OnGestureListener;
4. 可以通过OnGestureListener获得该对象,然后获取相关信息,以及做相关处理。

注: MotionEvent: 这个类用于封装手势、触摸笔、轨迹球等等的动作事件。 其内部封装了两个重要的属性X和Y,这两个属性分别用于记录横轴和纵轴的坐标。 GestureDetector: 识别各种手势。 OnGestureListener: 这是一个手势交互的监听接口,其中提供了多个抽象方法, 并根据GestureDetector的手势识别结果调用相对应的方法。

GestureListener

监听手势的关键是:GestureListener 它给我们提供了下述的回调方法:

按下onDown: 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
抛掷onFling: 手指在触摸屏上迅速移动,并松开的动作。
长按onLongPress: 手指按在持续一段时间,并且没有松开。
滚动onScroll: 手指在触摸屏上滑动。
按住onShowPress: 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
抬起onSingleTapUp:手指离开触摸屏的那一刹那。

知道了GestureListener的相关方法后,实现手势检测也很简单,步骤如下:

Step 1: 创建GestureDetector对象,创建时需实现GestureListener传入
Step 2: 将Activity或者特定组件上的TouchEvent的事件交给GestureDetector处理即可! 我们写个简单的代码来验证这个流程,即重写对应的方法:

public class MainActivity extends AppCompatActivity {

    private MyGestureListener mgListener;
    private GestureDetector mDetector;
    private final static String TAG = "MyGesture";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化GestureListener与GestureDetector对象
        mgListener = new MyGestureListener();
        mDetector = new GestureDetector(this, mgListener);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mDetector.onTouchEvent(event);
    }

    //自定义一个GestureListener,这个是View类下的,别写错哦!!!
    private class MyGestureListener implements GestureDetector.OnGestureListener {

        @Override
        public boolean onDown(MotionEvent motionEvent) {
            Log.d(TAG, "onDown:按下");
            return false;
        }

        @Override
        public void onShowPress(MotionEvent motionEvent) {
            Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
        }

        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            Log.d(TAG, "onScroll:在触摸屏上滑动");
            return false;
        }

        @Override
        public void onLongPress(MotionEvent motionEvent) {
            Log.d(TAG, "onLongPress:长按并且没有松开");
        }

        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            Log.d(TAG, "onFling:迅速滑动,并松开");
            return false;
        }
    }

}

但有时候我们不需要实现这么多的手势处理,比如只需要实现简单的滑动:官方提供了一个SimpleOnGestureListener类,只需要将上面的OnGestureListener替换SimpleOnGestureListener

比如下滑关闭Activity,上滑打开Activity:

public class MainActivity extends AppCompatActivity {

    private GestureDetector mDetector;
    private final static int MIN_MOVE = 200;   //最小距离
    private MyGestureListener mgListener;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化SimpleOnGestureListener与GestureDetector对象
        mgListener = new MyGestureListener();
        mDetector = new GestureDetector(this, mgListener);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mDetector.onTouchEvent(event);
    }

    //自定义一个GestureListener,这个是View类下的,别写错哦!!!
    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float v, float v1) {
            if(e1.getY() - e2.getY() > MIN_MOVE){
                startActivity(new Intent(MainActivity.this, MainActivity.class));
                Toast.makeText(MainActivity.this, "通过手势启动Activity", Toast.LENGTH_SHORT).show();
            }else if(e1.getY() - e2.getY()  < MIN_MOVE){
                finish();
                Toast.makeText(MainActivity.this,"通过手势关闭Activity",Toast.LENGTH_SHORT).show();
            }
            return true;
        }
    }

}

总体参考:https://www.runoob.com/w3cnote/android-tutorial-intro.html

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值