您的当前位置:首页正文

安卓课程设计——智能农业APP

2024-11-17 来源:个人技术集锦

一、项目概述

课程设计做的是智能农业APP,最低版本要求是安卓9,使用的编辑器是Android Studio,主要功能点有:

(提醒一下项目命名写的贼烂,所以诞生了这个文章)

(哦对了我连项目名都打错了我就是文盲)

(下面的图片我调不好大小了,直接跳过即可)

二、项目成果展示

2.1项目结构:

2.2项目实现效果:

2.2.1用户登录界面

用户打开后的登录界面如图4-1所示,

 图1

图2

2.2.2首页界面

 图3

2.2.3 首页三个跳转界面(温度、湿度、光照)

 图4

2.2.4数据台界面

图5

2.2.5 数据台三个跳转界面(数据台、温度、湿度、光照)

 图6

2.2.6设置界面

图7

2.2.7 设置界面的跳转界面及功能展示:

 图8

三、项目各个部分功能介绍及实现

3.1注册登录界面

3.1.1 功能

这部分主要功能是用户登录注册、保存账号密码至数据库并记住账号密码

3.1.2 实现

主要涉及的java和xml文件分别有:

 (为什么这部分没写呢,是因为在等豪哥有空写写doge)

3.2首页

3.2.1功能

1.连接服务器并显示服务器数据(此处直接用的json文件)

2.显示轮播图(后期可换为广告图)

3.设置打开风扇、水泵等操作调整温度、湿度、光照数值

(此处有两种判定,一是点击按钮进入后自动调整不适宜数值至推荐数值,二是用户手动点击打开风扇等按钮,进行温度等数值的改变,但到推荐的最值就会自动停下

3.2.2实现

主要涉及的文件有:

首页与其他界面切换使用的Fragment(Bottom navigation activity模板),

首页的轮播图使用了fragment,参考自文章:Android fragment中广告图片轮播效果的实现

 解析JSON文件代码也在MainActivity中,主要代码是

// 读取JSON文件内容,并显示在主页TextView中
        try {
            InputStreamReader inputStreamReader = new InputStreamReader(getAssets().open("data.json"), "UTF-8");//使用IO流读取json文件内容
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String line;
        //创建字符串存储对象StringBuilder
            StringBuilder stringBuilder = new StringBuilder();
            while((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 从stringBuilder中读取了json中的数据。
            JSONObject jsonObject = new JSONObject(stringBuilder.toString());

            JSONArray jsonArray1 = jsonObject.getJSONArray("temperature");// 传入JSONObject来构造一个实例
            JSONArray jsonArray2 = jsonObject.getJSONArray("humidity");
            JSONArray jsonArray3 = jsonObject.getJSONArray("light");

            value_tem = jsonArray1.getJSONObject(jsonArray1.length()-1).getString("values");
            value_hum = jsonArray2.getJSONObject(jsonArray2.length()-1).getString("values");
            value_light = jsonArray3.getJSONObject(jsonArray3.length()-1).getString("values");
            text_tem.setText(value_tem);//给textview赋值
            text_hum.setText(value_hum);
            text_light.setText(value_light);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        }

主要参考的是:Android 解析本地json

点击按钮打开界面并传参代码:

     //按钮点击打开界面并传参
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.Ibutton_tem:
                intent = new Intent(MainActivity.this, control_temperature.class);
                intent.putExtra("data", value_tem);
                startActivityForResult(intent, 1);
                break;
            case R.id.Ibutton_light:
                intent = new Intent(MainActivity.this, light_control.class);
                intent.putExtra("data", value_light);
                startActivityForResult(intent, 2);
                break;
            case R.id.Ibutton_hum:
                intent = new Intent(MainActivity.this, air_control.class);
                intent.putExtra("data", value_hum);
                startActivityForResult(intent, 3);
                break;
        }
    } 

以温度控制界面为例,按钮所写的代码有两个基本判断:

Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 若温度小于40但按钮标志位为1 或者 温度过低 则关闭
            if (value_tem <= 40 & bol2 == 1 | value_tem <= 20) {
                Message msg = new Message();
                msg.what = 1;
                handlerStop.sendMessage(msg);
            }
        //否则继续运行
            text.setText((value_tem--)+"");
            mHandler.postDelayed(this, 800);
        }
    };
//设置停止 若小于等于20则按钮不可点击
    Handler handlerStop = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    if (value_tem <= 20) {
                        bt.setEnabled(false);
                    }
                    bt.setImageDrawable(getResources().getDrawable(R.drawable.fan1));
                    mHandler.removeCallbacks(runnable);
                    bol2 = 0;
                    bol = 1;
                    break;
            }
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_control_temperature);
//隐藏标题栏
        if (getSupportActionBar() != null){
            getSupportActionBar().hide();
        }

        text = (TextView) findViewById(R.id.control_tem_textview);
        bt = (ImageButton) findViewById(R.id.imageButton);
//接收服务器传来的值
        String s = getIntent().getStringExtra("data");
        value_tem = Integer.parseInt(s);
        text.setText(value_tem+"");
//温度大于标准值40则切换图片(关闭风扇)并设置标志位bol为0(开启)
        if (value_tem >= 40) {
            bt.setImageDrawable(getResources().getDrawable(R.drawable.fan2));
            bol = 0;
            mHandler.postDelayed(runnable, 800);//设置定时器0.8秒后调用runnable对象
        }
//若点击button 则根据标志位开启或关闭
        bt.setOnTouchListener(new View.OnTouchListener(){
            public boolean onTouch(View v, MotionEvent event) {
                if(event.getAction() == MotionEvent.ACTION_DOWN){
                    if (bol == 1) {
                        ((ImageButton)v).setImageDrawable(getResources().getDrawable(R.drawable.fan2));
                        mHandler.postDelayed(runnable, 800);
                        bol = 0;
                    }
                    else if (bol == 0) {
                        ((ImageButton)v).setImageDrawable(getResources().getDrawable(R.drawable.fan1));
                        mHandler.removeCallbacks(runnable);
                        bol = 1;
                    }

                }
                return false;
            }
        });
        

进度条使用了progressBar,就是外观太难看

3.3数据台页

3.3.1功能

1.是四个界面中最美观的界面(这个需要放到第一条!!)

2.数据分析界面可以看到历史数据的饼状图、折线图、柱状图

3.温度湿度光照三个界面分别存储历史数据,条理清晰

3.3.2实现

主要涉及的文件有:

数据分析界面用的第三方库MPAndroidChart

主要参考的:MPAndroidChart的详细使用——BarChart条形图

温度湿度光照三个界面大体一致,使用listview接收json数据并显示,listview样式为lv_set_item1.xml所定义

//和上面类似的json解析
List<Map<String,Object>> ListItems = new ArrayList<Map<String, Object>>();
        try {

            InputStreamReader inputStreamReader = new InputStreamReader(getAssets().open("data.json"), "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String line;
            StringBuilder stringBuilder = new StringBuilder();
            while((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // values
            JSONObject jsonObject = new JSONObject(stringBuilder.toString());
            JSONArray jsonArray = jsonObject.getJSONArray("temperature");
            // 获取当前时间
            Calendar calendar = Calendar.getInstance();
            SimpleDateFormat formatter = new SimpleDateFormat("YYYY.MM.dd HH:mm:ss");

            //获取json文件
            for (int i = 0; i < jsonArray.length(); i++){
                Map<String,Object> listItem = new HashMap<String,Object>();
                JSONObject object = jsonArray.getJSONObject(i);
                // time
                calendar.add(Calendar.DAY_OF_WEEK-jsonArray.length(),1);
                date = calendar.getTime();

                listItem.put("idx", (i+1)+"");
                listItem.put("time", formatter.format(date));
                listItem.put("values", object.getString("values"));
                listItem.put("danwei", "℃");
                ListItems.add(listItem);
            }

//通过适配器将数据传入listview
            simpleAdapter = new SimpleAdapter(this, ListItems, R.layout.lv_set_item1,new String[]{"idx", "time", "values", "danwei"},new int[]{R.id.idx, R.id.time, R.id.values, R.id.danwei});
            //为ListView设置Adapter适配器
            setListAdapter(simpleAdapter);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        }

3.4设置页

3.4.1功能

1.设置用户手册,点击帮助即可分条查看

2.清除缓存功能,清除本软件运行时占用的缓存

3.版本更新功能,提示最新版本

4.退出功能,点击按钮快捷退出

3.4.2实现

 主要涉及的文件有:

帮助界面manual利用了可见性 android:visibility="gone" 再设置点击textview则显示的监听器,实现点击查看隐藏内容

清除缓存功能调用了常用工具类 DataCleanManager

版本更新功能使用了PackageManager,查询当前包名 

退出按钮点击后弹出AlertDialog弹窗,询问是否关闭,点击关闭则执行exit

listview的关键代码为:

   public View onCreateView(final LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_dashboard, container, false);
        listView=view.findViewById(R.id.set_lv);
        float density=getResources().getDisplayMetrics().density;
        hiddenHeight= (int) (density*160+0.5F);

//点击事件
        listView.setAdapter(new MyAdapter());
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //如果点击帮助 跳转界面
                if (position == 0) {
                    Intent intent=new Intent(getContext(), manualActivity.class);
                    startActivity(intent);
                }else if (position==1){
                    Toast.makeText(getContext(), "清除成功!", Toast.LENGTH_SHORT).show();
                } else if (position == 2) {
                    Toast.makeText(getContext(), "已经是最新版本!", Toast.LENGTH_SHORT).show();
                }else if (position == 3) {
                final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                builder.setTitle("退出");
                builder.setMessage("确定要退出吗?");
                builder.setPositiveButton("确认退出", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        System.exit(0);
                    }
                }).setNeutralButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                    }
                }).show();
                }
            }
        });
        return view;
    }

//适配器
    private class MyAdapter extends BaseAdapter {
//        Integer[] imgs={R.mipmap.qinchuhuancun,R.mipmap.qinchuhuancun,R.mipmap.banbengengx,R.mipmap.exit};
        String[] titles={"帮助","清除缓存","版本更新","退出",};

        public MyAdapter() {
        }

        @Override
        public int getCount() {
            return titles.length;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final ViewHolder viewHolder;
            if (convertView == null) {
                convertView=getLayoutInflater().inflate(R.layout.lv_set_item,null);
                viewHolder=new ViewHolder();
//                viewHolder.imageView=convertView.findViewById(R.id.lv_set_img);
                viewHolder.tv_title=convertView.findViewById(R.id.lv_set_tv1);
                viewHolder.tv_right=convertView.findViewById(R.id.lv_set_tv2);
                convertView.setTag(viewHolder);
            }else {
                viewHolder= (ViewHolder) convertView.getTag();
            }
//            viewHolder.imageView.setImageResource(imgs[position]);
            viewHolder.tv_title.setText(titles[position]);
            if (position==0|position==3){
                Drawable drawableLeft = getResources().getDrawable(R.mipmap.gengduo);
                viewHolder.tv_right.setCompoundDrawablesWithIntrinsicBounds(null,null,drawableLeft,null);
                viewHolder.tv_right.setText("");
            }
            if (position==1){
                String totalCacheSize=null;
                //调用DataCleanManager
                DataCleanManager dataCleanManager=new DataCleanManager();
                try {
                    totalCacheSize = dataCleanManager.getCacheSize(getContext().getCacheDir());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                viewHolder.tv_right.setText("缓存:"+totalCacheSize);
            }else if (position == 2) {
                String versionName=null;
                try {
                    PackageManager packageManager=getContext().getPackageManager();
                    PackageInfo packageInfo = packageManager.getPackageInfo(getContext().getPackageName(), 0);
                    versionName = packageInfo.versionName;
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
                viewHolder.tv_right.setText("当前版本:"+versionName);
            }else if (position == 3) {
            }
            return convertView;
        }

    }
    private class ViewHolder{
        TextView tv_title,tv_right;
        ImageView imageView;
    } 

四、目前存在的bug

 目前存在切换界面后首页变为静态界面的bug,按钮无法点按,也接收不到传参。暂时的处理是设置了默认初始值,变为静态之后显示看起来还挺正常的.....

 在网上查阅资料的过程中发现许多人出现了这种情况,暂时排查的问题是用这个框架返回时不会保存原来的数据状态,谷歌官方在2018年就收到了这个bug,详情可见https://issuetracker.google.com/issues/80029773?pli=1 ,但到现在也没解决就是了。

五、想说的话

首先,经过这次课程设计,我从整体项目选题,到设计界面,再到具体代码实现和debug,感受了一下正向开发一个项目的流程,让我收获很大。在结束这个项目,发在群文件中最后一个版本的时候,我真的很开心,先不说完成情况怎么样,可能还有挺多bug,还有可以修改的功能,但是我们已经尽力的做完了。

其次,美工帮大忙!!!先完成,在完善。这次给我很大的感触的是项目基本完成后美工的加入,真的让同样的功能,焕发出了不一样的光彩,最起码我要是用户,我愿意用了,那个按钮我愿意屈个手指点上一点。诚然,编写代码、测试都很重要,是技术力很强的一环,但是美工真的不能没有啊!!朋友们!!

最后,很感谢我的小组成员愿意选择我,在我后期急头白脸的时候还愿意和我讲话,就算一起讨论饼状图配色都很快乐,能和你们一起做一个项目,是一件让人感到庆幸的事。

显示全文