精华内容
下载资源
问答
  • Android二维码扫码集成:GitHub地址 1.GitHub上面下载会很慢,可以点我上传的zxing模块来来下载,需要1点积分。 2.下载下来之后解压,在你的项目中file → new → import module 将该文件夹导入项目,并将app依赖于...

    Android二维码扫码集成:GitHub地址

    1.GitHub上面下载会很慢,可以点我上传的zxing模块来来下载,需要1点积分。

    2.下载下来之后解压,在你的项目中file → new → import module 将该文件夹导入项目,并将app依赖于这个module(如下图),下面开始使用【开发工具:Android studio】


    3.扫码功能:你可以定义个按钮,点击按钮时调用下面的方法(checkCameraPermission())

    //检查是否开启了相机权限【Android 6.0之后必须添加】
        private void checkCameraPermission() {
            if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                //如果没有授权,则请求授权
                ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_CALL_CAMERA);
            } else {
                //有授权,直接开启摄像头扫描
                callCapture("UTF-8");//打开扫码签到
            }
        }
    
     private void callCapture(String characterSet) {
            /**
             * 获取屏幕的宽度,并将宽度的2/3作为扫码区宽度
             */
            int width = Tools.getScreenW(getContext()) * 2 / 3;
            Intent intent = new Intent();
            intent.setAction(Intents.Scan.ACTION);
            // intent.putExtra(Intents.Scan.MODE, Intents.Scan.QR_CODE_MODE);
            intent.putExtra(Intents.Scan.CHARACTER_SET, characterSet);
            /**
             * WIDTH==HEIGHT 设置方形扫码取景区
             * 取景区的总宽度是屏幕宽度的2/3——适配所有机型
             * */
            intent.putExtra(Intents.Scan.WIDTH, width);
            intent.putExtra(Intents.Scan.HEIGHT, width);//
            // intent.putExtra(Intents.Scan.PROMPT_MESSAGE, "type your prompt message");
            intent.setClass(getContext(), CaptureActivity.class);//进入zxing模块中的CaptureActivity
            startActivityForResult(intent, REQUEST_CODE);
        }
    
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            //获取扫描结果
            if (null != data && requestCode == REQUEST_CODE) {
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        LogD.d("扫描返回RESULT:\n" + data.getStringExtra(Intents.Scan.RESULT));
                        LogD.d("扫描返回RESULT_FORMAT:\n" + data.getStringExtra(Intents.Scan.RESULT_FORMAT));
                        LogD.d("扫描返回URI:\n" + data.toUri(data.getFlags()));
                        getResult(data.getStringExtra(Intents.Scan.RESULT));//获取扫码结果
                        break;
                    default:
                        break;
                }
            }
        }
    【如果你不需要设置取景区域的话,上面的代码,已够用。】

    4.设置取景区样式【主要调整zxing模块中的CaptureActivity,CameraManager,ViewfinderView】【下载我上传的zxinglib模块的话已经修改过了】

    第一步:调整扫码界面的样式——扫码界面的顶部添加返回按钮和标题

    布局代码如下:

    CaptureActivity布局代码

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <SurfaceView
                android:id="@+id/preview_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
            <com.google.zxing.client.android.ViewfinderView
                android:id="@+id/viewfinder_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
            <include layout="@layout/layout_capture_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </RelativeLayout>
    </FrameLayout>
    
    layout_capture_header.xml代码:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:background="@color/viewfinder_mask"
        >
        <ImageButton
            android:id="@+id/button_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/back" />
    
        <TextView
            android:id="@+id/textview_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center_vertical"
            android:text="二维码扫描"
            android:textColor="@android:color/white"
            android:textSize="18sp" />
    
    </RelativeLayout>
    第二步:添加返回事件(在CaptureActivity的onResume方法中添加以下代码段)【完成了扫码界面的调整】


    第三步:调整扫码取景区的相对位置【上面已经传了相对宽度和高度进入CaptureActivity,取景区已经按照这个设置去设置了】【在CameraManager中调整如下——实现扫码取景区位置在偏上】

    第四步:调整取景区的四个角,扫码线,取景区下发的提示文字【用以下代码段区替换ViewfinderView】(注释已经很清楚的说明了具体用法,有兴趣的自己去研究哈)

    public final class ViewfinderView extends View {
        private static final String TAG = "log";
        /**
         * 刷新界面的时间
         */
        private static final long ANIMATION_DELAY = 10L;
        private static final int OPAQUE = 0xFF;
    
        /**
         * 四个绿色边角对应的长度
         */
        private int ScreenRate;
    
        /**
         * 四个绿色边角对应的宽度
         */
        private static final int CORNER_WIDTH = 5;
        /**
         * 扫描框中的中间线的宽度
         */
        private static final int MIDDLE_LINE_WIDTH = 4;
    
        /**
         * 扫描框中的中间线的与扫描框左右的间隙
         */
        private static final int MIDDLE_LINE_PADDING = 6;
    
        /**
         * 中间那条线每次刷新移动的距离
         */
        private static final int SPEEN_DISTANCE = 5;
    
        /**
         * 手机的屏幕密度
         */
        private static float density;
        /**
         * 字体大小
         */
        private static final int TEXT_SIZE = 16;
        /**
         * 字体距离扫描框下面的距离
         */
        private static final int TEXT_PADDING_TOP = 60;
    
        /**
         * 画笔对象的引用
         */
        private Paint paint;
    
        /**
         * 中间滑动线的最顶端位置
         */
        private int slideTop;
    
        /**
         * 中间滑动线的最底端位置
         */
        private int slideBottom;
    
        private Bitmap resultBitmap;
        private final int maskColor;
        private final int resultColor;
    
        private final int resultPointColor;
        private Collection<ResultPoint> possibleResultPoints;
        private Collection<ResultPoint> lastPossibleResultPoints;
        private CameraManager cameraManager;
        private boolean isFirst;
    
        public ViewfinderView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            density = context.getResources().getDisplayMetrics().density;
            //将像素转换成dp
            ScreenRate = (int) (10 * density);
    
            paint = new Paint();
            Resources resources = getResources();
            maskColor = resources.getColor(R.color.viewfinder_mask);
            resultColor = resources.getColor(R.color.result_view);
    
            resultPointColor = resources.getColor(R.color.possible_result_points);
            possibleResultPoints = new HashSet<ResultPoint>(5);
        }
    
        public void setCameraManager(CameraManager cameraManager) {
            this.cameraManager = cameraManager;
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            if (cameraManager == null) return;
            //中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改
            Rect frame = cameraManager.getFramingRect();
            if (frame == null) {
                return;
            }
    
            //初始化中间线滑动的最上边和最下边
            if (!isFirst) {
                isFirst = true;
                slideTop = frame.top;
                slideBottom = frame.bottom;
            }
    
            //获取屏幕的宽和高
            int width = canvas.getWidth();
            int height = canvas.getHeight();
    
            paint.setColor(resultBitmap != null ? resultColor : maskColor);
    
            //画出扫描框外面的阴影部分,共四个部分,扫描框的上面到屏幕上面,扫描框的下面到屏幕下面
            //扫描框的左边面到屏幕左边,扫描框的右边到屏幕右边
            canvas.drawRect(0, 0, width, frame.top, paint);
            canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
            canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
                    paint);
            canvas.drawRect(0, frame.bottom + 1, width, height, paint);
    
    
            if (resultBitmap != null) {
                // Draw the opaque result bitmap over the scanning rectangle
                paint.setAlpha(OPAQUE);
                canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
            } else {
    
                //画扫描框边上的角,总共8个部分
                paint.setColor(Color.GREEN);
                canvas.drawRect(frame.left, frame.top, frame.left + ScreenRate,
                        frame.top + CORNER_WIDTH, paint);
                canvas.drawRect(frame.left, frame.top, frame.left + CORNER_WIDTH, frame.top
                        + ScreenRate, paint);
                canvas.drawRect(frame.right - ScreenRate, frame.top, frame.right,
                        frame.top + CORNER_WIDTH, paint);
                canvas.drawRect(frame.right - CORNER_WIDTH, frame.top, frame.right, frame.top
                        + ScreenRate, paint);
                canvas.drawRect(frame.left, frame.bottom - CORNER_WIDTH, frame.left
                        + ScreenRate, frame.bottom, paint);
                canvas.drawRect(frame.left, frame.bottom - ScreenRate,
                        frame.left + CORNER_WIDTH, frame.bottom, paint);
                canvas.drawRect(frame.right - ScreenRate, frame.bottom - CORNER_WIDTH,
                        frame.right, frame.bottom, paint);
                canvas.drawRect(frame.right - CORNER_WIDTH, frame.bottom - ScreenRate,
                        frame.right, frame.bottom, paint);
    
    
                //绘制中间的线,每次刷新界面,中间的线往下移动SPEEN_DISTANCE
                slideTop += SPEEN_DISTANCE;
                if (slideTop >= frame.bottom) {
                    slideTop = frame.top;
                }
    //            canvas.drawRect(frame.left + MIDDLE_LINE_PADDING, slideTop - MIDDLE_LINE_WIDTH / 2,
    //                    frame.right - MIDDLE_LINE_PADDING, slideTop + MIDDLE_LINE_WIDTH / 2, paint);
                Rect lineRect = new Rect();
                lineRect.left = frame.left;
                lineRect.right = frame.right;
                lineRect.top = slideTop;
                lineRect.bottom = slideTop + 18;
                canvas.drawBitmap(((BitmapDrawable) (getResources().getDrawable(R.drawable.qrcode_scan_line))).getBitmap(), null, lineRect, paint);
    
                //画扫描框下面的字()
                paint.setColor(Color.WHITE);
                paint.setTextSize(TEXT_SIZE * density);
                paint.setAlpha(0x40);
                paint.setTypeface(Typeface.DEFAULT_BOLD);
                String text = getResources().getString(R.string.scan_text);
                float textWidth = paint.measureText(text);
                canvas.drawText(text, (width - textWidth) / 2, (float) (frame.bottom + (float) TEXT_PADDING_TOP * density), paint);
    
                Collection<ResultPoint> currentPossible = possibleResultPoints;
                Collection<ResultPoint> currentLast = lastPossibleResultPoints;
                if (currentPossible.isEmpty()) {
                    lastPossibleResultPoints = null;
                } else {
                    possibleResultPoints = new HashSet<ResultPoint>(5);
                    lastPossibleResultPoints = currentPossible;
                    paint.setAlpha(OPAQUE);
                    paint.setColor(resultPointColor);
                    for (ResultPoint point : currentPossible) {
                        canvas.drawCircle(frame.left + point.getX(), frame.top
                                + point.getY(), 6.0f, paint);
                    }
                }
                if (currentLast != null) {
                    paint.setAlpha(OPAQUE / 2);
                    paint.setColor(resultPointColor);
                    for (ResultPoint point : currentLast) {
                        canvas.drawCircle(frame.left + point.getX(), frame.top
                                + point.getY(), 3.0f, paint);
                    }
                }
    
    
                //只刷新扫描框的内容,其他地方不刷新
                postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
                        frame.right, frame.bottom);
    
            }
        }
    
        public void drawViewfinder() {
            resultBitmap = null;
            invalidate();
        }
    
        /**
         * Draw a bitmap with the result points highlighted instead of the live
         * scanning display.
         *
         * @param barcode An image of the decoded barcode.
         */
        public void drawResultBitmap(Bitmap barcode) {
            resultBitmap = barcode;
            invalidate();
        }
    
        public void addPossibleResultPoint(ResultPoint point) {
            possibleResultPoints.add(point);
        }
    }

    效果图:





    展开全文
  • php+laravel 扫码二维码签到

    热门讨论 2021-05-13 16:07:00
    为满足公司签到业务场景 最终敲定使用微信二维码来实现 微信公众号相关配置 在微信公众平台登陆上去后,点开开发中的基本配置看到的基本信息 框架及拓展包 laravel overtrue/laravel-wechat 安装方式:composer ...

    简介

    为满足公司签到业务场景 最终敲定使用微信二维码来实现

    • 微信公众号相关配置
      在微信公众平台登陆上去后,点开开发中的基本配置看到的基本信息
      微信公众平台配置
    • 框架及拓展包
    laravel
    overtrue/laravel-wechat  
    安装方式:composer require "overtrue/laravel-wechat:^6.0"
    

    详细了解请看:laravel-wechat

    • 配置文件及对应信息
    config/wechat.php
    	/*
         * 公众号
         */
        'official_account' => [
            'default' => [
                'app_id'  => env('WECHAT_OFFICIAL_ACCOUNT_APPID', 'your-app-id'),         // AppID
                'secret'  => env('WECHAT_OFFICIAL_ACCOUNT_SECRET', 'your-app-secret'),    // AppSecret
                'token'   => env('WECHAT_OFFICIAL_ACCOUNT_TOKEN', 'your-token'),           // Token
                'aes_key' => env('WECHAT_OFFICIAL_ACCOUNT_AES_KEY', ''),                 // EncodingAESKey
    
            ],
        ],
    
    • 生成二维码
    <?php
    
    namespace App\Model\WeChat;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Qrcode extends Model
    {
    	private static $app;
    	public function __construct(){
    	    self::$app = app('wechat.official_account');
    	}
    	
        /**
         * @title 生成临时二维码
         * @param $action_info
         * @param float|int $expire_seconds
         * @return $result
         * @return $result[ticket]  获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。
         * @return $result[expire_seconds]  该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)
         * @return $result[url]  二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
         * @return $result[url1]  通过ticket换取二维码后地址
         */
    	public function temporary($action_info,$expire_seconds = 30*24*60*60){
    	    $result =  self::$app->qrcode->temporary($action_info, $expire_seconds);
    		$ticket = $result['ticket'];
    		$url = $this -> qrcode_url($ticket);
    		$result['url1'] = $url;
    		$result['action_info'] = $action_info;
    		return $result;
    	}
    
        /**
         * @title 生成永久二维码
         * @param $action_info
         * @return $result
         * @return $result[ticket] 获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码
         * @return $result[expire_seconds] 该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)
         * @return $result[url] 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
         * @return $result[url1] 通过ticket换取二维码后地址
         */
    	public function forever($action_info){
    	    $result =  self::$app->qrcode->forever($action_info);
    		$ticket = $result['ticket'];
    		$url = $this -> qrcode_url($ticket);
    		$result['url1'] = $url;
    		$result['action_info'] = $action_info;
    		return $result;
    	}
    
        /**
         * @title 获取二维码url
         * @param $ticket
         * @return $url 二维码url
         */
    	public function qrcode_url($ticket){
    		$url = self::$app->qrcode->url($ticket);
    		return $url;
    	}
    }
    
    
    • 实现一个简单的推送
    <?php
    
    namespace App\Http\Controllers;
    
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\Log;
    
    class WeChatController extends Controller
    {
    
        /**
         * 处理微信的请求消息
         *
         * @return string
         */
        public function serve()
        {
            Log::info('request arrived.'); # 注意:Log 为 Laravel 组件,所以它记的日志去 Laravel 日志看,而不是 EasyWeChat 日志
    
            $app = app('wechat.official_account');
            $app->server->push(function($message){
                return "hello everyone!";
            });
    
            return $app->server->serve();
        }
    }
    
    
    • 处理事件
    <?php
    
    namespace App\Http\Controllers;
    
    use App\Http\Controllers\Controller;
    use Illuminate\Support\Facades\Log;
    use Illuminate\Http\Request;
    use App\Model\SCAN;
    
    class WeChatController extends Controller
    {
    
        /**
         * 处理微信的请求消息
         *
         * @return string
         */
        public function serve()
        {
            Log::info('request arrived.'); # 注意:Log 为 Laravel 组件,所以它记的日志去 Laravel 日志看,而不是 EasyWeChat 日志
    
            $app = app('wechat.official_account');
            $app->server->push(function($message){
                case 'event':
                        switch ($message['Event']) {
                            case 'subscribe':  //关注事件, 扫描带参数二维码事件(用户未关注时,进行关注后的事件推送)
                                return "hello everyone!";
                                break;
                            case 'unsubscribe':  //取消关注事件
                                break;
                            case 'SCAN':  //扫描带参数二维码事件(用户已关注时的事件推送)
    							$obj = new SCAN(); //处理扫码相关业务逻辑
    							$info = $obj -> index($message);
    							Log::info($info);
                                return $info;
                                break;
                            default:
                                return $message['Event'];
                                break;
                        }
    					break;
            });
    
            return $app->server->serve();
        }
    }
    
    • 业务模块并推送模版消息
    <?php
    
    namespace App\Model;
    
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Support\Facades\DB;
    
    class SCAN extends Model
    {
        //扫码
    	public function index($message){
    		$openid = $message['FromUserName'];
    		$evenkey = $message['EventKey'];
    		$preg = '/^aaa-/';
    		preg_match_all($preg,$evenkey,$evenkey_arr);
    		if(!empty($evenkey_arr[0])){
    			$new_evenkey = $evenkey_arr[0][0];
    			$reg = "/^$new_evenkey/";
    			$evenkey = trim(preg_replace($reg, ' ', $evenkey));
    			if($new_evenkey == "aaa-"){  //生成二维码时所传人的参数
    				$this -> operation($evenkey,$openid);
    			}
    		}else{
    			return;
    		}
    	}
    
        /**
        * @param evenkey 参数
        * @param openid 要向哪个用户推送信息
        */
        public function operation($evenkey,$openid)
        {
           //此处省略业务逻辑 根据一个状态判断 大家直接套用就好
           $status = 1;
           if($status == 1){
               $this->success($openid);
           }else{
               $this->error($openid);
           }
        }
    
    
        /**
        * @title 扫码成功
        * @param openid 用户openid
        */
    	public function success($openid){
    		$app = app('wechat.official_account');
    
            //这里可以填写您选择的公众号中模版消息的模版id
            $template_id = '';  
            
            //$data是模版中的详细内容 按照微信中的内容进行填写 下面只是一个例子
    		$data = array(
                "first"  => '',
                "keyword1"   => '',
                "keyword2"  => '',
    			"keyword3"  => date('Y-m-d H:i'),
    			"keyword4" => '',
                "remark" => ''
            );
    
            //最后发送的信息
    		$info = [
    			'touser' => $openid,
    			'template_id' => $template_id,
    			'url' => '',
    			'data' => $data,
    		];
    
    		return $app -> template_message ->send($info);
    	}
    
    
        /**
        * @title 扫码失败
        * @param openid 用户openid
        */
    	public function error($openid){
    		$app = app('wechat.official_account');
    
            //这里可以填写您选择的公众号中模版消息的模版id
            $template_id = '';  
            
            //$data是模版中的详细内容 按照微信中的内容进行填写 下面只是一个例子
    		$data = array(
                "first"  => '',
                "keyword1"   => '',
                "keyword2"  => '',
    			"keyword3"  => date('Y-m-d H:i'),
    			"keyword4" => '',
                "remark" => ''
            );
    
            //最后发送的信息
    		$info = [
    			'touser' => $openid,
    			'template_id' => $template_id,
    			'url' => '',
    			'data' => $data,
    		];
    
    		return $app -> template_message ->send($info);
    	}
    }
    

    以上是我的使用心得 谢谢大家!

    展开全文
  • 1. 前言   首先说说为啥会接触软件的编码与...即用户在web端点击发起签到后,web浏览器显示二维码并动态刷新,同时移动端微信小程序用户可以点击扫码功能进行扫码签到,如果扫码成功了则会在服务器数据库的签到记录

    1. 前言
      首先说说为啥会接触软件的编码与开发呢?是因为这学期的“软件工程”必修课程要求每个小组开发一款软件。而我们打算开发的是一款课程签到软件,包括登录、注册、签到、查询、增删改等功能。在技术方面涉及web开发、小程序开发和服务器数据库的搭建。web开发我们主要使用php,小程序开发使用微信开发者工具
      而我负责的是发起签到与扫码签到部分。即用户在web端点击发起签到后,web浏览器显示二维码并动态刷新,同时移动端微信小程序用户可以点击扫码功能进行扫码签到,如果扫码成功了则会在服务器数据库的签到记录表插入一条学生签到信息并在微信小程序弹窗提示成功信息。
      为什么要在web浏览器上动态刷新二维码呢?其实是为了一定意义上防止诸如“某同学在教室拍二维码发给在宿舍睡觉的舍友,即使不去上课也能躲过课堂考勤”的现象~

    2. 附带有效期的二维码
      为什么拍照发回给舍友的二维码就失效了,原理是什么?主要的实现思路是在生成的二维码中加入了额外的计数信息,当二维码不断刷新的时候,这个计数就不断递增,当你拍照发给舍友之后,照片里的二维码计数信息相比当前计数信息已经滞后了很多,导致二维码失效而签到失败。
      利用phpqrcode库生成二维码(1.php):

    include "./phpqrcode/phpqrcode.php";
    
    $numdata = intval($checking_num);
    $qrdata = intval($checking_id + $numdata*10000);    # 合并成一个数据,解码即可获取计数和checking id
    $level = "L";      # 纠错级别,最多可识别已损失的数据, L--7%,M--15%,Q--25%,H--30%
    $size = 15;        # 二维码尺寸
    $margin = 2;       # 二维码边缘填充
    
    QRcode::png($qrdata,false,$level,$size,$margin,false);
    

    其中,checking_id是课程签到表的主键,代表某课程某次发起签到的id号,由checking_id可知对应课程在对应时刻的课堂签到总体情况。
      二维码信息在函数的第一个参数(qrdata是随便命名的),其中包含了两个信息,即计数num 和 checking_id,对它进行 num × 10000 + checking_id 的编码操作将两个变量合成一个数据插入二维码中,简单解码即可得到计数num和checking_id,如 120010
    120010 % 10000 = 10(checking_id
    (120010 - checking_id)/ 10000 = 12(num

      二维码动态刷新(2.php):

    <p>
    	<script> 
    	    function myrefresh() 
    	    { 
    	        //window.location.reload();   //只有这句就够了,下一行意思也是定位当前页面
    	        window.location.replace("teacher_make_checking_go.php?class_id="+<?php echo $class_id;?>+"&checking_id="+<?php echo $checking_id;?>);
    	    } 
    	    setTimeout('myrefresh()',1000); //指定1秒刷新一次 
        </script>
        //展示二维码,并传参数,传入的php可用 GET获取checking_id
        <img src="teacher_make_checking.php?<?php echo "checking_id=" . $checking_id;?>">
    </p>
    

    3. web端和小程序变量共享
      上面基本上就将生成并动态刷新附带有效期的二维码的原理讲完了。二维码的有效期只与num和num’有关,当小程序上传扫码获取的计数num’到服务器后,服务器需要将num’和num进行比对,如果num - num’ < 一定范围(几秒,比拍照回传时间短即可),则说明签到成功。
      一开始我想到的方法是使用SESSION全局变量在不同php之间传递计数num,以此可以在 3.php(1.php、2.php…代指不同页) 里对比小程序上传的num’和num的差值,并判断是否签到成功。然而我忽略了一个问题:服务器与客户端初次创建session时,在返回给客户端的header中有SetCookie,客户端会把里面的sessionid存到cookie中,这样下次请求服务器,服务器就能根据请求头中cookie信息确定客户端想要的SESSION,然而小程序请求服务器开启SESSION则又是另一个sessionid,不同的SESSION之间变量是不共享的,所以小程序请求服务器的php中根本无法得到web客户端当前的num,二者无法进行比较,如下图所示:
    在这里插入图片描述
      考虑到上述方法中不断递增的计数num是无法共享的,导致无法计算 num - num’的问题,我们则需要考虑一种能将计数num变量储存在服务器的方法,这样不同客户端便都可以访问了。
      于是我决定web端在刷新二维码递增num的同时,根据checking_id主键将num储存进数据库中,这样在移动端扫码上传checking_id和num’到服务器时,服务器就能根据checking_id读取当前的计数num,再计算 num - num’ 判断是否签到成功或超时即可。
      访问逻辑如下图所示:
    在这里插入图片描述
    数据库的设计如下:
    在这里插入图片描述
      获取数据库存储的计数num以及小程序扫码上传的num’并进行判定的php代码如下,如果签到成功就会将学生签到信息存进签到信息表里,并设置status发送给小程序用来在小程序中弹窗提示签到结果:

      if ($conn->connect_error) {   die("Could not connect to database!");}
    
      //获取小程序传递数据
      $qrdata = intval(isset($_GET['qrdata_send']) ? $_GET['qrdata_send'] : 0);
      $student_id = intval(isset($_GET['student_id']) ? $_GET['student_id'] : 0);
      $checking_id = $qrdata % 10000;
      $num_get = ($qrdata - $checking_id) / 10000;
      $status = '0';    // 签到成功标志, 1成功,0失败
      
      include "scan_mysql.php";
      
      foreach ($sql_2_presented_result as $row){
        		$checking_num = $row['checking_num'];
     	}
      
      // 保证GET到了数据
      if ($qrdata!=0 && $student_id!=0)
      {
        if ($checking_num-$num_get<4)
        {
          $sql = "INSERT INTO signin_info(checking_id, student_id) value('$checking_id', '$student_id')";
          $status = '1';
          if (!mysqli_query($conn, $sql)){
            die('Error: ' . mysqli_error($con)); 
            $status = '0';
          }
        }
      } 
    
      // 关闭连接
      $conn->close();
      
      $res['status'] = $status;
      header("Content-type:application/json");
      
      echo json_encode($res);
      
      die();
    

      至此就成功实现了动态刷新二维码并给每一帧二维码附带有效期的功能,一定意义上解决了点名费时费力和普通扫码考勤容易伪造签到的问题。使大家不能好好在宿舍睡觉了~
      如果有更好的解决方法,也欢迎指教~

    展开全文
  • PHP微信扫码签到源码

    2020-03-07 22:34:11
    使用微信公众号接口,生效推广二维码,用户扫码实现签到记录。并回显签到者。 DEMO示例: https://www.uenen.com/custom/qiandao.php
  • 二维码签到

    千次阅读 2017-06-21 22:10:28
    服务端主要完成课程创建,课程列表,然后查看列表,每一个有个展示,完成二维码扫码,扫出来此课程的所有学生信息。详细的界面完整流程: 我们的BaseApplication 是全局的Application,里面实现启动时候的全局初始...

    整体设计思路:

    客户端主要完成学生注册,登陆,查看自己的信息,以及自己的签到信息。
    服务端主要完成课程创建,课程列表,然后查看列表,每一个有个展示,完成二维码扫码,扫出来此课程的所有学生信息。

    详细的界面完整流程:
    我们的BaseApplication 是全局的Application,里面实现启动时候的全局初始化数据,主要完成数据库创建 二维码信息目录创建
    设置和获取用户信息。
    @Override
    public void onCreate() {
    super.onCreate();
    //创建数据库 info
    LiteOrmDBUtil.createDb(this, “info”);
    // 创建一个目录
    initFile();
    }

    private void initFile() {
        File storageDirectory = Environment.getExternalStorageDirectory();
        String path = storageDirectory.getPath() + "/QrWiFiSign";
        File file = new File(path);
        if (!file.exists()) {
            file.mkdir();
        }
    }
    
    // 存下来学生信息
    public void setUser(user info) {
        this.info = info;
    }
    
    // 返回学生信息
    public user getUser() {
        return info;
    }
    

    AndroidManifest.xml 里面有这些activity.
    AnimationActivity 第一个动画界面,启动入口,完成一个动画效果,然后进入选择是客户端和服务端主界面。
    AlphaAnimation aa = new AlphaAnimation(0.0f, 1.0f);
    aa.setDuration(ANIMATION_TIME);
    aa.setFillAfter(true);
    //动画,结束后进行启动选择是否进入客户端还是服务端
    //客户端就是登录用户,然后里面显示我的二维码,我的课程列表
    //服务端主要完成课程建立,然后用户扫码,进行签到课程即可

        this.findViewById(R.id.iv_animation_logo).startAnimation(aa);
    
        aa.setAnimationListener(new AnimationListener() {
    
            @Override
            public void onAnimationStart(Animation animation) {
    
            }
    
            @Override
            public void onAnimationRepeat(Animation animation) {
    
            }
    
            @Override
            public void onAnimationEnd(Animation animation) {
    
                startMainActivity();
            }
        });
    
    }
    
    //进入选择界面
    private void startMainActivity() {
        finish();
        Intent intent = new Intent(this, selActivity.class);
        startActivity(intent);
    }
    

    selActivity 选择的界面,主要点击客户端和服务端,进入自己的主界面
    // 客户端,进入用户注册登录界面
    kehuduan = (TextView) findViewById(R.id.kehuduan);
    //注册回调,于是selActivity会实现回调方法
    kehuduan.setOnClickListener(this);

        fuwuduan = (TextView) findViewById(R.id.fuwuduan);
        fuwuduan.setOnClickListener(this);
    

    //这里实现OnClickListener方法
    selActivity extends Activity implements OnClickListener {

    //然后实现的点击流程
    // 点击客户端,进入登录注册界面 完成登录界面 注册界面的启动
    @Override
    public void onClick(View arg0) {
    // TODO Auto-generated method stub
    switch (arg0.getId()) {
    case R.id.kehuduan:
    //客户端点击,进入注册界面
    Intent intent = new Intent(this, registerActivity.class);
    startActivity(intent);
    break;
    case R.id.fuwuduan:
    //服务端点击,进入服务端主界面 显示创建课程 和 所有课程
    Intent intent1 = new Intent(this, fuwuduanActivity.class);
    startActivity(intent1);
    break;
    }
    finish();
    }

    registerActivity 注册界面,显示信息如下,填写完成后点击注册或者去直接登陆

    // 姓名
    EditText xingming;
    // 班级
    EditText banji;
    // 学号
    EditText xuehao;
    // 注册
    TextView zhuce;
    // 登录
    TextView denglu;
    

    点击注册的内容信息为:
    // 注册逻辑代码,需要判断是否为空,为空提示,如果ok,存储下来,同时需要注意学号不能重复

        zhuce.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                String tmp1 = xingming.getText().toString();
                String tmp2 = banji.getText().toString();
                String tmp3 = xuehao.getText().toString();
                // 如果发现哪个为空,就提示让输入
                if (tmp1.isEmpty() || tmp2.isEmpty() || tmp3.isEmpty()) {
                    Toast.makeText(registerActivity.this,
                            "姓名 班级 学号 都不能为空,请输入完成注册!", Toast.LENGTH_SHORT)
                            .show();
                    return;
                }
                // 查询是否都存储过了 如果此学号的已经有,则提示已经有此用户了,可以去登陆
                // 查询此学号是否存在了
                QueryBuilder qb = new QueryBuilder(user.class).whereEquals(
                        "xuehao", tmp3);
                List<user> aa = LiteOrmDBUtil.getLiteOrm().<user> query(qb);
                if (aa.size() > 0) {
                    Toast.makeText(registerActivity.this,
                            "此学号已经存在了,如果注册了,请登录!", Toast.LENGTH_SHORT).show();
                    return;
                }
                // 如果此学号没有,就可以存储了
                LiteOrmDBUtil.saveData(new user(tmp1, tmp2, tmp3));
    
                Toast.makeText(registerActivity.this, "注册成功,可以登录了!",
                        Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(registerActivity.this,
                        loginActivity.class);
                startActivity(intent);
            }
        });
    

    点击去登录的逻辑:
    denglu.setOnClickListener(new OnClickListener() {
    //进入登录界面
    @Override
    public void onClick(View arg0) {
    // TODO Auto-generated method stub
    Intent intent = new Intent(registerActivity.this,
    loginActivity.class);
    startActivity(intent);
    }
    });

    loginActivity 登录界面,实现填写 学生 名字和 学号 ,然后登录进来

    点击登录的逻辑为:
    // 登录逻辑代码,需要判断是否为空,为空提示,如果ok 判断 输入的名字 和学号是否存在,存在说明正确的。

        denglu.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                String tmp1 = xingming.getText().toString();
                String tmp2 = xuehao.getText().toString();
                // 如果发现哪个为空,就提示让输入
                if (tmp1.isEmpty() || tmp2.isEmpty()) {
                    Toast.makeText(loginActivity.this, "姓名 学号 不能为空,请输入完成后登陆!",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                // 查询是否都存储过了 如果此学号的已经有,则提示已经有此用户了,可以去登陆
                // 查询此学号是否存在了
                QueryBuilder qb = new QueryBuilder(user.class)
                        .whereEquals("xuehao", tmp2).whereAppendAnd()
                        .whereEquals("name", tmp1);
                List<user> aa = LiteOrmDBUtil.getLiteOrm().<user> query(qb);
                // 如果 没有查到信息,说民输入错误了,请重输入
                if (aa.size() == 0) {
                    Toast.makeText(loginActivity.this, "姓名 学号输入有误,请重新输入后登陆!",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                //存储下来这个登录的用户,后面需要用 
                ((BaseApplication) getApplication()).setUser(aa.get(0));
                // 进入主界面
                Toast.makeText(loginActivity.this, "登陆成功!", Toast.LENGTH_SHORT)
                        .show();
                // 启动进入学生主界面 ,主要显示两个信息 ,我的信息,以及我的签到课程
                Intent intent = new Intent(loginActivity.this,
                        MainActivity.class);
                startActivity(intent);
            }
        });
    

    MainActivity 进入学生的主界面

    主要显示的为:我的信息 以及 我的签到
    点击为:
    case R.id.wodeqiandao:
    //我的签到界面
    Intent intent = new Intent(this, qiandaoActivity.class);
    startActivity(intent);
    break;
    case R.id.wodexinxi:
    //我的信息
    Intent intent1 = new Intent(this, xinxiActivity.class);
    startActivity(intent1);
    break;
    }

    xinxiActivity 我的信息,显示我的名字 我的班级 我的学号 ,我的二维码
    // 获取这个登录的用户
    user info = ((BaseApplication) getApplication()).getUser();
    if (info != null) {
    xingming.setText(“姓名 : ” + info.getName());
    banji.setText(“班级 : ” + info.getBanji());
    xuehao.setText(“学号 : ” + info.getXuehao());
    }
    //生成的二维码显示出来
    CreateQrCode(info);

    public void CreateQrCode(user info) {
        //将学生的信息存储下来,用的json存下来。
        //生成位图
        Bitmap bitmap = EncodingUtils.createQRCode(info.toString(), 300, 300,
                null);
        //存储下来
        saveBitmap2Image(bitmap, info.getXuehao() + ".jpg");
    }
    
    public void saveBitmap2Image(Bitmap bmp, String filename) {
        OutputStream stream = null;
        String path = "/sdcard/QrWiFiSign/" + filename;
        File file = new File(path);
        Bitmap.CompressFormat format = Bitmap.CompressFormat.JPEG;
        int quality = 100;
        if (file.exists()) {
            //如果已经生成过了,就直接拿到本地的图显示出来即可
            erweima.setImageBitmap(getLoacalBitmap(path));
            return;
        }
        try {
            //拿到存储的流
            stream = new FileOutputStream(path);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 存储下来
        bmp.compress(format, quality, stream);
        // 设置这个位图
        erweima.setImageBitmap(bmp);
    }
    
    /**
     * 加载本地图片
     * 
     * @param url
     * @return
     */
    public Bitmap getLoacalBitmap(String url) {
        try {
            FileInputStream fis = new FileInputStream(url);
            return BitmapFactory.decodeStream(fis); // /把流转化为Bitmap图片
    
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
    

    qiandaoActivity 显示我的所有签到信息,主要列出来所有此学号的签到课程,通过签到课程id,拿到所有的详细信息
    QueryBuilder qb = new QueryBuilder(qiandao.class).whereEquals(“xuehao”,
    info.getXuehao());
    qiandaoinfo = LiteOrmDBUtil.getLiteOrm(). query(qb);
    这里通过学号查询,查出所有的签到信息

    然后通过签到id,然后找出签到的课程信息,显示出来。

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            ViewHolder viewHolder = null;
    
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.list_item, null);
                viewHolder.name = (TextView) convertView
                        .findViewById(R.id.mingzi);
                viewHolder.index = (TextView) convertView
                        .findViewById(R.id.index);
                viewHolder.time = (TextView) convertView
                        .findViewById(R.id.shijian);
                viewHolder.laoshi = (TextView) convertView
                        .findViewById(R.id.laoshi);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            qiandao item = qiandaoinfo.get(position);
            //从课程里面,通过课程id 找到对应的课程信息,此处需要显示它
            QueryBuilder qb = new QueryBuilder(kecheng.class).whereEquals(
                    "index", item.getIndex());
            List<kecheng> kecheng = LiteOrmDBUtil.getLiteOrm().<kecheng> query(
                    qb);
            //如果查到了,拿到第一个就好了,因为我们原则上,不允许同一个课程id出现在 多个课程里面,所以可以保证一个课程id对应一门课程
            if (kecheng.size() != 0) {
                kecheng tmp = kecheng.get(0);
                viewHolder.name.setText(tmp.getName());
                viewHolder.index.setText(tmp.getIndex() + "");
                viewHolder.time.setText(tmp.getTime());
                viewHolder.laoshi.setText(tmp.getLaoshi());
            }
    
            return convertView;
        }
    
        private final class ViewHolder {
            public TextView name;
            // 课程index
            public TextView index;
            // 时间
            public TextView time;
            // 老师
            public TextView laoshi;
        }
    }
    

    服务端主界面,主要就是创建课程 和 课程列表

    chuangjianActivity 创建课程
    // 课程index
    EditText index;
    // 名字
    EditText name;
    // 时间
    TextView time;
    // 老师
    EditText laoshi;
    // 创建课程
    TextView chuangjian;

    时间响应为:
    time.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                initTimeFromPickerListener();
                showTimePickerDiaLog();
    
            }
        });
    
    //显示时间选择
    private void showTimePickerDiaLog() {
        calendar = Calendar.getInstance();
        new TimePickerDialog(this, timeSetListener,
                calendar.get(Calendar.HOUR_OF_DAY),
                calendar.get(Calendar.MINUTE), true).show();
    }
    //时间选择回调,设置我们的时间信息
    private void initTimeFromPickerListener() {
        timeSetListener = new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
    
                time.setText("时间:" + hourOfDay + ":" + minute);
            }
        };
    }
    

    点击创建,逻辑代码为:
    chuangjian.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                String tmp1 = index.getText().toString();
                String tmp2 = name.getText().toString();
                String tmp3 = time.getText().toString();
                String tmp4 = laoshi.getText().toString();
                // 如果发现哪个为空,就提示让输入
                if (tmp1.isEmpty() || tmp2.isEmpty() || tmp3.isEmpty()) {
                    Toast.makeText(chuangjianActivity.this,
                            "课程id 名字 时间 老师  都不能为空,请输入完成注册!", Toast.LENGTH_SHORT)
                            .show();
                    return;
                }
                int index = 0;
                // id 转换为整数,不行则确定课程id不正确,需要重输入
                try {
                    index = Integer.valueOf(tmp1);
    
                } catch (java.lang.NumberFormatException ex) {
                    Toast.makeText(chuangjianActivity.this, "课程ID只能是数字!",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                //存储下来课程信息
                LiteOrmDBUtil.saveData(new kecheng(index, tmp2, tmp3, tmp4));
                //显示创建成功
                Toast.makeText(chuangjianActivity.this, "创建课程成功!",
                        Toast.LENGTH_SHORT).show();
                //关闭此界面
                finish();
            }
        });
    

    二维码存储的位置在
    “/sdcard/QrWiFiSign/”下面

    com\yecc\scancode 这个里面就是核心的二维码扫码了,直接用的三方的库,不用关心具体实现。
    CaptureActivity.java 主要的扫码界面
    ImagecheckActivity.java 图片选择的界面
    Myalbumlist.java 图片选择的列表item

    服务器端代码是:
    fuwuduanActivity 主要显示菜单,完成两个功能:创建课程和所有课程
    具体实现为:
    switch (arg0.getId()) {
    case R.id.chuangjian:
    //启动创建课程界面
    Intent intent = new Intent(this, chuangjianActivity.class);
    startActivity(intent);
    break;
    case R.id.suoyoukecheng:
    // 所有课程界面
    Intent intent1 = new Intent(this, suoyouActivity.class);
    startActivity(intent1);
    break;
    }
    chuangjianActivity 创建课程界面
    主要元素:
    // 课程index
    EditText index;
    // 名字
    EditText name;
    // 时间
    TextView time;
    // 老师
    EditText laoshi;
    时间点击处理逻辑:

        time.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                //设置时间点击处理
                initTimeFromPickerListener();
                //显示时间选择对话框
                showTimePickerDiaLog();
    
            }
        });
    

    initTimeFromPickerListener 接收到时间选择的时候,设置下时间
    private void initTimeFromPickerListener() {
    timeSetListener = new TimePickerDialog.OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

                time.setText("时间:" + hourOfDay + ":" + minute);
            }
        };
    }
    

    弹出时间选择界面 timeSetListener 是回调方法。
    private void showTimePickerDiaLog() {
    calendar = Calendar.getInstance();
    new TimePickerDialog(this, timeSetListener,
    calendar.get(Calendar.HOUR_OF_DAY),
    calendar.get(Calendar.MINUTE), true).show();
    }
    点击创建课程的代码:
    chuangjian = (TextView) findViewById(R.id.chuangjian);
    chuangjian.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                String tmp1 = index.getText().toString();
                String tmp2 = name.getText().toString();
                String tmp3 = time.getText().toString();
                String tmp4 = laoshi.getText().toString();
                // 如果发现哪个为空,就提示让输入
                if (tmp1.isEmpty() || tmp2.isEmpty() || tmp3.isEmpty()) {
                    Toast.makeText(chuangjianActivity.this,
                            "课程id 名字 时间 老师  都不能为空,请输入完成注册!", Toast.LENGTH_SHORT)
                            .show();
                    return;
                }
                int index = 0;
                // id 转换为整数,不行则确定课程id不正确,需要重输入
                try {
                    index = Integer.valueOf(tmp1);
    
                } catch (java.lang.NumberFormatException ex) {
                    Toast.makeText(chuangjianActivity.this, "课程ID只能是数字!",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                //存储下此课程
                LiteOrmDBUtil.saveData(new kecheng(index, tmp2, tmp3, tmp4));
                //弹出创建成功
                Toast.makeText(chuangjianActivity.this, "创建课程成功!",
                        Toast.LENGTH_SHORT).show();
                        //关掉此界面
                finish();
            }
        });
    

    suoyouActivity 所有课程界面

    此界面是个list,然后设置适配器,查询到所有课程显示出来
    detlist = (ListView) findViewById(R.id.detlist);

        detlist.setAdapter(new tbViewAdapter(this));
        //查询所有课程
        kechenginfo = LiteOrmDBUtil.getLiteOrm().<kecheng> query(kecheng.class);
    

    界面每个元素的界面更新逻辑:
    @Override
    public View getView(final int position, View convertView,
    ViewGroup parent) {

            ViewHolder viewHolder = null;
    
            if (convertView == null) {
                viewHolder = new ViewHolder();
                //加载布局
                convertView = mInflater.inflate(R.layout.list_fuwu_item, null);
                // 初始化view
                viewHolder.name = (TextView) convertView
                        .findViewById(R.id.mingzi);
                viewHolder.index = (TextView) convertView
                        .findViewById(R.id.index);
                viewHolder.time = (TextView) convertView
                        .findViewById(R.id.shijian);
                viewHolder.laoshi = (TextView) convertView
                        .findViewById(R.id.laoshi);
                viewHolder.qiandao = (TextView) convertView
                        .findViewById(R.id.qiandao);
                viewHolder.yiqiandao = (TextView) convertView
                        .findViewById(R.id.yiqiandao);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            //获取到课程
            final kecheng item = kechenginfo.get(position);
            if (item != null) {
                viewHolder.name.setText("名字:" + item.getName());
                viewHolder.index.setText("Id:" + item.getId_index() + "");
                viewHolder.time.setText(item.getTime());
                viewHolder.laoshi.setText("老师:" + item.getLaoshi());
            } else {
                return convertView;
            }
            // 点击签到。我们启动二维码扫码界面,进行处理。
            //index 是此课程的id,我们做关联学生信息的,学生这里我们会存下来学号,作为标识
            viewHolder.qiandao.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    Intent intent1 = new Intent(suoyouActivity.this,
                            CaptureActivity.class);
                    index = item.getId_index();
                    //启动返回时候接受参数,具体在onActivityResult里面处理返回
                    startActivityForResult(intent1, SCANNIN_GREQUEST_CODE);
                }
            });
            //已签到,点击进入已签到学生,启动传入参数课程id
            viewHolder.yiqiandao.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    Intent intent1 = new Intent(suoyouActivity.this,
                            yiqiandaoActivity.class);
                    intent1.putExtra("index", item.getId_index());
                    startActivity(intent1);
                }
            });
            return convertView;
        }
    
        private final class ViewHolder {
            public TextView name;
            // 课程index
            public TextView index;
            // 时间
            public TextView time;
            // 老师
            public TextView laoshi;
            // 签到
            public TextView qiandao;
            // 已签到
            public TextView yiqiandao;
        }
    }
    

    我们看下扫码后的处理:
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
    case SCANNIN_GREQUEST_CODE:
    if (resultCode == RESULT_OK) {
    Bundle bundle = data.getExtras();
    //拿到结果
    String result = bundle.getString(“result”);
    // Log.i(“logxxxxx”, bundle.getString(“result”));
    //结果转换成user
    user userInfo = gson.fromJson(result, user.class);
    // 通过学号值和课程id,来确定下是否此学生已经签到了此课,如果签了就提示签过了
    否则签到
    QueryBuilder qb = new QueryBuilder(qiandao.class)
    .whereEquals(“xuehao”, userInfo.getXuehao())
    .whereAppendAnd().whereEquals(“id_index”, index);
    List tmp = LiteOrmDBUtil.getLiteOrm(). query(
    qb);
    if (tmp.size() == 0) {
    // 如果此学号没有,就可以存储了
    LiteOrmDBUtil.saveData(new qiandao(index, userInfo
    .getXuehao()));
    Toast.makeText(this, “课程签到成功”, Toast.LENGTH_LONG).show();
    } else {
    Toast.makeText(this, “此学生已经签到了”, Toast.LENGTH_LONG).show();
    }

            }
            break;
        }
    }
    

    yiqiandaoActivity 已签到的学生界面:
    //拿到课程id
    index = getIntent().getIntExtra(“index”,0);
    detlist.setAdapter(new tbViewAdapter(this));
    // 查询此学号在签到列表进行检索
    QueryBuilder qb = new QueryBuilder(qiandao.class).whereEquals(
    “id_index”, index);
    //查到签到信息
    qiandaoinfo = LiteOrmDBUtil.getLiteOrm(). query(qb);

    在列表里面更新的代码:
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

            ViewHolder viewHolder = null;
    
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.xinxi, null);
                viewHolder.xingming = (TextView) convertView.findViewById(R.id.xingming);
                viewHolder.banji = (TextView) convertView.findViewById(R.id.banji);
                viewHolder.xuehao = (TextView) convertView.findViewById(R.id.xuehao);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            qiandao item = qiandaoinfo.get(position);
            // 从课程里面,通过课程id 找到对应的课程信息,此处需要显示它
            QueryBuilder qb = new QueryBuilder(user.class).whereEquals(
                    "xuehao", item.getXuehao());
            List<user> tmp = LiteOrmDBUtil.getLiteOrm().<user> query(qb);
            // 如果查到了,拿到第一个就好了
            if (tmp.size() != 0) {
                user u = tmp.get(0);
                viewHolder.xingming.setText("姓名 : " + u.getName());
                viewHolder.banji.setText("班级 : " + u.getBanji());
                viewHolder.xuehao.setText("学号 : " + u.getXuehao());
            }
    
            return convertView;
        }
    
        private final class ViewHolder {
            // 姓名
            TextView xingming;
            // 班级
            TextView banji;
            // 学号
            TextView xuehao;
        }
    }   
    

    qiandaoActivity 我的签到,通过我的学号,去签到里面查找,然后查到的课程id,从课程id里面找课程,然后显示出来。
    // 存储下来这个登录的用户,后面需要用
    user info = ((BaseApplication) getApplication()).getUser();
    // 查询此学号在签到列表进行检索
    QueryBuilder qb = new QueryBuilder(qiandao.class).whereEquals(“xuehao”,
    info.getXuehao());
    qiandaoinfo = LiteOrmDBUtil.getLiteOrm(). query(qb);
    更新view的界面:

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            ViewHolder viewHolder = null;
    
            if (convertView == null) {
                viewHolder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.list_item, null);
                viewHolder.name = (TextView) convertView
                        .findViewById(R.id.mingzi);
                viewHolder.index = (TextView) convertView
                        .findViewById(R.id.index);
                viewHolder.time = (TextView) convertView
                        .findViewById(R.id.shijian);
                viewHolder.laoshi = (TextView) convertView
                        .findViewById(R.id.laoshi);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            qiandao item = qiandaoinfo.get(position);
            //从课程里面,通过课程id 找到对应的课程信息,此处需要显示它
            QueryBuilder qb = new QueryBuilder(kecheng.class).whereEquals(
                    "id_index", item.getId_index());
            List<kecheng> kecheng = LiteOrmDBUtil.getLiteOrm().<kecheng> query(
                    qb);
            //如果查到了,拿到第一个就好了,因为我们原则上,不允许同一个课程id出现在 多个课程里面,所以可以保证一个课程id对应一门课程
            if (kecheng.size() != 0) {
                kecheng tmp = kecheng.get(0);
                viewHolder.name.setText("课程名字:"+tmp.getName());
                viewHolder.index.setText("课程id:"+tmp.getId_index() + "");
                viewHolder.time.setText(tmp.getTime());
                viewHolder.laoshi.setText("老师:"+tmp.getLaoshi());
            }
    
            return convertView;
        }
    
        private final class ViewHolder {
            public TextView name;
            // 课程index
            public TextView index;
            // 时间
            public TextView time;
            // 老师
            public TextView laoshi;
        }
    }
    
    展开全文
  • 微信扫码签到系统asp源码2.0示例

    千次阅读 2019-02-18 14:15:08
    asp写的微信扫码签到系统,代码简单。 不管是工作会议,还是员工活动,每次集会基本都需要签到。传统的纸质签到效率低下,找名字费时,排队费时,更可怕的是好不容易轮到了你,却发现你的名字在隔壁人堆里那张纸上。...
  • 基于二维码签到系统是从前台入手还是从后台入手?先设计数据库还是先手机界面设计,是开发新手,望大神指点
  • 分享下最近做的一个微信程序会议签到,本系统后台基于ssm(Spring+SpringMVC+MyBatis)开发,前台为小程序开发的会议扫码签到程序,系统以Java作为编程语言,采用Mysql数据库作为后台数据库。它是基于Browser/Server...
  • 此代代码的功能是,集会现场只需贴出一个二维码,成员打开表单直接签到签到时还可以获取签到者的位置信息,防止没到会议现场,在别处签到。提交的签到数据还可以直接生成报表,后期统计一步到位。 代码如下...
  • 用户到场主动扫码签到、或用户出示二维码签到、或工作人员后台帮签到。 注意:活动报名二维码和会议签到二维码是同一个二维码,即同一个页面内容可同时报名及签到,或只报名,或只签到,只需在编缉器里插入报名或...
  • 使用白低代码开发平台的公开页,实现系统里的客户或学生扫描二维码跳转到签到页面,输入手机号码进行签到。 前期准备: 在工作台,系统设置–用户管理中,启用公共用户。 数据表创建:创建一张签到人表、签到...
  • 【python】django实现扫码签到

    千次阅读 2017-06-16 21:27:00
    一个基本的扫码签到系统: 访问指定URL能出来二维码 管理员登陆后才能访问URL 后端产生二维码 二维码发送到前端 二维码在前端显示出来 二维码能定时更新 扫码之后能执行一些操作 用户设备扫...
  • 0. 前言 环境: python3.6 模块: tornado IDE工具: pycharm 1. 学习方法 ...不要过于追追根到底, ...用户扫描二维码,跳转到一个用户签到页面; 用户签到页面, 用户填入正确信息,即可将签到信息存储到文件或...
  • 最近实现的业务需求上,要实现现场的实地签到,由于本身报名就是有小程序来实现的,又定好了扫码签到是有企业微信中接一个web应用来实现扫码。 利用vue-cli起一个web项目,写好页面,然后就是调微信扫一扫api。 根据...
  • zxing集成到Android Studio中实现二维码扫一扫功能
  • 需求:用户在扫描我们提供的二维码后,可以在二维码位置提示‘扫码成功’等信息 环境:php7.2、redis 实现: 1、http长连接 通过websocket建立起客户端与服务端的双工通信。 用户扫码后打开二维码对应的链接...
  • Java扫描二维码进行会议签到思路

    千次阅读 2017-07-12 17:29:34
    1:签到页面都是同一个JSP页面 2:根据不同的会议ID进行拼接URL跳转页面进行签到 JSP页面代码如下 Insert title here function on_load() { alert(); } 点击获取会议ID ...
  • 项目需求:会议管理模块涉及到会议扫码签到功能,扫描二维码判断用户是否关注公众号,如果没关注跳转关注页面,给用户推送绑定消息;如果关注了判断用户是否绑定<PC端与微信端用户信息绑定>,如果没绑定跳转...
  • 2.微信扫码登录注册,登录成功后即可进入草料二维码后台(手机微信小程序也可以实时查看后台数据) 3.根据自己需要创建活动二维码(选择自己喜欢的模板,这里我们选择得事会议签到) 4.然后根据模板可任意修改自己...
  • 【项目】扫码签到

    千次阅读 2018-11-26 02:52:48
    【3】百度‘二维码生成器’,如果写域名,直接跳转。 【4】前端提交后,后台收到数据 【5】收到的数据,写到文件 【6】获取当前时间 》》import time 》》time.localtime() 【7】pyth...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,103
精华内容 441
关键字:

二维码扫码签到