从0到1:志愿者小程序开发心得

2023-12-17 22:20

本文主要是介绍从0到1:志愿者小程序开发心得,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景调研

志愿者服务小程序可以使志愿服务更加有序和高效,方便志愿者参与,也方便组织方管理和组织志愿服务活动;志愿者可以方便的在手机上报名,查看记录,获取积分,了解积分排行; 同时提供积分商城模块,可以让志愿者使用通过参与活动所获得的积分来兑换各种奖励或福利.

功能规划

【用户端】
  • 活动浏览: 志愿者可以浏览各种志愿服务活动,了解项目的具体内容、时间和地点等信息。
  • 在线报名: 提供在线报名功能,志愿者可以方便地选择感兴趣的活动并提交报名申请。
  • 个人信息管理: 志愿者可以管理个人信息,包括联系方式、技能和经验等。
  • 志愿服务活动记录: 记录志愿者的活动历史,包括参与的活动、服务获取积分等。
  • 积分获取和排行榜:每次活动可以获得一定数量的积分,后台也可以手工给与特别贡献者额外积分奖励。并按积分进行排行张榜公布。
  • 积分商城: 提供各种商品或服务供志愿者兑换,可以包括礼品、优惠券、志愿者服务证书、特殊活动的参与资格等。
【管理端】
  • 活动发布:发布新的志愿服务项目,包括项目介绍、时间、地点和所需人数,完成活动获取积分数等信息。
  • 报名管理: 管理方便地查看志愿者的报名情况,确保志愿者的匹配和合格。并可以导出每次报名的Excel名单。
  • 积分商城管理:可以录入可供兑换的商品信息,并设置所需积分数。
  • 积分兑换订单:可以检索和查看用户兑换记录,并设定是否领取的状态;也可以取消某订单。
  • 用户管理:查看和检索用户资料,积分明细,并手工给与特别贡献者额外积分奖励;也可以导出用户资料为Excel。
  • 统计分析: 可以按月统计本月注册人数,活动发起数,报名人数,签到人数等 。

概要设计

在这里插入图片描述

数据库设计


ActivityModel.DB_STRUCTURE = {_pid: 'string|true',ACTIVITY_ID: 'string|true',ACTIVITY_TITLE: 'string|true|comment=标题',ACTIVITY_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中',ACTIVITY_CHECK_REASON: 'string|false|comment=审核理由',ACTIVITY_CATE_ID: 'string|true|default=0|comment=分类',ACTIVITY_CATE_NAME: 'string|false|comment=分类冗余',ACTIVITY_CANCEL_SET: 'int|true|default=1|comment=取消设置 0=不允,1=允许,2=仅报名截止前可取消',  ACTIVITY_MAX_CNT: 'int|true|default=20|comment=人数上限 0=不限',ACTIVITY_START: 'int|false|comment=活动时间',ACTIVITY_START_DAY: 'string|false',ACTIVITY_BEGIN: 'int|true|default=0|comment=报名开始时间',ACTIVITY_STOP: 'int|true|default=0|comment=报名截止时间',ACTIVITY_ADD_MONTH: 'string|false',ACTIVITY_ORDER: 'int|true|default=9999',ACTIVITY_VOUCH: 'int|true|default=0',ACTIVITY_FORMS: 'array|true|default=[]',ACTIVITY_OBJ: 'object|true|default={}',ACTIVITY_JOIN_FORMS: 'array|true|default=[]',ACTIVITY_ADDRESS: 'string|false|comment=详细地址',ACTIVITY_ADDRESS_GEO: 'object|false|comment=详细地址坐标参数',ACTIVITY_QR: 'string|false',ACTIVITY_VIEW_CNT: 'int|true|default=0',ACTIVITY_JOIN_CNT: 'int|true|default=0',ACTIVITY_COMMENT_CNT: 'int|true|default=0',ACTIVITY_ADD_TIME: 'int|true',ACTIVITY_EDIT_TIME: 'int|true',ACTIVITY_ADD_IP: 'string|false',ACTIVITY_EDIT_IP: 'string|false',
};
ActivityJoinModel.DB_STRUCTURE = {_pid: 'string|true',ACTIVITY_JOIN_ID: 'string|true',ACTIVITY_JOIN_ACTIVITY_ID: 'string|true|comment=报名PK',ACTIVITY_JOIN_IS_ADMIN: 'int|true|default=0|comment=是否管理员添加 0/1',ACTIVITY_JOIN_CODE: 'string|true|comment=核验码15位',ACTIVITY_JOIN_IS_CHECKIN: 'int|true|default=0|comment=是否签到 0/1 ',ACTIVITY_JOIN_CHECKIN_TIME: 'int|false|default=0|签到时间',ACTIVITY_JOIN_USER_ID: 'string|true|comment=用户ID',ACTIVITY_JOIN_SCORE: 'int|true|default=0|comment=获取积分',ACTIVITY_JOIN_FORMS: 'array|true|default=[]|comment=表单',ACTIVITY_JOIN_OBJ: 'object|true|default={}',ACTIVITY_JOIN_STATUS: 'int|true|default=1|comment=状态 1=报名成功, 99=系统取消',ACTIVITY_JOIN_REASON: 'string|false|comment=取消理由',ACTIVITY_JOIN_ADD_MONTH: 'string|false',ACTIVITY_JOIN_ADD_TIME: 'int|true',ACTIVITY_JOIN_EDIT_TIME: 'int|true',ACTIVITY_JOIN_ADD_IP: 'string|false',ACTIVITY_JOIN_EDIT_IP: 'string|false',
};

核心算法

// 获取当前活动状态getJoinStatusDesc(activity) {let timestamp = this._timestamp;if (activity.ACTIVITY_STATUS == ActivityModel.STATUS.UNUSE)return '活动停止';else if (activity.ACTIVITY_START_DAY < timeUtil.time('Y-M-D'))return '活动结束';else if (activity.ACTIVITY_BEGIN > timestamp)return '报名未开始';else if (activity.ACTIVITY_STOP <= timestamp)return '报名结束';else if (activity.ACTIVITY_MAX_CNT > 0&& activity.ACTIVITY_JOIN_CNT >= activity.ACTIVITY_MAX_CNT)return '报名已满';elsereturn '报名中';}/** 浏览信息 */async viewActivity(userId, id) {let fields = '*';let where = {_id: id,ACTIVITY_STATUS: ActivityModel.STATUS.COMM,}let activity = await ActivityModel.getOne(where, fields);if (!activity) return null;ActivityModel.inc(id, 'ACTIVITY_VIEW_CNT', 1);// 判断我是否有报名let whereJoin = {ACTIVITY_JOIN_USER_ID: userId,ACTIVITY_JOIN_ACTIVITY_ID: id,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC}let activityJoin = await ActivityJoinModel.getOne(whereJoin);if (activityJoin) {activity.myActivityJoinId = activityJoin._id;activity.myActivityJoinTag = '已报名';}else {activity.myActivityJoinId = '';activity.myActivityJoinTag = '';}return activity;}/** 取得分页列表 */async getActivityList(type = 'run', {cateId, //分类查询条件search, // 搜索条件sortType, // 搜索菜单sortVal, // 搜索菜单orderBy, // 排序 page,size,isTotal = true,oldTotal}) {orderBy = orderBy || {'ACTIVITY_ORDER': 'asc','ACTIVITY_START': 'asc','ACTIVITY_ADD_TIME': 'desc'};let fields = 'ACTIVITY_ADDRESS,ACTIVITY_STOP,ACTIVITY_BEGIN,ACTIVITY_JOIN_CNT,ACTIVITY_OBJ,ACTIVITY_VIEW_CNT,ACTIVITY_TITLE,ACTIVITY_MAX_CNT,ACTIVITY_START_DAY,ACTIVITY_START,ACTIVITY_ORDER,ACTIVITY_STATUS,ACTIVITY_CATE_NAME,ACTIVITY_OBJ.cover,ACTIVITY_OBJ.score';let where = {};if (cateId && cateId !== '0') where.ACTIVITY_CATE_ID = cateId;where.ACTIVITY_STATUS = ActivityModel.STATUS.COMM; // 状态  // 进行状态let day = timeUtil.time('Y-M-D');if (type == 'run') {where.ACTIVITY_START_DAY = ['>=', day];}else {where.ACTIVITY_START_DAY = ['<', day];orderBy = {'ACTIVITY_ORDER': 'asc','ACTIVITY_START': 'desc','ACTIVITY_ADD_TIME': 'desc'};}if (util.isDefined(search) && search) {where['ACTIVITY_TITLE'] = ['like', search];} else if (sortType && util.isDefined(sortVal)) {// 搜索菜单switch (sortType) {case 'cateId': {if (sortVal) where.ACTIVITY_CATE_ID = String(sortVal);break;}case 'sort': {// 排序orderBy = this.fmtOrderBySort(sortVal, 'ACTIVITY_ADD_TIME');break;}}}let ret = await ActivityModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);if (ret) ret.type = type;return ret;}/** 取得我的报名分页列表 */async getMyActivityJoinList(userId, {search, // 搜索条件sortType, // 搜索菜单sortVal, // 搜索菜单orderBy, // 排序 page,size,isTotal = true,oldTotal}) {orderBy = orderBy || {'ACTIVITY_JOIN_ADD_TIME': 'desc'};let fields = 'ACTIVITY_JOIN_IS_CHECKIN,ACTIVITY_JOIN_REASON,ACTIVITY_JOIN_ACTIVITY_ID,ACTIVITY_JOIN_STATUS,ACTIVITY_JOIN_ADD_TIME,activity.ACTIVITY_START,activity.ACTIVITY_TITLE';let where = {ACTIVITY_JOIN_USER_ID: userId};if (util.isDefined(search) && search) {where['activity.ACTIVITY_TITLE'] = {$regex: '.*' + search,$options: 'i'};} else if (sortType) {// 搜索菜单switch (sortType) {case 'timedesc': { //按时间倒序orderBy = {'activity.ACTIVITY_START': 'desc','ACTIVITY_JOIN_ADD_TIME': 'desc'};break;}case 'timeasc': { //按时间正序orderBy = {'activity.ACTIVITY_START': 'asc','ACTIVITY_JOIN_ADD_TIME': 'asc'};break;}case 'succ': {where.ACTIVITY_JOIN_STATUS = ActivityJoinModel.STATUS.SUCC;break;}case 'cancel': {where.ACTIVITY_JOIN_STATUS = ActivityJoinModel.STATUS.ADMIN_CANCEL;break;}}}let joinParams = {from: ActivityModel.CL,localField: 'ACTIVITY_JOIN_ACTIVITY_ID',foreignField: '_id',as: 'activity',};let result = await ActivityJoinModel.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal);return result;}/** 取得我的报名详情 */async getMyActivityJoinDetail(userId, activityJoinId) {let fields = '*';let where = {_id: activityJoinId,ACTIVITY_JOIN_USER_ID: userId};let activityJoin = await ActivityJoinModel.getOne(where, fields);if (activityJoin) {activityJoin.activity = await ActivityModel.getOne(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID, 'ACTIVITY_TITLE,ACTIVITY_START');}return activityJoin;}//################## 报名 // 报名 async activityJoin(userId, activityId, forms) {// 报名是否结束let whereActivity = {_id: activityId,ACTIVITY_STATUS: ActivityModel.STATUS.COMM}let activity = await ActivityModel.getOne(whereActivity);if (!activity)this.AppError('该活动不存在或者已经结束');// 是否报名开始if (activity.ACTIVITY_BEGIN > this._timestamp)this.AppError('该活动报名报名尚未开始,请耐心等待');// 是否过了报名截止期if (activity.ACTIVITY_STOP < this._timestamp)this.AppError('该活动报名已经截止,请选择其他活动');// 人数是否满if (activity.ACTIVITY_MAX_CNT > 0) {let whereCnt = {ACTIVITY_JOIN_ACTIVITY_ID: activityId,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC}let cntJoin = await ActivityJoinModel.count(whereCnt);if (cntJoin >= activity.ACTIVITY_MAX_CNT)this.AppError('该活动报名已满,请选择其他活动');}// 自己是否已经有报名let whereMy = {ACTIVITY_JOIN_USER_ID: userId,ACTIVITY_JOIN_ACTIVITY_ID: activityId,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC}let my = await ActivityJoinModel.getOne(whereMy);if (my) {this.AppError('您已经报名成功,无须重复报名');}// 入库let data = {ACTIVITY_JOIN_ADD_MONTH: activity.ACTIVITY_ADD_MONTH,ACTIVITY_JOIN_USER_ID: userId,ACTIVITY_JOIN_ACTIVITY_ID: activityId,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC,ACTIVITY_JOIN_FORMS: forms,ACTIVITY_JOIN_OBJ: dataUtil.dbForms2Obj(forms),ACTIVITY_JOIN_CODE: dataUtil.genRandomIntString(15),}let activityJoinId = await ActivityJoinModel.insert(data);// 报名统计数量await this.statActivityJoin(activityId);// 月统计 let statService = new StatService();statService.statJoinMonth(activity.ACTIVITY_ADD_MONTH);return { activityJoinId }}async statActivityJoin(id) {// 报名数let where = {ACTIVITY_JOIN_ACTIVITY_ID: id,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC}let cnt = await ActivityJoinModel.count(where);await ActivityModel.edit(id, { ACTIVITY_JOIN_CNT: cnt });}/**  报名前获取关键信息 */async detailForActivityJoin(userId, activityId) {let fields = 'ACTIVITY_JOIN_FORMS, ACTIVITY_TITLE';let where = {_id: activityId,ACTIVITY_STATUS: ActivityModel.STATUS.COMM,}let activity = await ActivityModel.getOne(where, fields);if (!activity)this.AppError('该活动不存在');// 取出本人最近一次的填写表单let whereMy = {ACTIVITY_JOIN_USER_ID: userId,}let orderByMy = {ACTIVITY_JOIN_ADD_TIME: 'desc'}let joinMy = await ActivityJoinModel.getOne(whereMy, 'ACTIVITY_JOIN_FORMS', orderByMy);let myForms = joinMy ? joinMy.ACTIVITY_JOIN_FORMS : [];if (myForms.length == 0) {let user = await UserModel.getOne({ USER_MINI_OPENID: userId, USER_STATUS: UserModel.STATUS.COMM });if (!user) this.AppError('用户异常');// 取得我的报名信息myForms = [{ mark: 'name', type: 'text', title: '姓名', val: user.USER_NAME },{ mark: 'phone', type: 'mobile', title: '手机', val: user.USER_MOBILE },]}activity.myForms = myForms;return activity;}/** 取消我的报名 只有成功可以取消 取消即为删除记录 */async cancelMyActivityJoin(userId, activityJoinId) {let where = {ACTIVITY_JOIN_USER_ID: userId,_id: activityJoinId,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC};let activityJoin = await ActivityJoinModel.getOne(where);if (!activityJoin) {this.AppError('未找到可取消的报名记录');}if (activityJoin.ACTIVITY_JOIN_IS_CHECKIN == 1)this.AppError('该活动已经签到,无法取消');let activity = await ActivityModel.getOne(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);if (!activity)this.AppError('该活动不存在');if (activity.ACTIVITY_STATUS == ActivityModel.STATUS.UNUSE)this.AppError('该活动已停止,不能取消');if (activity.ACTIVITY_CANCEL_SET == 0)this.AppError('该活动不能取消');if (activity.ACTIVITY_CANCEL_SET == 2 && activity.ACTIVITY_STOP < this._timestamp)this.AppError('该活动已经截止报名,不能取消');await ActivityJoinModel.del(where);// 报名数量统计await this.statActivityJoin(activityJoin.ACTIVITY_JOIN_ACTIVITY_ID);// 月统计 let statService = new StatService();statService.statJoinMonth(activity.ACTIVITY_ADD_MONTH);}/** 用户自助签到 */async myJoinSelf(userId, activityId) {let activity = await ActivityModel.getOne({ _id: activityId, ACTIVITY_STATUS: ActivityModel.STATUS.COMM });if (!activity)return { ret: '活动不存在或者已经关闭' };if (activity.ACTIVITY_START_DAY != timeUtil.time('Y-M-D'))return { ret: '仅在活动当天可以签到,当前签到码日期是' + activity.ACTIVITY_START_DAY };let whereCheckin = {ACTIVITY_JOIN_USER_ID: userId,ACTIVITY_JOIN_ACTIVITY_ID: activityId,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC}let activityJoin = await ActivityJoinModel.getOne(whereCheckin);let ret = '';if (!activityJoin) {ret = '您没有本次活动报名成功的记录,请在「个人中心 - 我的活动报名」查看详情~';return { ret };}if (activityJoin.ACTIVITY_JOIN_IS_CHECKIN == 1) {ret = '本次活动您已签到,无须重复签到,请在「个人中心 - 我的活动报名」查看详情~';return { ret };}let score = activity.ACTIVITY_OBJ.score;let where = {ACTIVITY_JOIN_USER_ID: userId,ACTIVITY_JOIN_IS_CHECKIN: 0,ACTIVITY_JOIN_ACTIVITY_ID: activityId,ACTIVITY_JOIN_STATUS: ActivityJoinModel.STATUS.SUCC}let data = {ACTIVITY_JOIN_SCORE: score,ACTIVITY_JOIN_IS_CHECKIN: 1,ACTIVITY_JOIN_CHECKIN_TIME: this._timestamp,}await ActivityJoinModel.edit(where, data);if (score > 0)ret = '签到成功,获取积分' + score + '分,请在「个人中心 - 我的活动报名」查看详情~';elseret = '签到成功,请在「个人中心 - 我的活动报名」查看详情~'// 积分let delay = async () => {const ScoreService = require('./score_service.js');let scoreService = new ScoreService();let scoreData = {oid: activityJoin._id,userId,score: score,desc: '参加活动《' + activity.ACTIVITY_TITLE + '》奖励积分'}scoreService.changeScore(null, scoreData);// 月统计 let statService = new StatService();statService.statJoinMonth(activity.ACTIVITY_ADD_MONTH);}delay();return { ret };}/** 按天获取报名项目 */async getActivityListByDay(day) {let start = timeUtil.time2Timestamp(day);let end = start + 86400 * 1000 - 1;let where = {ACTIVITY_STATUS: ActivityModel.STATUS.COMM,ACTIVITY_START: ['between', start, end],};let orderBy = {'ACTIVITY_ORDER': 'asc','ACTIVITY_ADD_TIME': 'desc'};let fields = 'ACTIVITY_TITLE,ACTIVITY_START,ACTIVITY_OBJ.cover';let list = await ActivityModel.getAll(where, fields, orderBy);let retList = [];for (let k = 0; k < list.length; k++) {let node = {};node.timeDesc = timeUtil.timestamp2Time(list[k].ACTIVITY_START, 'h:m');node.title = list[k].ACTIVITY_TITLE;node.pic = list[k].ACTIVITY_OBJ.cover[0];node._id = list[k]._id;retList.push(node);}return retList;}/*** 获取从某天开始可报名的日期* @param {*} fromDay  日期 Y-M-D*/async getActivityHasDaysFromDay(fromDay) {let where = {ACTIVITY_START: ['>=', timeUtil.time2Timestamp(fromDay)],};let fields = 'ACTIVITY_START';let list = await ActivityModel.getAllBig(where, fields);let retList = [];for (let k = 0; k < list.length; k++) {let day = timeUtil.timestamp2Time(list[k].ACTIVITY_START, 'Y-M-D');if (!retList.includes(day)) retList.push(day);}return retList;}

UI设计

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码git地址

下载

这篇关于从0到1:志愿者小程序开发心得的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/506071

相关文章

JAVA程序开发参考手册

开发十年,就只剩下这套架构体系了! >>>    <!--包装数据类型 ->//javalangInteger——整数类 //bitCount方法——获取二进制补码中位的数量 int i = 10;int count = Integer.bitCount(i);System.out.print(i + "的二进制补码表示形式的1位的数量");System.out.println(count)

PHP程序开发参考手册

开发十年,就只剩下这套架构体系了! >>>    /**语句*///if语句——条件控制语句<?php$num = 2011;if(($num%4)==0&&($num%100)!=0){echo "$num".'年'."是闰年";}else{echo "$num".'年'."是平年";}?><?php$num = 2011;if(($num%4)==0&&($num%100)!=0

uniapp微信小程序开发踩坑日记:Pinia持久化报错Cannot read property ‘localStorage‘ of undefined

插件默认使用 localStorage 实现持久化,小程序端不兼容,需要替换持久化 API import { defineStore } from 'pinia'   export const useCommonStore = defineStore('pack-store', {state: (): State => ({wwInfo: {},globalData: {},timerLoc

记一次小程序开发过程

写在前面 前后两天花了大约四五个小时制作完了自己第一个小程序,当然是没法发布的,小程序的发布要求还是挺严格的:企业资质、HTTPS、审核。 先大概介绍下自己,我9年前和很多网友一样开始自学编程,这些年来什么语言都学过、什么平台都接触过,自己也做过十来个产品,所以编程基础不是很稳固但是各方面都相对比较熟悉,因此在接触小程序的时候上手比较快。 至于为什么现在选择开发小程序,原因很简单,尝尝鲜!

产品图片小程序开发:全方位指导,让产品展示更出色

想要快速开发并上线一个展示产品图片的小程序吗?乔拓云平台是您的理想选择。只需简单几步,即可打造专属的小程序平台。 首先,访问乔拓云官方网站,注册并登录您的账号。在小程序后端,您可以自由探索丰富的模板库,轻松复制心仪模板作为起点。无论是图片替换、文字修改,还是页面布局调整,一切尽在指尖掌控。选中图片,直接上传您的产品美图;选中文字,即刻调整字号、颜色,让内容更加吸引人。 不仅如此,乔

招聘面试程序员的一些心得

         最近雅虎北研解散的消息触动了各大互联网公司 HR 的神经。公司里一下子面试的任务多了起来。正好借机会总结一下自己毕业后作面试官以来参与若干场技术面试的一点心得,分享一下。        当好一个面试官其实也很不容易,不但要练就一双火眼金睛,把优秀的人才和水货在短短的面试的几十分钟里分辨出来,同时面试官本身就是候选人观察公司的一个窗口。在面试中,面试官还要注意很多接人待

微信小程序开发流程详解

目录 一 申请小程序账号 1 找到微信小程序所在链接入口 2 注册小程序号码 3 邮箱激活 4 登陆 二 下载小程序编译器 三 开发helloworld 四 上传微信公众平台小程序 五 发布 六 总结  一 申请小程序账号 1 找到微信小程序所在链接入口 打开微信小程序所在公众平台链接地址,注意不是开发平台。 https://mp.weixin.qq.com 如图

Windows自动化程序开发指南

自动化程序的概念 “自动化程序”指的是通过电脑编程来代替人类手工操作的一类程序或软件。这类程序具有智能性高、应用范围广的优点,但是自动化程序的开发难度大、所用技术杂。 本文对自动化程序开发的各个方面进行讲解。 常见的处理对象 自动化程序要处理的对象,与具体的业务需求有关。假设制作一个QQ信息群发工具,所处理对象就是QQ这个软件;如果要制作游戏外挂,处理对象就是那个游戏的界面。 常见的

小程序开发需要服务器吗?

在小程序开发的热潮中,许多人都有一个疑问:小程序开发一定要服务器吗?今天,我们就来深入探讨这个问题。 一、小程序的独特魅力 小程序以其便捷、轻量的特点,迅速在移动互联网领域占据了一席之地。无需下载安装,用户可以随时随地通过微信等平台快速打开使用。它为企业和开发者提供了一个高效的推广和服务渠道,也为用户带来了极大的便利。 二、服务器在小程序中的作用 存储数据 服务器可以存储

android4.4的Keyguard心得

在总结锁屏代码之前,有两个中心思想要铭记于心 A) KeyguardHostView就是我们最终所要展示的界面,所以不论用什么方法手段,都要将这个KeyguardHostView添加到窗口中,后续填充它,都是细节问题 B) 那么问题来了,通常我们将一个view添加到窗口中会用什么方法呢?          答案有两种 1 WindowManager.addView()  2 Lay