android 自定义裁剪View


1 自定义可裁剪的VIew,其中的mBmpPath,是图片路径,从其他页面传过来的,测试时可改成本地文件地址。80385897

主要功能,自定义裁剪框的初始位置,手动伸缩裁剪框

 

package com.minzh.onemedia.widget;

import android.content.Context;
import android.drm.DrmStore.RightsStatus;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

public class MyCropView extends ImageView {

    // Private Constants ///
    private static final float BMP_LEFT = 5f;
    private static final float BMP_TOP = 5f;

    private static  float DEFAULT_BORDER_RECT_WIDTH = 200f;
    private static  float DEFAULT_BORDER_RECT_HEIGHT = 200f;

    private static final int POS_TOP_LEFT = 0;
    private static final int POS_TOP_RIGHT = 1;
    private static final int POS_BOTTOM_LEFT = 2;
    private static final int POS_BOTTOM_RIGHT = 3;
    private static final int POS_TOP = 4;
    private static final int POS_BOTTOM = 5;
    private static final int POS_LEFT = 6;
    private static final int POS_RIGHT = 7;
    private static final int POS_CENTER = 8;

    // this constant would be best to use event number
    private static final float BORDER_LINE_WIDTH = 6f;
    private static final float BORDER_CORNER_LENGTH = 30f;
    private static final float TOUCH_FIELD = 10f;

    // Member Variables 
    private String mBmpPath;
    private Bitmap mBmpToCrop;
    private RectF mBmpBound;
    private Paint mBmpPaint;

    private Paint mBorderPaint;// 裁剪区边框
    private Paint mGuidelinePaint;
    private Paint mCornerPaint;
    private Paint mBgPaint;

//    private RectF mDefaultBorderBound;
    private RectF mBorderBound;

    private PointF mLastPoint = new PointF();

    private float mBorderWidth;
    private float mBorderHeight;

    private int touchPos;

    // Constructors 
    public MyCropView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        init(context);
    }

    public MyCropView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    // View Methods 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        // super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        // super.onDraw(canvas);
    	
    	this.setScaleType(ImageView.ScaleType.FIT_CENTER);
    	
    	
    	
        if (mBmpPath != null) {
        
            canvas.drawBitmap(mBmpToCrop, null, mBmpBound, mBmpPaint);
            canvas.drawRect(mBorderBound.left, mBorderBound.top, mBorderBound.right, mBorderBound.bottom, mBorderPaint);
            drawGuidlines(canvas);
            drawBackground(canvas);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        // super.onTouchEvent(event);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            setLastPosition(event);
            getParent().requestDisallowInterceptTouchEvent(true);
            // onActionDown(event.getX(), event.getY());
            touchPos = detectTouchPosition(event.getX(), event.getY());
            break;
        case MotionEvent.ACTION_MOVE:
            onActionMove(event.getX(), event.getY());
            setLastPosition(event);
            break;
        case MotionEvent.ACTION_UP:
            break;
        }

        return true;
    }

    // Public Methods //
    public String getBmpPath() {
        return mBmpPath;
    }

    public void setBmpPath(String picPath) {
        this.mBmpPath = picPath;
        setBmp();
    }

    public Bitmap getCroppedImage() {
        // 等比例充满屏幕宽度的情况下,裁剪框等比例缩减到图片实际尺寸的裁剪位置
    	int imgWSource=mBmpToCrop.getWidth();
        int imgHSource=mBmpToCrop.getHeight();
    	double rateW=imgWSource*1.0/imgW;
    	double rateH=imgHSource*1.0/imgH;
    	
    	int left=(int) (rateW*mBorderBound.left);
    	left=left==0?1:left;
    	int top= (int) (rateH* mBorderBound.top);
    	top=top==0?1:top;
    	int width=(int) (rateW* mBorderWidth);
    	int height=(int) (rateH* mBorderHeight);
    	
    	if(left+width>imgWSource){
    		width=imgWSource-left;
    	}
    	if(top+height>imgHSource){
    		height=imgHSource-top;
    	}
    	Bitmap bit=Bitmap.createBitmap(mBmpToCrop, left, top, width,
                height);
        return bit;
    }

    // Private Methods /
    private void init(Context context) {

        mBmpPaint = new Paint();
        // 以下是抗锯齿
        mBmpPaint.setAntiAlias(true);// 防止边缘的锯齿
        mBmpPaint.setFilterBitmap(true);// 对位图进行滤波处理

        mBorderPaint = new Paint();
        mBorderPaint.setStyle(Style.STROKE);
        mBorderPaint.setColor(Color.parseColor("#AAFFFFFF"));
        mBorderPaint.setStrokeWidth(BORDER_LINE_WIDTH);

        mGuidelinePaint = new Paint();
        mGuidelinePaint.setColor(Color.parseColor("#AAFFFFFF"));
        mGuidelinePaint.setStrokeWidth(1f);

        mCornerPaint = new Paint();

        mBgPaint = new Paint();
        mBgPaint.setColor(Color.parseColor("#B0000000"));
        mBgPaint.setAlpha(150);

    }
    int w_screen,h_screen;
    int imgW,imgH;
    
    private void setBmp() {
    	DisplayMetrics dm =getResources().getDisplayMetrics();  
        w_screen = dm.widthPixels;  
        h_screen = dm.heightPixels; 
        
       
        mBmpToCrop = BitmapFactory.decodeFile(mBmpPath);
        imgW=mBmpToCrop.getWidth();
        imgH=mBmpToCrop.getHeight();
        
//        if(w_screen*1.0/h_screen<imgW*1.0/imgH){
//        	//宽度为主显示图片
//        	imgH=imgH*w_screen/imgW;
//        	imgW=w_screen;
//        }else{
//        	//高度为主显示图片
//        	imgW=imgW*h_screen/imgH;
//        	imgH=h_screen;
//        }
        
      //宽度为主显示图片,等比例充满屏幕宽度
    	imgH=imgH*w_screen/imgW;
    	imgW=w_screen;
    	
    	
    	
        
        //图片大小
        mBmpBound = new RectF();
        mBmpBound.left = BMP_LEFT;
        mBmpBound.top = BMP_TOP;
        mBmpBound.right = imgW-mBmpBound.left;
        mBmpBound.bottom =imgH-mBmpBound.top;

        //定义 裁剪框大小
        DEFAULT_BORDER_RECT_WIDTH= imgW-mBmpBound.left;
        DEFAULT_BORDER_RECT_HEIGHT= DEFAULT_BORDER_RECT_WIDTH*4/5;
        
       
     // 使裁剪框一开始出现在图片的中心位置
//        mBorderBound = new RectF();
//        mBorderBound.left = (mBmpBound.left + mBmpBound.right - DEFAULT_BORDER_RECT_WIDTH) / 2;
//        mBorderBound.top = (mBmpBound.top + mBmpBound.bottom - DEFAULT_BORDER_RECT_HEIGHT) / 2;
//        mBorderBound.right = mBorderBound.left + DEFAULT_BORDER_RECT_WIDTH;
//        mBorderBound.bottom = mBorderBound.top + DEFAULT_BORDER_RECT_HEIGHT;
        
        
        mBorderBound = new RectF();
        mBorderBound.left = (mBmpBound.left + mBmpBound.right - DEFAULT_BORDER_RECT_WIDTH) / 2;
        float top = (mBmpBound.top + mBmpBound.bottom - DEFAULT_BORDER_RECT_HEIGHT);
        mBorderBound.top=top<=0?1:top;
        mBorderBound.right = mBorderBound.left + DEFAULT_BORDER_RECT_WIDTH;
        mBorderBound.bottom = mBorderBound.top + DEFAULT_BORDER_RECT_HEIGHT;

        getBorderEdgeLength();
        invalidate();
    }

    private void drawBackground(Canvas canvas) {

        /*-
          -------------------------------------
          |                top                |
          -------------------------------------
          |      |                    |       |<——————————mBmpBound
          |      |                    |       |
          | left |                    | right |
          |      |                    |       |
          |      |                  <─┼───────┼────mBorderBound
          -------------------------------------
          |              bottom               |
          -------------------------------------
         */

        // Draw "top", "bottom", "left", then "right" quadrants.
        // because the border line width is larger than 1f, in order to draw a complete border rect ,
        // i have to change zhe rect coordinate to draw
        float delta = BORDER_LINE_WIDTH / 2;
        float left = mBorderBound.left - delta;
        float top = mBorderBound.top - delta;
        float right = mBorderBound.right + delta;
        float bottom = mBorderBound.bottom + delta;

        // -------------------------------------------------------------------------------移动到上下两端会多出来阴影
        canvas.drawRect(mBmpBound.left, mBmpBound.top, mBmpBound.right, top, mBgPaint);
        canvas.drawRect(mBmpBound.left, bottom, mBmpBound.right, mBmpBound.bottom, mBgPaint);
        canvas.drawRect(mBmpBound.left, top, left, bottom, mBgPaint);
        canvas.drawRect(right, top, mBmpBound.right, bottom, mBgPaint);
    }

    // 画裁剪区域中间的参考线
    private void drawGuidlines(Canvas canvas) {
        // Draw vertical guidelines.
        final float oneThirdCropWidth = mBorderBound.width() / 3;

        final float x1 = mBorderBound.left + oneThirdCropWidth;
        canvas.drawLine(x1, mBorderBound.top, x1, mBorderBound.bottom, mGuidelinePaint);
        final float x2 = mBorderBound.right - oneThirdCropWidth;
        canvas.drawLine(x2, mBorderBound.top, x2, mBorderBound.bottom, mGuidelinePaint);

        // Draw horizontal guidelines.
        final float oneThirdCropHeight = mBorderBound.height() / 3;

        final float y1 = mBorderBound.top + oneThirdCropHeight;
        canvas.drawLine(mBorderBound.left, y1, mBorderBound.right, y1, mGuidelinePaint);
        final float y2 = mBorderBound.bottom - oneThirdCropHeight;
        canvas.drawLine(mBorderBound.left, y2, mBorderBound.right, y2, mGuidelinePaint);
    }

    private void onActionDown(float x, float y) {

    }

    private void onActionMove(float x, float y) {
        float deltaX = x - mLastPoint.x;
        float deltaY = y - mLastPoint.y;
        // 这里先不考虑裁剪框放最大的情况
        switch (touchPos) {
        case POS_CENTER:
            mBorderBound.left += deltaX;
            // fix border position
            if (mBorderBound.left < mBmpBound.left)
                mBorderBound.left = mBmpBound.left;
            if (mBorderBound.left > mBmpBound.right - mBorderWidth)
                mBorderBound.left = mBmpBound.right - mBorderWidth;

            mBorderBound.top += deltaY;
            if (mBorderBound.top < mBmpBound.top)
                mBorderBound.top = mBmpBound.top;

            if (mBorderBound.top > mBmpBound.bottom - mBorderHeight)
                mBorderBound.top = mBmpBound.bottom - mBorderHeight;

            mBorderBound.right = mBorderBound.left + mBorderWidth;
            mBorderBound.bottom = mBorderBound.top + mBorderHeight;

            break;

        case POS_TOP:
            resetTop(deltaY);
            break;
        case POS_BOTTOM:
            resetBottom(deltaY);
            break;
        case POS_LEFT:
            resetLeft(deltaX);
            break;
        case POS_RIGHT:
            resetRight(deltaX);
            break;
        case POS_TOP_LEFT:
            resetTop(deltaY);
            resetLeft(deltaX);
            break;
        case POS_TOP_RIGHT:
            resetTop(deltaY);
            resetRight(deltaX);
            break;
        case POS_BOTTOM_LEFT:
            resetBottom(deltaY);
            resetLeft(deltaX);
            break;
        case POS_BOTTOM_RIGHT:
            resetBottom(deltaY);
            resetRight(deltaX);
            break;
        default:

            break;
        }
        invalidate();
    }

    private void onActionUp(float x, float y) {

    }

    private int detectTouchPosition(float x, float y) {
        if (x > mBorderBound.left + TOUCH_FIELD && x < mBorderBound.right - TOUCH_FIELD
                && y > mBorderBound.top + TOUCH_FIELD && y < mBorderBound.bottom - TOUCH_FIELD)
            return POS_CENTER;

        if (x > mBorderBound.left + BORDER_CORNER_LENGTH && x < mBorderBound.right - BORDER_CORNER_LENGTH) {
            if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + TOUCH_FIELD)
                return POS_TOP;
            if (y > mBorderBound.bottom - TOUCH_FIELD && y < mBorderBound.bottom + TOUCH_FIELD)
                return POS_BOTTOM;
        }

        if (y > mBorderBound.top + BORDER_CORNER_LENGTH && y < mBorderBound.bottom - BORDER_CORNER_LENGTH) {
            if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + TOUCH_FIELD)
                return POS_LEFT;
            if (x > mBorderBound.right - TOUCH_FIELD && x < mBorderBound.right + TOUCH_FIELD)
                return POS_RIGHT;
        }

        // 前面的逻辑已经排除掉了几种情况 所以后面的 ┏ ┓ ┗ ┛ 边角就按照所占区域的方形来判断就可以了
        if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + BORDER_CORNER_LENGTH) {
            if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)
                return POS_TOP_LEFT;
            if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)
                return POS_BOTTOM_LEFT;
        }

        if (x > mBorderBound.right - BORDER_CORNER_LENGTH && x < mBorderBound.right + TOUCH_FIELD) {
            if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)
                return POS_TOP_RIGHT;
            if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)
                return POS_BOTTOM_RIGHT;
        }

        return -1;
    }

    private void setLastPosition(MotionEvent event) {
        mLastPoint.x = event.getX();
        mLastPoint.y = event.getY();
    }

    private void getBorderEdgeLength() {
        mBorderWidth = mBorderBound.width();
        mBorderHeight = mBorderBound.height();
    }

    private void getBorderEdgeWidth() {
        mBorderWidth = mBorderBound.width();
    }

    private void getBorderEdgeHeight() {
        mBorderHeight = mBorderBound.height();
    }

    private void resetLeft(float delta) {
        mBorderBound.left += delta;

        getBorderEdgeWidth();
        fixBorderLeft();
    }

    private void resetTop(float delta) {
        mBorderBound.top += delta;
        getBorderEdgeHeight();
        fixBorderTop();
    }

    private void resetRight(float delta) {
        mBorderBound.right += delta;

        getBorderEdgeWidth();
        fixBorderRight();

    }

    private void resetBottom(float delta) {
        mBorderBound.bottom += delta;

        getBorderEdgeHeight();
        fixBorderBottom();
    }

    private void fixBorderLeft() {
        // fix left
        if (mBorderBound.left < mBmpBound.left)
            mBorderBound.left = mBmpBound.left;
        if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.left = mBorderBound.right - 2 * BORDER_CORNER_LENGTH;
    }

    private void fixBorderTop() {
        // fix top
        if (mBorderBound.top < mBmpBound.top)
            mBorderBound.top = mBmpBound.top;
        if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.top = mBorderBound.bottom - 2 * BORDER_CORNER_LENGTH;
    }

    private void fixBorderRight() {
        // fix right
        if (mBorderBound.right > mBmpBound.right)
            mBorderBound.right = mBmpBound.right;
        if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.right = mBorderBound.left + 2 * BORDER_CORNER_LENGTH;
    }

    private void fixBorderBottom() {
        // fix bottom
        if (mBorderBound.bottom > mBmpBound.bottom)
            mBorderBound.bottom = mBmpBound.bottom;
        if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)
            mBorderBound.bottom = mBorderBound.top + 2 * BORDER_CORNER_LENGTH;
    }
}


2  Activty

 

 

package com.minzh.onemedia.maintain;

import java.io.File;
import java.io.FileOutputStream;

import com.minzh.onemedia.R;
import com.minzh.onemedia.widget.MyCropView;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ScrollView;

public class CropImgActivity extends Activity {

    private MyCropView myCropView;
    private Button btnCrop;
    private Button btnCancel;
    private ScrollView sv;

    // 假设从图片选择器传递来的图片路径如下
    private String CROP_IMAGE_PATH = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mycrop);

        myCropView = (MyCropView) findViewById(R.id.myCropView);
        btnCrop = (Button) findViewById(R.id.btn_crop);
        
        
        btnCancel = (Button) findViewById(R.id.btn_cancel);
        sv = (ScrollView) findViewById(R.id.scrollview);

        CROP_IMAGE_PATH=getIntent().getStringExtra("fileImgPath");
        
        myCropView.setBmpPath(CROP_IMAGE_PATH);
        btnCrop.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				 Bitmap croppedImage = myCropView.getCroppedImage();
		            File f = new File(CROP_IMAGE_PATH);
		            if (f.exists()) {
		             f.delete();
		            }
		            try {
		             FileOutputStream out = new FileOutputStream(f);
		             croppedImage.compress(Bitmap.CompressFormat.JPEG, 90, out);
		             out.flush();
		             out.close();
		            } catch (Exception e) {
		              // TODO Auto-generated catch block
		              e.printStackTrace();
		            }
		            setResult(RESULT_OK);
		            finish();
			}
		});
        btnCancel.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
	            finish();
			}
		});

        sv.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                myCropView.getParent().requestDisallowInterceptTouchEvent(false);
                return false;
            }
        });
        
    }
    int REQUEST_CODE_IMGZOOM=1001;
   

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
//        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}

 

 

3 布局文件

 

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/scrollview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="0dp"
        android:layout_marginRight="0dp" >
        
        <com.minzh.onemedia.widget.MyCropView
            android:id="@+id/myCropView"
    android:layout_width="match_parent"
    android:layout_height="600dp"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"/>

    </LinearLayout>
    
     <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:layout_alignParentBottom="true" >

         <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
            android:layout_centerInParent="true" >
       

        <Button
            android:id="@+id/btn_cancel"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="取消" />
        
         <Button
            android:id="@+id/btn_crop"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="40dp"
            android:text="确定" />
        
        </LinearLayout>
        
     </RelativeLayout>
	</RelativeLayout>

</ScrollView>

 

 

 

 

 

4 效果图

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android自定义图片裁剪可以通过使用自定义View和Bitmap对象来实现。以下是一些步骤: 1. 创建自定义View类,继承自View类。在onDraw()方法中绘制Bitmap对象。 2. 设置触摸事件,获取用户手指操作的坐标,计算出裁剪区域的左上角和右下角坐标。 3. 在onTouchEvent()方法中重绘View,绘制裁剪后的Bitmap对象。 4. 使用Matrix对象进行缩放和旋转操作。 下面是一个简单的示例代码: ``` public class CropImageView extends View { private Bitmap mBitmap; private Paint mPaint; private Rect mRect; private Matrix mMatrix; private float mStartX; private float mStartY; private float mEndX; private float mEndY; public CropImageView(Context context) { super(context); init(); } public CropImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); mPaint.setColor(Color.RED); mRect = new Rect(); mMatrix = new Matrix(); } public void setImageBitmap(Bitmap bitmap) { mBitmap = bitmap; invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mBitmap != null) { canvas.drawBitmap(mBitmap, mMatrix, null); } canvas.drawRect(mRect, mPaint); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mStartX = event.getX(); mStartY = event.getY(); mEndX = mStartX; mEndY = mStartY; break; case MotionEvent.ACTION_MOVE: mEndX = event.getX(); mEndY = event.getY(); break; case MotionEvent.ACTION_UP: mEndX = event.getX(); mEndY = event.getY(); break; } calculateRect(); invalidate(); return true; } private void calculateRect() { float left = Math.min(mStartX, mEndX); float top = Math.min(mStartY, mEndY); float right = Math.max(mStartX, mEndX); float bottom = Math.max(mStartY, mEndY); mRect.set((int) left, (int) top, (int) right, (int) bottom); float centerX = (left + right) / 2; float centerY = (top + bottom) / 2; mMatrix.reset(); mMatrix.postTranslate(-centerX, -centerY); mMatrix.postRotate(45); mMatrix.postScale(0.5f, 0.5f); mMatrix.postTranslate(centerX, centerY); } } ``` 这个示例代码实现了一个简单的图片裁剪功能,并在裁剪后对图片进行了缩放和旋转。你可以根据自己的需求修改代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值