栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android图书馆选座系统课程设计

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android图书馆选座系统课程设计

简介
  1. 项目时间:2020年6月大专二年级的安卓课程设计
  2. 项目亮点:2D可视化的编辑地图,灵感和代码来源于贪吃蛇项目的编辑地图,曾有一家互联网公司借鉴了本小项目的编辑地图的页面功能
  3. 项目简要:
    管理员编辑图书馆楼层的2D地图,学生在图书馆2D地图上选座。
    用户管理:登录、注册、修改个人信息、更改头像 。
    楼层管理:添加、修改、编辑、删除楼层地图、保存为模板 。
    模板管理:修改、删除、生成为楼层地图 。
    选座管理:查询座位、我的座位、修改座位 。
    知识点:application+sqlite+canvas+碎片+recycleviw。
  4. 视频链接:基于Android的图书馆选座系统app_哔哩哔哩_bilibili

Android实践

2019-2020第二学期期末项目考试

班级:     安卓1802     

学号:     183052745    

姓名:     刘建杰       

得分:                   

目录

一、项目目的及意义... 0

1.1项目设计目的... 0

1.2项目设计意义... 0

二、相关技术及工具... 0

2.1 Android. 0

2.2 Sqlite. 1

2.3 Canvas. 1

三、系统设计及功能... 2

3.1功能需求... 2

3.2系统总体设计... 3

3.3模块设计... 4

3.4数据库相关设计... 4

四、系统实现及关键代码分析... 6

4.1 程序运行... 6

4.2 程序模块细分... 21

4.3主要功能关键代码分析... 24

五、收获与体会... 36

5.1 项目总结... 36

5.2 收获与体会... 37

 

一、项目目的及意义

1.1项目设计目的

1.复习、巩固Android的基础知识,进一步加深对Android的理解和掌握。

2.为我提供一个动手动脑,独立实践的机会,将课本上的知识和实际应用有机结合起来,锻炼分析我实际解决问题的能力。

3.提高我面对对象分析与设计能力以及实际项目进行综合开发能力。

1.2项目设计意义

我开发的这个项目是图书馆在线选座,学生在手机app上,对今天明天图书馆的座位进行选座,这样可以避免早起排长队,选座的界面需要2D平面地图化,不是单纯的数字座位。

对我个人来说是展示个人能力的项目,开发项目过程中是以工程为目的的开发模式,系统模块分明,功能完善。

二、相关技术及工具

2.1 Android    

       安卓(Android)是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的操作系统。主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。Android操作系统最初由Andy Rubin开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。第一部Android智能手机发布于2008年10月。Android逐渐扩展到平板电脑及其他领域上,如电视、数码相机、游戏机、智能手表等。2011年第一季度,Android在全球的市场份额首次超过塞班系统,跃居全球第一。 2013年的第四季度,Android平台手机的全球市场份额已经达到78.1%。2013年09月24日谷歌开发的操作系统Android在迎来了5岁生日,全世界采用这款系统的设备数量已经达到10亿台。

2.2 Sqlite

       SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite第一个Alpha版本诞生于2000年5月。 至2021年已经接近有21个年头,SQLite也迎来了一个版本 SQLite 3已经发布。

2.3 Canvas

      Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES 1.0。大部分2D使用的api都在android.graphics和android.graphics.drawable包中。他们提供了图形处理相关的: Canvas、ColorFilter、Point(点)和RetcF(矩形)等,还有一些动画相关的:AnimationDrawable、 BitmapDrawable和TransitionDrawable等。以图形处理来说,我们最常用到的就是在一个View上画一些图片、形状或者自定义的文本内容,这里我们都是使用Canvas来实现的。你可以获取View中的Canvas对象,绘制一些自定义形状,然后调用View. invalidate方法让View重新刷新,然后绘制一个新的形状,这样达到2D动画效果。下面我们就主要来了解下Canvas的使用方法。

三、系统设计及功能

3.1功能需求

一、项目基本内容

学生在手机app上,对今天明天图书馆的座位进行选座,这样可以避免早起排长队,像天猫电影院订座一样有座位分布图显示,地图界面需要2D平面地图化,不是单纯的数字座位。图书馆管理员在app上编辑修改图书馆楼层地图分布显示。

二、大体功能

1.用户管理

  1. 用户登录
  2. 用户注册
  3. 用户找回密码
  4. 用户修改个人信息

2.选座功能

  1. 查看每个楼层的地图
  2. 查询座位根据不同时间段剩余的座位信息
  3. 选座要选的座位
  4. 取消原先座位
  5. 选择坐多久:上午、下午、晚上
  6. 我的座位信息

3.管理员

  1. 设置楼层数目
  2. 设置一层的2D地图
  3. 编辑一层的2D地图
  4. 删除一层的2D地图
  5. 设置为第几层的2D地图

3.2系统总体设计

3.3模块设计

3.4数据库相关设计

一、实体关系分析

二、数据库表设计

表1.1 用户信息表(user)

列名

数据类型

是否允许为空

说明

uid

int

否

序号(主键)

username

text

否

昵称

userpwd

text

否

密码

role

Int

是

角色(0普通用户,1管理员)

headimg

text

是

头像

phone

text

是

电话

表1.2楼层地图表(floormap)

列名

数据类型

是否允许为空

说明

fid

int

否

序号(主键)

flayer

int

否

第几层

flayermap

varchar(15)

是

*地图数据*

表1.3座位信息表(seatinfo)

列名

数据类型

是否允许为空

说明

sid

int

否

序号(主键)

uid

int

否

用户id

fid

Int

否

楼层id

sday

Text

否

日期

stime

Int

否

时间段int值(0上午,1下午,2晚上,3无)

snumber

text

否

座位号

Sx

Int

否

X坐标

sy

Int

否

Y坐标

表1.4地图模板表(mapmoudlesql)

列名

数据类型

是否允许为空

说明

mid

int

否

序号(主键)

mname

Text

否

模板名称

mlayermap

text

否

地图数据

四、系统实现及关键代码分析

4.1 程序运行

1.用户打开程序显示“登录界面”,登录界面会显示上一个登录的用户头像,并且账号和密码已输好,下面一行左右两侧分别是忘记密码?和用户注册。

 

2.按照国际案列,先注册,点击“登录页面”的新用户注册,会跳转到“用户注册”页面。此页面和登录界面比较像,输入昵称和密码即可注册。

3.回到“登录页面”登录管理员账号

4.输入完账号密码点击登录会跳转到程序的“主页面”,此页面分为上中下3部分,上:是标题、返回、功能按钮,中:是显示各个楼层的预览图,下是3个模块切换,第一个选中的模块是楼层模块,第二个是楼层模板模块,第三个是用户模块

5.我们先点击第三个模块“用户信息页面”,对用户信息可以修改,更改头像、个人用户信息、密码、管理员可以删除指定用户等

1)用户信息页面

2)修改手机

3)修改头像可以拍照和从图库选择

4)查看所有用户

5)可以删除用户

6.用户相关模块讲完,回到选座主题。点开第一个楼层模块。  

点击右上角的+号图标可以进行对空白地图编辑,跳转到“编辑地图”页面。

此页面分两块,左边是显示由右边控制按钮操作的地图结果,右边是控制按钮。

对地图适当编辑后显示的地图。

其中,凳子图片代表座位,木头的图标代表书架,深灰色的图标代表墙壁。

可以根据不同地图的平面设置不同的地图。

7.    我们切换到普通用户,进行座位选座,首先登录普通用户账号后,楼层管理并没有编辑和删除按钮,只有选座的功能按钮。

用户选择相应楼层的座位,在“选座地图”中,手先单击座位,屏幕用绿色标记次座位,再右边控制按钮点击选座,此时将所选座位选中,并用黄色标记。

此“选择地图”页面和“编辑地图”页面大体相似,不过是为了用户选座而设计,不能对地图编辑,只能选择相应座位。

8.一个用户选座完成,另一个用户再登录选择这个座位会提示选择失败,需另选择座位。

9.选完座位,可以在“个人信息”界面的我的座位按钮点击进入“我的座位”信息界面,显示自己的座位信息
 

4.2 程序模块细分

总体目录

1.页面模块包

2.列表适配器包

3.实体bean包

4.数据库表操作包

5.canvas绘图包

6.工具包

4.3主要功能关键代码分析

编辑地图

Xml代码





  

    

         

       

          

       

    



    

        

       

          

              

              

这是编辑地图的xml代码,线性布局里面套相对布局,右边scrollview包围,左边显示地图是自定义canvas的视图,是在代码里写的,如下:

// 选择座位的地图的view,还是公用原始地图与当前地图,不过能变得东西少了,只有座位

public class ChooseSeatMapLayerView extends View{

   // 上级

   Context supercon;

   // 很重要的

   ChooseSeatMapActivity chooseactivity;

  

   private Paint yuxuanpaint;// 预选位置的paint,不填充并且绿色

   private Paint myseatpaint;// 我选择的座位

   private Paint beichooseseatpaint;// 被选择的座位

   // 地图范围

// private int mapdata[][];

   public ChooseSeatMapLayerView(Context context, AttributeSet attrs) {

      super(context, attrs);

      supercon = context;

      // 需要强制转换

      chooseactivity = (ChooseSeatMapActivity)context;

      // 直接从maplist获取地图数据

      initData();

     

      initPaint();

      Log.i("library", "ChooseSeatMapLayerView构造函数");

      // 图片大小是固定的

      mSrcRect = new Rect(0, 0, MapConstant.MapBigimgWidth, MapConstant.MapBigimgHeight);

   }

   public void initData(){

      EditMapDataUtil.getMapDataFromMapList();

      invalidate();

   }

  

   protected void initPaint(){

     

      yuxuanpaint = new Paint();

      yuxuanpaint.setColor(Color.GREEN);

      yuxuanpaint.setStyle(Paint.Style.STROKE);

      yuxuanpaint.setStrokeWidth(2);

     

      myseatpaint = new Paint();

      myseatpaint.setColor(Color.YELLOW);

      myseatpaint.setStyle(Paint.Style.STROKE);

      myseatpaint.setStrokeWidth(2);

     

      beichooseseatpaint = new Paint();

      beichooseseatpaint.setColor(Color.RED);

      beichooseseatpaint.setStyle(Paint.Style.STROKE);

      beichooseseatpaint.setStrokeWidth(2);

   }

   @Override

   public void invalidate() {

      // TODO Auto-generated method stub

      // 重新更新画布

      super.invalidate();

   }

   // 根据pix大小设置 图片显示区域

   private Rect mSrcRect;

   // 测量

   @Override

   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

      super.onMeasure(widthMeasureSpec, heightMeasureSpec);

      int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        Log.e("library", "onMeasure--widthMode-->" + widthMode);

        Log.e("library", "onMeasure--widthSize-->" + widthSize);

        Log.e("library", "onMeasure--heightMode-->" + heightMode);

        Log.e("library", "onMeasure--heightSize-->" + heightSize);

        // 通过height设置width,

        // 因为height是match ,height减去100, 要居中

        heightSize = heightSize - 100;

        // 向下取整绘制像素大小

        int pix = (int) Math.floor(heightSize / (double)MapConstant.i);

        // 设为全局的像素

        MapConstant.MapPiexl = pix;



        Log.e("library", "onMeasure--pix-->" + pix);

//        Toast.makeText(supercon, "pix:"+pix, Toast.LENGTH_SHORT).show();

       

        // 因为绘制像素向下取整,偏小了,就小下去

        heightSize = pix * MapConstant.i;

       

        // 根据绘制像素得到width

        widthSize = pix * MapConstant.j;

       

        //取最小边为控件的宽高的最小值

        setMeasuredDimension(widthSize,heightSize);

     

   }

   // 触碰函数

   

   int lasti,lastj;

   int mychoosei, mychoosej;

   @Override

   public boolean onTouchEvent(MotionEvent event) {

      // 让这个位置 /40

      mychoosej = (int) (event.getX() / MapConstant.MapPiexl);

      mychoosei = (int) (event.getY() / MapConstant.MapPiexl);

      switch(event.getAction()){

         // 手指按下

         case  MotionEvent.ACTION_DOWN:

           

            break;

         // 手指松开, 选择的位置预选

         case MotionEvent.ACTION_UP:

            // 1.先恢复之前选择的预选位置。,这个只需指定是哪一个ij就行

            if(!EditMapDataUtil.isOverLine(lasti, lastj)){

                EditMapDataUtil.recoverDataFromYuanByIJ(lasti, lastj);

            }

            // 2.当前位置变为预选,前提不是边界

            if(!EditMapDataUtil.isOverLine(mychoosei, mychoosej)){

                EditMapDataUtil.setIjBeYu(mychoosei, mychoosej);

                // 得放这里

                lasti = mychoosei;

                lastj = mychoosej;

            }

            // 更新绘图

            invalidate();

            break;

      }

//    return super.onTouchEvent(event);

      return true;

   }

   // 设为预选位置为选座,需要判断是否座位

   // 1.判断是否座位

   // 2.判断是否是不可用座位

   // 2.判断这个座位是否被其它人占了,是不能选

   // 3.判断这个时间段,这天,我是否有其它座位,询问是否取消之前座位。是:修改原来座位,如果是同一层更新view

   // 返回bean 为了通信

   public void MapDatayuToMySeat(){

      if(!EditMapDataUtil.isOverLine(mychoosei, mychoosej)){

         // 1

         int zhi = EditMapDataUtil.yuanMapData[mychoosei][mychoosej];

         //2

         if(zhi == MapConstant.SEATNO){

            Constant.commonToast(supercon, "此座位不可用");

            return;

         }

         if(zhi == MapConstant.SEAT){

            // 2

            boolean isoccur = thiseatIsOccur(mychoosei, mychoosej);

            if(!isoccur){

                // 3

                if(!isThisTimeIhadSeat(mychoosei, mychoosej)){

//                 SeatInfoBean seat = new SeatInfoBean();

//                 // 添加到数据库中,当前i,j,fid,sday,stime,等

//                 seat.setSnumber(mychoosei+""+mychoosej);

//                 seat.setSx(mychoosej);

//                 seat.setSy(mychoosei);

                  // 将预选的位置数组恢复

                   if(!EditMapDataUtil.isOverLine(mychoosei, mychoosej)){

                      EditMapDataUtil.recoverDataFromYuanByIJ(mychoosei, mychoosej);

                   }

                   chooseactivity.insertoneSeatToSqiteAndList(mychoosei, mychoosej);

                }

            }else{

                Constant.commonToast(supercon, "此座位已被占用,请选择其它");

            }

         }else{

            Constant.commonToast(supercon, "请选择座位");

         }

      }else{

         Constant.commonToast(supercon, "请先选择座位");

      }

//    invalidate();

   }

   // 判断这个座位是否被占

   public boolean thiseatIsOccur(int i, int j){

      for(int z = 0; z < MapConstant.curallseatlist.size(); z++){

         SeatInfoBean seat = MapConstant.curallseatlist.get(z);

         // x, j

         int m = seat.getSx();

         int n = seat.getSy();

         if(m == j && n == i){

            return true;

         }

      }

      return false;

   }

   // 判断我这个时间段是否有了座位

   public boolean isThisTimeIhadSeat(int i, int j){

      // 一般只有一条

      for(int z = 0; z < MapConstant.myseatlist.size(); z++){

         SeatInfoBean oldseat = MapConstant.myseatlist.get(z);

         // 弹框

         SeatInfoBean newseat = new SeatInfoBean();

         newseat.setSy(i);

         newseat.setSx(j);

         newseat.setSnumber(i+""+j);

         // 放入新楼层的fid

         newseat.setFid(MapConstant.getCurLayerZhujian());

        

         chooseactivity.generateHadSeatDialog(oldseat, newseat);

//       chooseactivity.generateHadSeatDialog(seat.getSid(), seat.getSy(), seat.getSx(), MapConstant.getLayerNumberByFid(seat.getFid()), i, j, MapConstant.getLayerNumber());

         return true;

      }

      return false;

   }

   // 绘画函数

   @Override

   protected void onDraw(Canvas canvas) {

      super.onDraw(canvas);

      // i是行,j是列

      for(int i = 0; i < MapConstant.i; i++){

         for(int j = 0; j < MapConstant.j; j++){

            int leftj = j * MapConstant.MapPiexl;

            int lefti = i * MapConstant.MapPiexl;

            int righti = j * MapConstant.MapPiexl + MapConstant.MapPiexl;

            int rightj = i * MapConstant.MapPiexl + MapConstant.MapPiexl;

            // 实时目标位置要计算的。

            Rect rc = new Rect(leftj, lefti, righti, rightj);

            // 如果是墙

            if(EditMapDataUtil.currMapData[i][j] == MapConstant.BAR){

//              canvas.drawBitmap(MapConstant.bigwallbitmap, leftj, lefti, null);

                canvas.drawBitmap(MapConstant.bigwallbitmap, mSrcRect, rc, null);

            }

            // 如果是预选地图

            else if(EditMapDataUtil.currMapData[i][j] == MapConstant.YU){

                canvas.drawRect(rc, yuxuanpaint);

            }

            // 如果是座位

            else if(EditMapDataUtil.currMapData[i][j] == MapConstant.SEAT){

//              canvas.drawBitmap(MapConstant.bigseatbitmap, leftj, lefti, null);

                canvas.drawBitmap(MapConstant.bigseatbitmap, mSrcRect, rc, null);

            }

            // 如果是地板

            else if(EditMapDataUtil.currMapData[i][j] == MapConstant.FIELD){

//              canvas.drawBitmap(MapConstant.bigfloorbitmap, leftj, lefti, null);

                canvas.drawBitmap(MapConstant.bigfloorbitmap, mSrcRect, rc, null);

            }

            // 如果是书架

            else if(EditMapDataUtil.currMapData[i][j] == MapConstant.SHUJIA){

//              canvas.drawBitmap(MapConstant.bigshujiabitmap, leftj, lefti, null);

                canvas.drawBitmap(MapConstant.bigshujiabitmap, mSrcRect, rc, null);

            }

            // 如果是不可用座位

            else if(EditMapDataUtil.currMapData[i][j] == MapConstant.SEATNO){

//              canvas.drawBitmap(MapConstant.bigseatnobitmap, leftj, lefti, null);

                canvas.drawBitmap(MapConstant.bigseatnobitmap, mSrcRect, rc, null);

            }

         }

      }

      // 绘制被学生选座的地图,循环

      // 直接根据ij将指定位置绘制, 需要判断楼层是否对应

      Paint curp = new Paint();

      for(int z = 0; z < MapConstant.curallseatlist.size(); z++){

         SeatInfoBean seat = MapConstant.curallseatlist.get(z);

         if(seat.getFid() == MapConstant.getCurLayerZhujian()){

            // 如果这个座位是我订的,换Paint

            if(seat.getUid() == MyApplication.user.getUid()){

                curp = myseatpaint;

            }else{

                curp = beichooseseatpaint;

            }

            // x, j

            int j = seat.getSx();

            int i = seat.getSy();

            int leftj = j * MapConstant.MapPiexl;

            int lefti = i * MapConstant.MapPiexl;

            int righti = j * MapConstant.MapPiexl + MapConstant.MapPiexl;

            int rightj = i * MapConstant.MapPiexl + MapConstant.MapPiexl;

            Rect rc = new Rect(leftj, lefti, righti, rightj);

            canvas.drawRect(rc, curp);

         }

      }

   }

}

主要是继承View类,然后重写onDraw绘图函数,进行绘图。

其它:编辑地图是一块比较难的地方,只要做好了这块其它功能就可以相对简单了。

五、收获与体会

5.1 项目总结 

1. 完成度80%,大体完善

2. 有许多改进和增加功能方向,比如用户可以取消预选地图、用户查看我的座位可以跳转到相应的地图界面、管理员可以拖动楼层更改楼层顺序等细节问题

3. 充分利用Canvas绘图,完成编辑地图、显示地图这难点,并适用于不同手机

4. 利用面向对象思想 使类划分细明,更容易实现效果

5. 采用了一点点mvc模式,bean代表model模型

6. 使用Sqlite表保存地图数据

5.2 收获与体会   
  1. 开发程序必须要有设计,设计项目中存在的对象,提取出属性和行为,再关联各个类之间的关系,才能写出高效和可扩展的代码
  2. 知识面太窄,需要多学习,才能扩展思维,实现更好的程序
  3. 算法以及数据结构很重要
  4. 提高写代码的效率,不能写代码10分钟,找bug1小时,太耗时间,应该先有思路再写,可在本子上先验算下
  5. 特别注意数组是引用类型的问题
  6. 提高找bug的精确性,先要理清思路,逐步跟踪,特别是可以利用调试功能 一步一步下去。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/331781.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号