赞
踩
目录
健康是一种人在身体与精神上的完全统一的状态,根据世卫组织(WHO)的统计数据,中国居民中符合世卫组织对健康的定义的人数只占总人口的15%,并且有同等比例的人群处在疾病状态中,剩下近十分之七的人处在所谓“亚健康”状态。
后疫情时代,健康已成为当下热点话题之一,健康消费也正逐步渗透到更多的生活领域当中,与此同时,市场上关于生活与健康的APP数目也逐渐增多,包括keep等健身软件,今日养生等健康知识软件,果蔬百科等生活常识软件多种类型。
而健康饮食是指恰当选择搭配合理和适当份量的食物进行食用,人体摄入各种营养素和恰当热量去维持身体细胞的正常生长,增强身体对疾病的抵抗力和维持一定的体重。.
饮食健康因为与人们日常生活的息息相关,又格外被人们所重视,食材的新鲜程度,菜谱搭配合理与否,都是影响厨房能否高效运行,家人的饮食健康的关键因素。
后疫情时代,新冠疫情防控常态化关键时期,为响应政府号召,民众减少外出,居家抗疫;屯菜也成为不可避免的生活常态。
购菜数目的增长往往带来管理的混乱,列举如下:
为做好家庭食材的管理,借助Android移动端对购买食材信息进行记录,将已购的食材录入到移动端,便捷民众整理与记忆,做到购菜时心中有数(解决问题一/三),时刻关注到菜篮中食材的新鲜程度,包括照片间的比对,购入时间的记录等等(解决问题二)。
同时该APP提供了大量正确有效的烹饪方法,在使用过程中用户能够锻炼厨艺(解决问题四),提升生活质量,进行高品质饮食。
该软件设计基于安卓(Android)进行开发,安卓(Android)是基于Linux内核基础的自由开放源代码的操作系统,适用于便捷携带的设备,如智能手持设备;随后在其发展的过程中不断扩展到其他领域中,例如平板电脑等。安卓(Android)由美国Google公司收购注资。
Android Studio 是美国Google公司推出的Android集成开发环境,整体架构与JetBrains旗下的IntelliJ IDEA类似,并提供了相似的开发调试工具。
Android采取代码与布局相分离的方式对项目进行组织,由Java语言编写内部的逻辑,XML语言修改调整布局界面。
Java:最为广泛使用的编程语言之一,也是Android开发的基础。
XML: 可扩展标记语言,标准通用标记语言的子集。
考虑到菜篮中的食材数据是本手机软件(APP)的关键数据,而保存在文件系统中的数据文件分散程度较高,程序难以读取与管理;采用本地数据库对数据进行存储,数据库技术能够帮助我们实现数据共享,避免不同用户建立独立的重复数据,从而进行数据的集中统一控制管理。
通过food_store.class文件创建数据库(见下方代码),并实现了对数据库的增删改查操作,利用TextView对查询数据的正确性进行了测试。
food_store_database helper; helper = new food_store_database(food_store.this, "basket.db", null, 1); Button create_basket = (Button) findViewById(R.id.button1); create_basket.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { helper.getWritableDatabase(); } }); |
针对食材建立了basket.db文件,并执行SQL语句创建表basket
其各字段的数据类型、索引与字段名如下
索引 | 字段名称 | 数据类型 | 解释 |
0 | id | 整型 | 主键 |
1 | name | 字符串 | 名称 |
2 | date | 字符串 | 购入时间 |
3 | price | 实数 | 价格 |
4 | time | 实数 | 保鲜时长 |
5 | timeunit | 字符串 | 保鲜时长单位 |
6 | number | 实数 | 数目 |
7 | numberunit | 字符串 | 数目单位 |
8 | classify | 字符串 | 类别 |
9 | address | 字符串 | 图片地址 |
public static final String CREATE_basket="create table basket(" +"id integer primary key autoincrement," + "name text," + "date text," + "price real," + "time real," + "timeunit text," + "number real," + "numberunit text," + "classify text," + "address text)"; db.execSQL(CREATE_basket); |
创建数据库表格表basket关键代码如下:
<style name="Theme.Basket" parent="Theme.MaterialComponents.DayNight.NoActionBar"> |
数据库建立后,建立“添加食材”页面,为了制作自定义顶部栏,修改themes.xml文件去除了页面的默认顶部栏。
利用layout代码,自定义了基础的顶部栏,在其中添加了“<”(返回)ImageButton 以实现界面跳转,从而使得页面逻辑通顺。
为实现用户调用相机拍照,并将JPG文件存储至数据库的功能,实现FileProvider类,并在res资源文件路径下建立file_paths.xml文件,从而指定文件存储路径。
调用相机拍照并进行存储的关键代码如下:
用户点击拍照后,照片路径便被存储在数据库中,而其路径下对应的图片存储在虚拟机的内存文件中。
image_uri=FileProvider.getUriForFile(food_add.this,"com.example.Basket.fileprovider",outputImage); Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri); startActivityForResult(intent, 1); |
路径例如“/storage/emulated/0/Android/data/com.example.basket/cache/id3.jpg”
通过这种组织方式,能够将不定量数目的图片有效存储,而不是依赖于Android R中的资源文件。
通过EditText文本框获取用户输入,并将信息组织为一条长记录,插入在数据库中,以ID作为主键从而保证对数据库的操作不会出错。
利用EditText的setText(null)方法与picture.setVisibility(View.GONE)方法;,将用户的输入与照片清空,便捷用户下一轮的输入。
check_classify[0] =classify[position]; //准备数据 在此查询数据库 String name; String path; //需要获取名称与图片路径 Cursor cursor_food = db.rawQuery("select * from Basket where classify =?", new String[]{check_classify[0]}); |
动态调整SQL语句,从而获取不同分类
用户进行分类的选择,因此需要实现界面左侧classify的ListView,根据用户对ListView的点击事件来获取用户选择的分类,同步更新右侧ListView展示食材:
将分类查询数据库后的获取的食材列表分配适配器中,并加载ListView:
//准备菜篮食物列表listview_ ListView listView_ = (ListView) findViewById(R.id.food_list); //准备food_list 适配器 FoodAdaper adapter_ = new FoodAdaper(basket_food_.this, R.layout.fooditem, foodList); listView_.setAdapter(adapter_); listView_.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
为使得布局清晰,使用ImageView与ListView类进行线性布局的排布;左侧为类别列表,其包含项目为“蔬菜”“豆制”“水产”“肉禽”“水果”“速食”“饮品”“辅料”“粮油”
而右侧列表为数据库中食材内容,通过对数据库的查询结果来填充。
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { int jcdun; for(jcdun=0;jcdun<listView_classify.getChildCount();jcdun++){ View tv = listView_classify.getChildAt(jcdun); tv.setBackgroundColor(Color.parseColor("#f7f7f7")); //利用整数jcdun遍历整个listview //将所有的背景色设置为默认的灰色 } listView_classify.getChildAt( position-listView_classify.getFirstVisiblePosition()) .setBackgroundColor(Color.parseColor("#fd8e0a")); //将选中的方框背景色修改为橙色 |
为了使得界面更加美化,修改并调整分类列表选中项的背景色,并保证其他未被选中项中背景色为灰色,该效果使用ListView. setOnItemClickListener与View.setBackgroundColor实现,关键代码如下:
RadioGroup radioGroup = (RadioGroup) findViewById(R.id.food_tap); RadioButton radioButton = (RadioButton) findViewById(radioGroup.getCheckedRadioButtonId()); TextView textView = (TextView) findViewById(R.id.food_top_title); radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if (checkedId == R.id.food_tab_home) { textView.setText("首页"); Intent it = new Intent(getApplicationContext(), home_.class); startActivity(it); } else if (checkedId == R.id.food_tab_book) { textView.setText("食谱"); Intent it = new Intent(getApplicationContext(), book.class); startActivity(it); } else if (checkedId == R.id.food_tab_food) { textView.setText("菜篮"); } else if (checkedId == R.id.food_tab_my) { textView.setText("我的"); Intent it = new Intent(getApplicationContext(), mine.class); startActivity(it);}}}); |
底部栏使用单选框RadioGroup实现,通过监听底部栏的状态变化(用户点击),更改顶部栏文字并进行页面的跳转。
利用XML文件修改底部栏的显示,使得被选中的页面底部栏图标变色,这在一定程度上能够提升用户的交互体验:利用Style文件定义风格,能够帮助我们降低工作量,提高代码复用率,在各个主页面的底部,都可以添加该Style的底部栏。
<style name="tab_menu_item"> <item name="android:layout_width">0dp</item> <item name="android:layout_weight">1</item> <item name="android:layout_height">match_parent</item> <item name="android:button">@null</item> <item name="android:gravity">center</item> <item name="android:textColor">@drawable/home1_text</item> <item name="android:textSize">18sp</item> </style> |
paintAxes = new Paint(); //画轴(横轴和竖轴) paintAxes.setStyle(Paint.Style.STROKE); paintAxes.setAntiAlias(true); paintAxes.setDither(true); paintAxes.setColor(ContextCompat.getColor(getContext(), colors.get(0))); paintAxes.setStrokeWidth(1); paintCoordinate = new Paint(Paint.ANTI_ALIAS_FLAG); //画坐标(文字或数值) paintCoordinate.setColor(ContextCompat.getColor(getContext(), colors.get(1))); paintCoordinate.setTextSize(22f); paintRectF = new Paint(); //画矩形 paintRectF.setColor(ContextCompat.getColor(getContext(), colors.get(2))); paintRectF.setStyle(Paint.Style.FILL); paintRectF.setDither(true); paintRectF.setAntiAlias(true); |
该功能与“添加食材”功能为同一页面的子页面,用于对当前用户添加数据构建的菜篮空间进行数据分析,利用常规的柱状图作直观展示与分类统计,分类统计实现较为简单,自数据库中进行查询,利用计数语句count进行分类计算即可,柱状图的构建较为困难,参考网络代码以及结合自身数据类型与大小进行修改调整,在画布上进行绘制,最终得到一个简单的效果。
该界面内容较少,利用简单的布局;将图片放置在上方,文字内容放置在下方,每行数据利用LinearLayout框架容纳,并在不同的线性布局框间利用外边距隔开,从而表现出整齐分布而又存在间隔的排版模式。
顶部利用常规顶部栏,利用SetText()方法修改其中TextView文字即可;而后使用ListView展示养生建议网站,每一项目使用WebView+TextView的形式实现,同步设计点击事件,当用户对具体的项目进行单击,我们能够跳转界面,利用WebView进入相应网站;此处的网站是本地保存的,并非网络实时爬取,但访问仍需要联网进行。
WebView items = (WebView)findViewById(R.id.good_health_item); // 使用Intent对象得到传过来的参数 Intent intent = getIntent(); String list = intent.getStringExtra("name"); items.loadUrl(list); |
关键代码:获取列表页面传参 loadUrl WebView
列表页使用ListView展示,详情页使用WebView展示。
在APP中内嵌记事本,帮助用户进行便捷快速记忆部分无法被记录在菜篮中的内容,以一种更加自由的方式进行备忘。
通过“我的”页面 点击“待办”跳转到内嵌记事本中,通过添加记录,实时更新数据库来保存用户输入,通过ListView展示已保存笔记
public boolean onItemLongClick(AdapterView<?> parent, View view, final intposition, long id) { AlertDialog dialog; AlertDialog.Builder builder = new AlertDialog.Builder( onenote.this).setMessage("是否删除此事件?").setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { OnenoteBean notepadBean = list.get(position); if(mSQLiteHelper.deleteData(notepadBean.getId())){ list.remove(position); adapter.notifyDataSetChanged(); Toast.makeText(onenote.this,"删除成功", Toast.LENGTH_SHORT).show(); } } }) |
支持数据库的增删操作,能够通过长按,实现对笔记的删除,关键代码如下。
对菜篮进行删查操作,支持用户删除与查找食材,当用户发觉食材过期/变质,或是已经将家中某项食材完全消耗,应当支持用户对其进行删除,从而为之后的输入腾出空间。
利用长按方法进行删除,同步执行数据库内删除与listView中remove。
查找应该属于数据库的基本操作,而对菜篮的查找,也能够为用户带来更好的用户体验,在实际APP中,查找往往是不可避免的操作。
利用SQL中like子句对数据库进行模糊匹配查询的关键代码如下:
Cursor cursor_food = db.rawQuery("select * from Basket where name like '%" + home_search_food_name.getText().toString() + "%'", null); if (cursor_food.moveToFirst()) { do{ name = (cursor_food.getString(1)); path = (cursor_food.getString(9)); File file = new File(path); if(file.exists()){ Bitmap bm = BitmapFactory.decodeFile(path); foodList.add(new food(name, bm)); }} while(cursor_food.moveToNext());} cursor_food.close(); |
查询部分并未制作新界面,而是利用控件View的可见性Visibility进行控制,当用户点击搜索按钮,对数据库进行查询并将内容填充在ListView中,并将其他内容进行隐藏,而后点击取消,将内容可见性进行交换,从而实现单页面内的工作。
通过用户在EditText中输入或是通过菜篮详情页跳转传参,将其作为关键词检索网络接口,获取各类食材的常见烹饪做法
该部分功能难点在于API接口的连接,需要一定网络请求知识与相关的JSON数据解析能力。
用于解析JSON数据的关键代码如下:
public void handleMessage(@NonNull Message msg) { // 解析JSON数据 从中获得我需要的内容! super.handleMessage(msg); if (msg.what == 0) { String strData = (String) msg.obj; str=strData; JSONObject jsonObject1; try { jsonObject1 = new JSONObject(str); // 整个JSON对象 JSONObject jsonObject2=jsonObject1.getJSONObject("result"); // 进入result部分 JSONArray arr = jsonObject2.getJSONArray("list"); // 进入result-list部分 for(int i=0;i<30;i++){ jsonObject1 = arr.getJSONObject(i); foodList.add(new book_food(jsonObject1.opt("name").toString(),jsonObject1.opt("pic").toString())); } //读取三十个结果 } catch (JSONException e) { e.printStackTrace(); } |
而网络请求的关键代码如下:(在主线程中的HTTP请求,运行时都会报错)
public void start(View view) { new Thread(new Runnable() { public void run() { String stringFromNet = getStringFromNet(); Message message = new Message(); message.what = 0; message.obj = stringFromNet; mHandler.sendMessage(message); }}).start();
ext(this,"开启子线程请求网络!",Toast.LENGTH_SHORT).show(); } |
<uses-permission android:name="android.permission.INTERNET" /> |
当然,连接API接口前需要申请网络权限,并打开虚拟机的wifi功能
在上述菜谱查询的过程中,用户若发现喜爱的食谱做法,可以建立单人的收藏夹,对喜爱的做法进行收藏,并能够通过单独的页面展示。
对此我使用了文本txt文件来做数据持久化的工作,当用户长按列表中的某类食谱,能够弹窗提示是否收藏,若用户点击收藏,则我将该记录的数据追加到txt的末尾处,并在收藏页中利用ListView进行简单展示,若用户选择取消,则弹窗关闭。
public static void savefile(String file, String conent) { BufferedWriter out = null; try { out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true))); out.write(conent); } catch (Exception e) { e.printStackTrace(); } finally { try { if(out != null){ out.close();} } catch (IOException e) { e.printStackTrace();}}} |
savefile( )将收藏内容添加到内存中txt文件中的方法,
关键代码如下
private String loadFromSDFile(String fname) { String result=null; try { File f=new File(fname); int length=(int)f.length(); byte[] buff=new byte[length]; FileInputStream fin=new FileInputStream(f); fin.read(buff); fin.close(); result=new String(buff,"UTF-8"); }catch (Exception e){ e.printStackTrace(); Toast.makeText(shoucang.this,"没有找到指定文件",Toast.LENGTH_SHORT).show(); } return result; } |
将SD卡内存中文件读取为String字符串
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。