Android之签字板

news2024/9/28 1:16:44

文章目录

  • 前言
  • 一、效果图
  • 二、实现步骤
    • 1.GestureSignatureView类
    • 2.xml布局
    • 3.Activity类(kotlin)
    • 4.Activity类(Java)
    • 5.动态申请权限(kotlin)
    • 6.动态申请权限(Java)
  • 总结


前言

随着公司发展需求,很多金融APP都会涉及到需要用户签字的环节,所以在此贴出代码以供参考少踩坑。

一、效果图

在这里插入图片描述

二、实现步骤

1.GestureSignatureView类

package com.example.kotlinbasedome.activity.utils;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Author : CaoLiulang
 * @Time : 2023/7/7 17:02
 * @Description :自定义签字板
 */
public class GestureSignatureView extends View {

    private static final String TAG = "GestureSignatureView";
    private Path mPath;//绘制路径
    private Paint mPaint;// 绘制画笔
    private Canvas mCanvas;//背景画布
    private Bitmap mMBitmap;//背景bitmap
    private boolean isTouchedSignature = false;//是否签名 默认为false

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

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

    public GestureSignatureView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "onMeasure: 测量的宽高:" + getMeasuredWidth() + "-----------" + getMeasuredHeight());
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mMBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mMBitmap);
        mCanvas.drawColor(Color.TRANSPARENT);
        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
    }

    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(10.0f);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setDither(true);
        mPath = new Path();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
        canvas.drawBitmap(mMBitmap, 0, 0, mPaint);
        // 通过画布绘制多点形成的图形
        canvas.drawPath(mPath, mPaint);
    }

    private float[] downPoint = new float[2];

    private float[] previousPoint = new float[2];

    /**
     * 监听触摸事件的回调
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //获取距离自身(点击位置)左边界的距离
        downPoint[0] = event.getX();
        //获取距离自身(点击位置)上边界的距离
        downPoint[1] = event.getY();
        switch (event.getAction()) {
            //手势开始
            case MotionEvent.ACTION_DOWN:
                previousPoint[0] = downPoint[0];
                previousPoint[1] = downPoint[1];
                // moveTo 不会进行绘制,只用于移动移动画笔。
                mPath.moveTo(downPoint[0], downPoint[1]);
                break;
            //手势过程
            case MotionEvent.ACTION_MOVE:
                float dX = Math.abs(downPoint[0] - previousPoint[0]);
                float dY = Math.abs(downPoint[1] - previousPoint[1]);

                // 两点之间的距离大于等于3时,生成贝塞尔绘制曲线
                if (dX >= 3 || dY >= 3) {
                    // 设置贝塞尔曲线的操作点为起点和终点的一半
                    float cX = (downPoint[0] + previousPoint[0]) / 2;
                    float cY = (downPoint[1] + previousPoint[1]) / 2;
                    //quadTo 用于绘制圆滑曲线,即贝塞尔曲线 二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点
                    mPath.quadTo(previousPoint[0], previousPoint[1], cX, cY);
                    // 第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值
                    previousPoint[0] = downPoint[0];
                    previousPoint[1] = downPoint[1];
                }
                break;
            //手势结束
            case MotionEvent.ACTION_UP:
                //设置签名成功状态
                isTouchedSignature = true;
                mCanvas.drawPath(mPath, mPaint);
                mPath.reset();
                break;
        }

        invalidate();
        return true;
    }

    // 缩放
    public static Bitmap resizeImage(Bitmap bitmap, int width, int height) {
        int originWidth = bitmap.getWidth();
        int originHeight = bitmap.getHeight();

        float scaleWidth = ((float) width) / originWidth;
        float scaleHeight = ((float) height) / originHeight;

        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, originWidth,
                originHeight, matrix, true);
        return resizedBitmap;
    }

    public Bitmap getPaintBitmap() {
        return resizeImage(mMBitmap, 320, 480);
    }

    public void clear() {
        if (mCanvas != null) {
            isTouchedSignature = false;
            mPath.reset();
            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            invalidate();
        }
    }

    /**
     * 保存画板
     *
     * @param path 保存到路径
     */

    public void save(String path) {
        try {
            save(path, true, 50);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Bitmap getBitmap() {
        return mMBitmap;
    }

    /**
     * 保存画板
     *
     * @param path       保存到路径
     * @param clearBlank 是否清除空白区域
     * @param blank      边缘空白区域
     */
    public void save(String path, boolean clearBlank, int blank) throws IOException {

        Bitmap bitmap = mMBitmap;
        if (clearBlank) {
            bitmap = clearBlank(mMBitmap, blank);
        }
        Bitmap littleBmp = ConstantsUtil.smallImage(bitmap, 700);
        Bitmap newBitmap = Bitmap.createBitmap(littleBmp.getWidth(), littleBmp.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(newBitmap);
        canvas.drawColor(Color.WHITE);
        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
        canvas.drawBitmap(littleBmp, 0, 0, null);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        newBitmap.compress(Bitmap.CompressFormat.JPEG, 50, bos);
        byte[] buffer = bos.toByteArray();
        if (buffer != null) {
            File file = new File(path);

            OutputStream outputStream = new FileOutputStream(file);
            outputStream.write(buffer);
            outputStream.close();
            scanMediaFile(file);
        }
    }

    /**
     * 是否有签名
     *
     * @return
     */
    public boolean getTouched() {
        return isTouchedSignature;
    }

    /**
     * 逐行扫描 清除边界空白。
     *
     * @param bp
     * @param blank 边距留多少个像素
     * @return
     */
    private Bitmap clearBlank(Bitmap bp, int blank) {
        int HEIGHT = bp.getHeight();
        int WIDTH = bp.getWidth();
        int top = 0, left = 0, right = 0, bottom = 0;
        int[] pixs = new int[WIDTH];
        boolean isStop;
        for (int y = 0; y < HEIGHT; y++) {
            bp.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);
            isStop = false;
            for (int pix : pixs) {
                if (pix != Color.TRANSPARENT) {
                    top = y;
                    isStop = true;
                    break;
                }
            }
            if (isStop) {
                break;
            }
        }
        for (int y = HEIGHT - 1; y >= 0; y--) {
            bp.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);
            isStop = false;
            for (int pix : pixs) {
                if (pix != Color.TRANSPARENT) {
                    bottom = y;
                    isStop = true;
                    break;
                }
            }
            if (isStop) {
                break;
            }
        }
        pixs = new int[HEIGHT];
        for (int x = 0; x < WIDTH; x++) {
            bp.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);
            isStop = false;
            for (int pix : pixs) {
                if (pix != Color.TRANSPARENT) {
                    left = x;
                    isStop = true;
                    break;
                }
            }
            if (isStop) {
                break;
            }
        }
        for (int x = WIDTH - 1; x > 0; x--) {
            bp.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);
            isStop = false;
            for (int pix : pixs) {
                if (pix != Color.TRANSPARENT) {
                    right = x;
                    isStop = true;
                    break;
                }
            }
            if (isStop) {
                break;
            }
        }
        if (blank < 0) {
            blank = 0;
        }
        left = left - blank > 0 ? left - blank : 0;
        top = top - blank > 0 ? top - blank : 0;
        right = right + blank > WIDTH - 1 ? WIDTH - 1 : right + blank;
        bottom = bottom + blank > HEIGHT - 1 ? HEIGHT - 1 : bottom + blank;
        return Bitmap.createBitmap(bp, left, top, right - left, bottom - top);
    }

    private void scanMediaFile(File photo) {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri contentUri = Uri.fromFile(photo);
        mediaScanIntent.setData(contentUri);
        getContext().sendBroadcast(mediaScanIntent);
    }
}



2.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="28dp"
        android:src="#DEDFE2" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2">

        <com.example.kotlinbasedome.activity.utils.GestureSignatureView
            android:id="@+id/signSave_gsv_signature"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white" />


    </LinearLayout>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:src="#DEDFE2" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="20dp"
            android:text="Preview:"
            android:textSize="20sp"
            android:typeface="serif">

        </TextView>

        <ImageView
            android:id="@+id/singImg"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_gravity="center"
            android:layout_marginLeft="20dp">

        </ImageView>


    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp">

        <TextView
            android:id="@+id/signSave_tv_cancel"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_weight="1"
            android:background="#CDCDCD"
            android:clickable="true"
            android:gravity="center"
            android:onClick="onClick"
            android:text="EXIT"
            android:textColor="@color/white"
            android:textSize="16sp"
            android:typeface="serif" />

        <TextView
            android:id="@+id/signSave_tv_clear"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_marginLeft="10dp"
            android:layout_weight="1"
            android:background="#acacac"
            android:clickable="true"
            android:gravity="center"
            android:onClick="onClick"
            android:text="CLEAN"
            android:textColor="@color/white"
            android:textSize="16sp"
            android:typeface="serif" />

        <TextView
            android:id="@+id/signSave_tv_save"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_marginLeft="10dp"
            android:layout_weight="1"
            android:background="#1d9eec"
            android:clickable="true"
            android:gravity="center"
            android:onClick="onClick"
            android:text="CONFIRM"
            android:textColor="@color/white"
            android:textSize="16sp"
            android:typeface="serif" />
    </LinearLayout>

</LinearLayout>

3.Activity类(kotlin)

/**
 * @Author : CaoLiulang
 * @Time : 2023/7/7 17:02
 * @Description :签名
 */
class Signature : Activity(), OnClickListener {

    private lateinit var signSave_gsv_signature: GestureSignatureView
    private lateinit var singImg: ImageView
    private lateinit var signSave_tv_save: TextView
    private lateinit var signSave_tv_clear: TextView
    private lateinit var signSave_tv_cancel: TextView
    private lateinit var message: String
    private var imagurl: String = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //去掉状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val decorView = window.decorView
            val option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            decorView.systemUiVisibility = option
            window.statusBarColor = Color.parseColor("#00000000")
        }
        //修改状态栏文字为黑色
        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        setContentView(R.layout.signature)
        instantiation()
    }

    fun instantiation() {
        signSave_gsv_signature = findViewById(R.id.signSave_gsv_signature)
        singImg = findViewById(R.id.singImg)
        signSave_tv_save = findViewById(R.id.signSave_tv_save)
        signSave_tv_clear = findViewById(R.id.signSave_tv_clear)
        signSave_tv_cancel = findViewById(R.id.signSave_tv_cancel)
        signSave_tv_save.setOnClickListener(this)
        signSave_tv_clear.setOnClickListener(this)
        signSave_tv_cancel.setOnClickListener(this)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onClick(v: View?) {
        when (v?.id) {
            //保存
            R.id.signSave_tv_save ->
                //防止多次触发
                if (ButtonUtils.isFastDoubleClick(R.id.signSave_tv_save) === false) {
                    if (imagurl == "") {
                        if (!signSave_gsv_signature.touched) {
                            ToastUtilsKT.showToast1("Please sign first")
                            return
                        }
                        val file = File(ConstantsUtil.IMG_FOLDER_PATH)
                        file.mkdirs()
                        val fillPath: String =
                            ConstantsUtil.IMG_FOLDER_PATH + "signImg" + System.currentTimeMillis() + ".jpg"
                        Log.i("w--", fillPath)
                        signSave_gsv_signature.save(fillPath)
                        println("图片路径打印:$fillPath")
                        Glide.with(this)
                            .load(fillPath)
                            .into(singImg)
                        imagurl = fillPath
                       //网络请求
                    } else {
                        //网络请求
                    }
                }
            //清空
            R.id.signSave_tv_clear -> {
                signSave_gsv_signature.clear()
                singImg.setImageDrawable(null)
                imagurl = ""
            }
            //返回
            R.id.signSave_tv_cancel ->
                finish()
        }
    }

4.Activity类(Java)

public class Signature extends Activity implements View.OnClickListener {

    private TextView signSave_tv_cancel;//退出
    private TextView signSave_tv_clear;//清除
    private TextView signSave_tv_save;//保存
    private GestureSignatureView signSave_gsv_signature;//签字板
    private ImageView singImg;//签名图片展示
    private String message;//返回消息
    private String imagurl = "";//图片路径

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去掉状态栏
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setStatusBarColor(Color.parseColor("#00000000"));
        }
        //修改状态栏文字为黑色
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                        View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        setContentView(R.layout.signature);
        instantiation();
    }

    //实例化
    private void instantiation() {
        signSave_tv_cancel = findViewById(R.id.signSave_tv_cancel);
        signSave_tv_clear = findViewById(R.id.signSave_tv_clear);
        signSave_tv_save = findViewById(R.id.signSave_tv_save);
        signSave_gsv_signature = findViewById(R.id.signSave_gsv_signature);
        singImg = findViewById(R.id.singImg);
        signSave_tv_save.setOnClickListener(this);
        signSave_tv_clear.setOnClickListener(this);
        signSave_tv_cancel.setOnClickListener(this);
    }

    @SuppressLint("NewApi")
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            //退出
            case R.id.signSave_tv_cancel:
                finish();
                break;
            //清除
            case R.id.signSave_tv_clear:
                signSave_gsv_signature.clear();
                singImg.setImageDrawable(null);
                imagurl = "";
                break;
            //保存
            case R.id.signSave_tv_save:
                //防止多次触发
                if (ButtonUtils.isFastDoubleClick(R.id.signSave_tv_save) == false) {
                    if (imagurl.equals("")) {
                        if (!signSave_gsv_signature.getTouched()) {
                            ToastUtils.ToastCllShow("您尚未签字");
                            return;
                        }
                        File file = new File(ConstantsUtil.IMG_FOLDER_PATH);
                        file.mkdirs();
                        String fillPath = ConstantsUtil.IMG_FOLDER_PATH + "signImg" + System.currentTimeMillis() + ".jpg";
                        Log.i("w--", fillPath);
                        signSave_gsv_signature.save(fillPath);
                        System.out.println("图片路径打印:" + fillPath);
                        Glide.with(this)
                                .load(fillPath)
                                .into(singImg);
                        imagurl = fillPath;
                        //网络请求
                    } else {
                        //网络请求
                    }
                }
                break;
        }
    }

5.动态申请权限(kotlin)

                   //6.0才用动态权限
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        //读写权限
                        if (ContextCompat.checkSelfPermission(
                                this@PDFWebViewActivity,
                                Manifest.permission.WRITE_EXTERNAL_STORAGE
                            )
                            != PackageManager.PERMISSION_GRANTED
                        ) {
                            ActivityCompat.requestPermissions(
                                this@PDFWebViewActivity,
                                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                                1
                            )
                        } else {//有权限
                            startActivity(Intent(this@PDFWebViewActivity,Signature::class.java))
                        }
                    }

6.动态申请权限(Java)

    //6.0才用动态权限
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        //读写权限
                        if (ContextCompat.checkSelfPermission(PDFWebViewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                                != PackageManager.PERMISSION_GRANTED) {
                            ActivityCompat.requestPermissions(PDFWebViewActivity.this,
                                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                        } else {
                            startActivity(new Intent(PDFWebViewActivity.this, Signature.class));
                        }
                    }

总结

以上便是签字板所有代码了,kotlin和Java我都分别贴了上去,注册activity的时候需要设置横屏显示即可,竖屏也行,看需求,然后保持图片需要用到权限这个一定要记得,欢迎讨论指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/738050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考高级架构师笔记-9系统架构

目录 1. 前文回顾 & 考情分析2. 软件架构概述3. 软件架构风格3.1 层次架构风格3.2 面向服务架构风格4. 软件架构复用5. 特定领域软件体系结构DSSA6. ABSD7. 质量属性8. 架构评估9 结语1. 前文回顾 & 考情分析 前文回顾: 软考高级架构师笔记-1计算机硬件软考高级架构师…

TCP 协议(三)十种核心机制

1.确认应答&#xff08;可靠机制&#xff09; 2.超时重传&#xff08;可靠机制&#xff09; 3.连接管理&#xff08;可靠机制&#xff09; 4.滑动窗口&#xff08;效率机制&#xff09; 5.流量控制&#xff08;效率机制&#xff09; 6.拥塞控制&#xff08;效率机制&#xff09…

优维低代码实践:权限设置

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

js两种对象混合写,返回的是哪一个

<script>function jiafa() {this.name "xuhaitao";this.age 36;var obj {};obj.xx "hunkxu";obj.yy "88";return obj;}var aa new jiafa();console.log(aa);</script> 打印&#xff1a; FR&#xff1a;徐海涛(hunk xu)

3D引擎龙头Unity:元宇宙和AI活跃玩家

Unity是用于创建和操作交互式实时3D内容的世界领先平台。凭借灵活的编辑器、友好的开发环境、丰富的工具套件&#xff0c;Unity吸引了大量开发者&#xff0c;全球排名前1000的移动游戏70%以上使用了Unity的创作和运营解决方案&#xff0c;如今&#xff0c;Unity引擎在工业场景、…

leaflet地图移动防抖问题

现在有这么一个需求&#xff0c;当移动地图时&#xff0c;需要获取当前地图范围属于那个城市。如果频繁移动地图&#xff0c;会不停的调用接口获取当前地图视图所属城市&#xff0c;所以加个防抖&#xff0c;减少请求。代码示例&#xff1a;<!DOCTYPE html> <html>…

【Leetcode】36. 有效的数独

一、题目 1、题目描述 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 注意:…

微信小程序生态15- 批量提交微信小程序审核的一种方式

文章导航 微信小程序生态1-初识小程序 微信小程序生态2-创建一个微信小程序 微信小程序生态3-微信小程序登录流程设计 微信小程序生态4-扫普通二维码进入小程序、打开短链接进入小程序 微信小程序生态5-微信公众号扫码登录PC端网页 微信小程序生态6-微信公众号授权登录(适用于…

字节跳动(抖音),软件测试四面,面试题总结!走过路过不要错过

面试一 1、 简单做一下自我介绍 2、 简要介绍一下项目/你负责的模块/选一个模块说一下你设计的用例 3 、get请求和post请求的区别 4、 如何判断前后端bug/3xx是什么意思 5、 说一下XXX项目中你做的接口测试/做了多少次 6、 http和https的区别 7、 考了几个ADB命令/查看连…

JAVA_WEB 购物商城(WEB端)

仓库地址&#xff1a;https://gitee.com/ThMyGitee/EeasyEeasyCityFrontEnd.git CSDN的友友们&#xff0c;项目如果适合您的话&#xff0c;麻烦给个小小的Star&#xff0c;谢谢啦&#xff01; JAVA_WEB 购物商城(WEB端) 效果图: 技术选型: 后端技术栈 Jsp Servlet &#x…

【力扣算法01】之最接近的三数之和

文章目录 前言问题描述示例 1示例 2提示 解题思路代码分析完整代码运行效果如图执行示例代码1执行示例代码2 完结 前言 近期已经将python 的大部分内容讲完了, 接下来的一段时间会着重于算法和面试题相关的内容, 确保学有所用, 同时也为准备进入大厂的童靴们做个铺垫, 记得关注…

抖音seo源码-源代码开发搭建-开源部署操作日志

一、开发者前言大环境概述 抖音seo源码开发是一项非常重要的技术&#xff0c;在抖音上&#xff0c;有很多视频&#xff0c;如果你想让自己的视频脱颖而出&#xff0c;那么就需要优化自己的seo源码。不过&#xff0c;为了保护用户的隐私&#xff0c;抖音并不公开其seo算法的细节…

Alluxio入门手册

1. 定义 Alluxio&#xff08;之前名为 Tachyon&#xff09;是世界上第一个以内存为中心的虚拟的分布式存储系统。 它统一了数据访问的方式&#xff0c;为上层计算框架和底层存储系统构建了桥梁。应用只需要连接Alluxio即可访问存储在底层任意存储系统中的数据。此外&#xff0c…

太难了,别再为难我们这些测试工程师了好吗?

前言 有不少技术友在测试群里讨论&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了,考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些测试工程师了。 这不&#xff0c;为了帮大家节约时…

从零开发短视频电商 Jmeter基础介绍和使用心得

文章目录 基本Jmeter主要元件元件执行顺序提取器Json提取器边界值提取器 高级动态参数参数化前后关联设置Jmeter语言为中文环境非GUI模式压测分布式压测监控模板 基本 Jmeter主要元件 1、测试计划&#xff1a;是使用 JMeter 进行测试的起点&#xff0c;它是其它 JMeter测试元…

一、Zabbix介绍及6.0部署

Zabbix 一、Zabbix介绍1、zabbix是什么&#xff1f;2、zabbix的监控原理是什么&#xff1f;3、Zabbix 6.0新特性:4、Zabbix 6.0 功能组件&#xff1a; 二、Zabbix 6.0 部署1、部署 zabbix 服务端2、部署数据库3、编译安装 zabbix Server 服务端4、部署 Web 前端&#xff0c;进行…

Jmeter接口关联(二)【使用jsonpath表达式提取值】与python中使用jsonpath提取值 完成接口关联

文章目录 前言一、jmeter中使用jsonpath的表达式来提取值 1、使用jsonpath表达式匹配date的所有数据2、使用json提取器提取想要拿取的date值二、在Python中使用Jsonpath来完成接口的关联 ​​​​​​​1、jsonpath使用讲解 2、jsonpath在接口关联中使用总结 前言 Jmeter中关…

NFT + JAVA + 可更新合约

前言 合约部署参考&#xff1a;hardhat 合约开发参考&#xff1a;openzeppelin 钱包&#xff1a;metamask 部署链使用的是&#xff1a;sepolia 合约生成Java&#xff1a;solc 合约部署和测试自行处理 1. 合约 合约代码&#xff1a;ZhuZiNFTU.sol // SPDX-License-Iden…

STM32 Proteus UCOSII系统微波炉控制系统DS18B20-0057

STM32 Proteus UCOSII系统微波炉控制系统DS18B20-0057 Proteus仿真小实验&#xff1a; STM32 Proteus UCOSII系统微波炉控制系统DS18B20-0057 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD1602显示器多个按键蜂鸣器DS18B20温度传感器3个LED灯表示档位加热指…

[SDOI2008] 仪仗队 题解

注&#xff1a;在洛谷同时发布。 题目链接 解题思路 首先观察样例。似乎什么也观察不出来&#xff1f; 那就根据题目描述中所给的图表做。首先找到对角线&#xff0c;将图形沿着对角线一分为二。注意对角线上可以看到一个人&#xff0c;所以答案要加 1 1 1。 其次逐个分析…