课程设计做的是智能农业APP,最低版本要求是安卓9,使用的编辑器是Android Studio,主要功能点有:
(提醒一下项目命名写的贼烂,所以诞生了这个文章)
(哦对了我连项目名都打错了我就是文盲)
(下面的图片我调不好大小了,直接跳过即可)
用户打开后的登录界面如图4-1所示,
图1
图2
图3
图4
图5
图6
图7
图8
这部分主要功能是用户登录注册、保存账号密码至数据库并记住账号密码
主要涉及的java和xml文件分别有:
(为什么这部分没写呢,是因为在等豪哥有空写写doge)
1.连接服务器并显示服务器数据(此处直接用的json文件)
2.显示轮播图(后期可换为广告图)
3.设置打开风扇、水泵等操作调整温度、湿度、光照数值
(此处有两种判定,一是点击按钮进入后自动调整不适宜数值至推荐数值,二是用户手动点击打开风扇等按钮,进行温度等数值的改变,但到推荐的最值就会自动停下
主要涉及的文件有:
首页与其他界面切换使用的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,就是外观太难看
1.是四个界面中最美观的界面(这个需要放到第一条!!)
2.数据分析界面可以看到历史数据的饼状图、折线图、柱状图
3.温度湿度光照三个界面分别存储历史数据,条理清晰
主要涉及的文件有:
数据分析界面用的第三方库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();
}
1.设置用户手册,点击帮助即可分条查看
2.清除缓存功能,清除本软件运行时占用的缓存
3.版本更新功能,提示最新版本
4.退出功能,点击按钮快捷退出
主要涉及的文件有:
帮助界面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,按钮无法点按,也接收不到传参。暂时的处理是设置了默认初始值,变为静态之后显示看起来还挺正常的.....
在网上查阅资料的过程中发现许多人出现了这种情况,暂时排查的问题是用这个框架返回时不会保存原来的数据状态,谷歌官方在2018年就收到了这个bug,详情可见https://issuetracker.google.com/issues/80029773?pli=1 ,但到现在也没解决就是了。
首先,经过这次课程设计,我从整体项目选题,到设计界面,再到具体代码实现和debug,感受了一下正向开发一个项目的流程,让我收获很大。在结束这个项目,发在群文件中最后一个版本的时候,我真的很开心,先不说完成情况怎么样,可能还有挺多bug,还有可以修改的功能,但是我们已经尽力的做完了。
其次,美工帮大忙!!!先完成,在完善。这次给我很大的感触的是项目基本完成后美工的加入,真的让同样的功能,焕发出了不一样的光彩,最起码我要是用户,我愿意用了,那个按钮我愿意屈个手指点上一点。诚然,编写代码、测试都很重要,是技术力很强的一环,但是美工真的不能没有啊!!朋友们!!
最后,很感谢我的小组成员愿意选择我,在我后期急头白脸的时候还愿意和我讲话,就算一起讨论饼状图配色都很快乐,能和你们一起做一个项目,是一件让人感到庆幸的事。