非官方,仅我个人整理的文件!!!
- <由于我在武汉,所以暂时去不了学校,学校机房电脑里还有不少我的项目,所以等开学之后我再更新吧>
- 我是参加了2019全国职校技能大赛智能家居项目的其中一名选手,最终获得了国赛三等奖的成绩。
- 这个repo是我整理的所有我用到的文件,包括题库,说明文档,企想提供的Demo项目文件,我自己写的项目以及我参考过的项目,还有2017届的题目,题库以及Demo。
- 2019年抽取的是E卷作为最终试题。
- 由于现在高三,准备参加技能高考了,所以可能这个repo整理的有点仓促,但是该写的我都写了,应该还是能看的。
千万不要太过于在意代码规范,毕竟就三个小时,时间会不够用的!(除非你觉得还有多的时间)
比赛中,部分重要代码记得写注释,听说可以加分。
在Eclipse中导入企想提供的jar包,或直接将jar文件拖到项目的libs目录中,包名为com.bizideal.smarthome 包结构:
|-- BuildConfig.class |-- socket |-- Utils |--SpUtils.class |-- ConstantUtil.class |-- ControlUtils.class |-- DataCallback.class |-- DeviceBean.class |-- LoginCallback.class |-- SocketClient.class
SpUtils是一个sharedPreferences的操作类,可以直接用于存储数据,但是不要当作数据库使用!可以用于存储账号密码,使用方法: 存数据:
SpUtils.putValue(context, key, value);
- 第一个参数为上下文Context,比如MainActivity.this;
- 第二个参数为键值String,比如"account";
- 第三个参数为键值对应的值String,比如"bizideal";
取数据:
SpUtils.getValue(context, key, defValue);
- 第一个参数为上下文Context,比如MainActivity.this;
- 第二个参数为需要取的键值String,比如"account";
- 第三个参数为默认值String,比如"0";
- 最后会返回一个String值;
ControlUtils是一个控制类,用于控制智能家居并获取数据,使用前先设置用户名密码:
ControlUtils.setUser("用户名", "密码", "IP地址");
三个参数均为String;SocketClient是一个创连接用的类,先使用
SocketClient.getInstance().creatConnect();
获取实例化对象并创建连接,然后使用SocketClient.getInstance().login(new LoginCallback());
来获取返回数据,在LoginCallback内重写onEvent方法,必须创建一个runOnUiTread,因为连接是需要一定时间的,需要使用其他线程来获取结果,返回值为String类型,使用.equals();来判断是否连接成功;DeviceBean是一个组件类,作用是将功能、值和其他任何可以用代码创造的对象进行打包,方便调用。
ConstantUtil是一个不知道啥类,存了所有智能家居英文对应的id。
DataCallback是一个用来返回所有传感器数据的类。
LoginCallback是一个用来返回登陆成功与否的类。
登陆连接部分示例代码:
//将值放入SpUtil中 SpUtils.putValue( this, "Account", mAccountEt.getText().toString() ); SpUtils.putValue( this, "Password", mPasswordEt.getText().toString() ); SpUtils.putValue( this, "Ip", mIpEt.getText().toString() ); //设置账号密码 ControlUtils.setUser( mAccountEt.getText().toString(), mPasswordEt.getText().toString(), mIpEt.getText().toString() ); //进行socket连接 SocketClient.getInstance().creatConnect(); //连接回调 SocketClient.getInstance().login( new LoginCallback() { @Override public void onEvent(final String status) { runOnUiThread( new Runnable() { @Override public void run() { if (status.equals( ConstantUtil.SUCCESS )) { startActivity( new Intent( MainActivity.this, MainActivity.class)); finish(); }else { Toast.makeText( MainActivity.this, "失败!", Toast.LENGTH_SHORT ).show(); } } }); } });备注:在反编译后的库文件中,发现了好几个demo中没有使用到的方法,我感觉其中有一个可能会用到的就是
SocketClient.disConnect();
这个方法,看方法名和代码,作用应该是断开连接.
部分题目中要求滑动验证,可以直接使用SeekBar,通过设置
android:progressDrawable
属性来设置背景资源,通过android:thumb
属性来设置拖动滑块的资源;甚至还有一套题需要滑动拼图验证,也可以使用这种方法,通过android:padding
来设置内边距,就可以让滑块到指定的位置,例子:<SeekBar android:id="@+id/sb_load" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="40dp" android:minWidth="40dp" android:paddingTop="15dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:progressDrawable="@drawable/custom_progress" android:thumb="@drawable/a"/>
进度条加载是个假的,只需要达到加载动画的效果就可以了,要新开一个线程用来计数,在主线程中计数会导致UI界面卡死:
new Thread(new Runnable() { @Override public void run() { //使用for循环计数 for (int i = 0; i < 101; i++) { //使用Message向主线程传递计数 Message msg=new Message(); msg.what=i; mHandler.sendMessage(msg); mainBar.setProgress(i); //线程操作部分记得用trycatch括起来 try { //线程休眠10ms,随后继续计数 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();然后在主线程内使用Handler接收数据:
Handler mHandler=new Handler(){ public void handleMessage(Message msg){ super.handleMessage(msg); //通过其他线程传进来的Message对象,获取其中的msg的值,随后设置UI文本 loadnum.setText(msg.what+"%"); //当msg传进来的值到达100时,新建弹窗并设置弹窗按钮监听,跳转到下一个界面。 switch (msg.what) { case 100: AlertDialog.Builder dialog=new AlertDialog.Builder(LoadActivity.this); dialog.setTitle("加载完毕"); dialog.setNegativeButton("OK", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); startActivity(new Intent(LoadActivity.this,MainActivity.class)); finish(); } }); dialog.show(); break; default: break; } } };
在安卓里,系统自带sqlite数据库,在某些题目里需要使用到数据库,首先你得写一个SqlHelper继承SQLiteOpenHelper类,在这个类里面咱们得实现三种方法:构造函数,onCreate,onUpgrade:
构造函数:
//构造函数,必须得有,dbName为你需要创建的数据库名 public DatabaseHelper(Context context, String dbName, CursorFactory factory, int version) { super(context, name, factory, version); }onCreate:
//可以理解为初始化数据库,使用sql语句创建数据库 public void onCreate(SQLiteDatabase db) { db.execSQL("sql语句"); }onUpgrade:
//实现数据库升级方法,可以暂时不用管 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }来一段示例代码:MyDatabaseHelper.java
package com.example.sqlitedb; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class MyDatabaseHelper extends SQLiteOpenHelper{ //创建表UserData_1的sql语句 public static final String CREATE_ST_STRING = "CREATE TABLE UserData_1" + "(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + //序号,键值,int类型,自增 "userName TEXT," + //用户名,text类型 "passWord TEXT," + //密码,text类型 "tempData TEXT," + //温度传感器数据,text类型 "curtainStatus INT" + //窗帘状态,int类型 ")"; //构造方法,可在其他类中调用 public MyDatabaseHelper(Context context, String name,CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { //执行sql语句 db.execSQL(CREATE_ST_STRING); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }然后在主类中使用这个MyDatabaseHelper类:
//新建一个MyDatabaseHelper对象,main_data是数据库名,factory使用默认factory构造,所以可为null,version随便写一个就行 MyDatabaseHelper dbHelper = new MyDatabaseHelper(this, "main_data", null, 1); //获取可读写数据库 SQLiteDatabase dbMain = dbHelper.getWritableDatabase();可能需要自己实现数据库的增删改查功能,我自己写了一个SqliteDatabaseUtil工具类,可以作为参考:
import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Message; import android.util.Log; import android.widget.SimpleCursorAdapter; import com.icezx.subbook.MainActivity; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author Yili(yili) * @description * @package com.icezx.subbook * @date 2020-02-12 */ public class SqliteDatabaseUtil { private static final String TAG = "SqliteDatabaseUtil"; /** * Sqlite数据库增加记录 * * @param db 数据库对象 * @param tableName 数据表名字 * @param keys 要插入的数据的列的名字 * @param values 与列对应的值 */ public static void insertData(SQLiteDatabase db, String tableName, String[] keys, String[] values) { ContentValues mContentValues = new ContentValues(); for (int i = 0; i < keys.length; i++) { mContentValues.put(keys[i], values[i].trim()); } db.insert(tableName, null, mContentValues); mContentValues.clear(); Log.i(TAG, "执行了增加记录操作"); } /** * Sqlite数据库删除记录,根据_id删除 * * @param adapter SimpleCursorAdapter适配器对象适配器对象 * @param db 数据库对象 * @param tableName 数据表名字 * @param positon 在列表中对应的位置 */ public static void deleteData(SimpleCursorAdapter adapter, SQLiteDatabase db, String tableName, int positon) { Cursor mCursor = adapter.getCursor(); mCursor.moveToPosition(positon); int itemId = mCursor.getInt(mCursor.getColumnIndex("_id")); db.delete(tableName, "_id=?", new String[]{itemId + ""}); mCursor.close(); Log.i(TAG, "执行了删除记录操作"); } /** * Sqlite数据库修改记录 * * @param adapter SimpleCursorAdapter适配器对象 * @param db 数据库对象 * @param tableName 数据表名字 * @param positon 在列表中对应的位置 * @param keys 要插入的数据的列的名字 * @param values 与列对应的值 */ public static void updateData(SimpleCursorAdapter adapter, SQLiteDatabase db, String tableName, int positon, String[] keys, String[] values) { Cursor mCursor = adapter.getCursor(); mCursor.moveToPosition(positon); int itemId = mCursor.getInt(mCursor.getColumnIndex("_id")); ContentValues mContentValues = new ContentValues(); for (int i = 0; i < keys.length; i++) { mContentValues.put(keys[i], values[i].trim()); } db.update(tableName, mContentValues, "_id=?", new String[]{itemId + ""}); mContentValues.clear(); mCursor.close(); Log.i(TAG, "执行了修改记录操作"); } /** * Sqlite数据库查询记录,返回一个List对象 * * @param db 数据库对象 * @param tableName 数据表名字 * @param whichColumn 想要查询的列,这是一个String[] * @param keyWord 想要查询的关键词 * @param whichSelect 想要查询的这个关键词对应的列名字 */ public static List<Map<String, String>> queryData1(SQLiteDatabase db, String tableName, String[] whichColumn, String keyWord, String whichSelect) { List<Map<String, String>> mStringList = new ArrayList<>(); //第二个参数是你需要查找的列 //第三和第四个参数确定是从哪些行去查找第二个参数的列 Cursor mCursor1 = db.query(tableName, whichColumn, whichSelect + "=?", new String[]{keyWord}, null, null, null); Log.d(TAG, "得到了" + mCursor1.getCount() + "条结果"); if (mCursor1.getCount() > 0) { mStringList.clear(); //游标总是在查询到的上一行 while (mCursor1.moveToNext()) { Map<String, String> mMap = new HashMap<>(); for (int i = 0; i < whichColumn.length; i++) { mMap.put(whichColumn[i], mCursor1.getString(mCursor1.getColumnIndex(whichColumn[i]))); } mStringList.add(mMap); } mCursor1.close(); } else { mStringList.clear(); Map<String, String> mMap = new HashMap<>(); mMap.put(whichSelect, "无结果"); mStringList.add(mMap); } Log.d(TAG, "查询结果" + mStringList.toString()); return mStringList; } }数据库这部分可能会有些复杂,慢慢来吧。
- 获取登陆或数据回调
这一部分我遇到过坑,所以一定要写!!!
在整个App中,获取回调的时候,这些代码一定只能出现一次:
这是获取登陆状态回调:
SocketClient.getInstance().login( new LoginCallback() { @Override public void onEvent(final String status) { runOnUiThread( new Runnable() { @Override public void run() { if (status.equals( ConstantUtil.SUCCESS )) { Toast.makeText( MainActivity.this, "重连成功!", Toast.LENGTH_SHORT ).show(); }else if (status.equals( ConstantUtil.FAILURE )) { Toast.makeText( MainActivity.this, "重连失败!", Toast.LENGTH_SHORT ).show(); }else { Toast.makeText( MainActivity.this, "重连中!", Toast.LENGTH_SHORT ).show(); } } });这是获取传感器数据回调:
ControlUtils.getData(); SocketClient.getInstance().getData(new DataCallback<DeviceBean>() { @Override public void onResult(final DeviceBean bean) { // TODO Auto-generated method stub runOnUiThread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub if (!TextUtils.isEmpty(bean.getTemperature())) { wendu.setText(bean.getTemperature()); } if (!TextUtils.isEmpty(bean.getHumidity())) { shidu.setText(bean.getHumidity()); } if (!TextUtils.isEmpty(bean.getGas())) { ranqi.setText(bean.getGas()); } if (!TextUtils.isEmpty(bean.getIllumination())) { guangzhao.setText(bean.getIllumination()); } if (!TextUtils.isEmpty(bean.getPM25())) { pm25.setText(bean.getPM25()); } if (!TextUtils.isEmpty(bean.getAirPressure())) { qiya.setText(bean.getAirPressure()); } if (!TextUtils.isEmpty(bean.getSmoke())) { yanwu.setText(bean.getSmoke()); } if (!TextUtils.isEmpty(bean.getCo2())) { co2.setText(bean.getCo2()); } if (!TextUtils.isEmpty( bean.getStateHumanInfrared() ) && bean.getStateHumanInfrared().equals( ConstantUtil.CLOSE )){ renti.setText( "无人" ); }else { renti.setText( "有人" ); } if (!TextUtils.isEmpty( bean.getLamp() ) && bean.getLamp().equals( ConstantUtil.CLOSE )) { shedeng.setChecked( false ); shedeng.setBackgroundResource(R.drawable.shedeng); }else { shedeng.setChecked( true ); shedeng.setBackgroundResource(R.drawable.shedeng_press); } if (!TextUtils.isEmpty( bean.getFan() ) && bean.getFan().equals( ConstantUtil.CLOSE )) { fengshan.setChecked( false ); fengshan.setBackgroundResource(R.drawable.fengshan); }else { fengshan.setChecked( true ); fengshan.setBackgroundResource(R.drawable.fengshan_press); } if (!TextUtils.isEmpty( bean.getCurtain() ) && bean.getCurtain().equals( ConstantUtil.CHANNEL_3 )) { Toast.makeText(MainActivity.this, "窗帘开", Toast.LENGTH_SHORT).show(); }else if (!TextUtils.isEmpty( bean.getCurtain() ) && bean.getCurtain().equals( ConstantUtil.CHANNEL_1 )) { Toast.makeText(MainActivity.this, "窗帘停", Toast.LENGTH_SHORT).show(); }else if (!TextUtils.isEmpty( bean.getCurtain() ) && bean.getCurtain().equals( ConstantUtil.CHANNEL_2 )) { Toast.makeText(MainActivity.this, "窗帘关", Toast.LENGTH_SHORT).show(); } if (!TextUtils.isEmpty( bean.getWarningLight() ) && bean.getWarningLight().equals( ConstantUtil.CLOSE )) { baojing.setChecked( false ); baojing.setBackgroundResource(R.drawable.baojing); }else { baojing.setChecked( true ); baojing.setBackgroundResource(R.drawable.baojing_press); } } }); } });这些代码在整个App中一定只能出现一次,不同界面间的数据传输请使用全局变量的方法(java的全部变量可以自行百度),比如我在“MainActivity”中使用了这些代码获取回调数据,我想在“SecondActivity”中也想使用“MainActivity”中获取到的数据,那就在“MainActivity”中传值给全局变量的类。千万不能重复使用上面的代码,那样会导致每刷新一次数据,界面就会重载一次!
- 绘图界面
具体项目请参考:
需要自定义View,然后使用Canvas绘图,可供参考的代码:
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; public class MyView extends View { public MyView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } // 默认边距 private int Margin = 40; // 原点坐标 private int Xpoint; private int Ypoint; // X,Y轴的单位长度 private int Xscale = 20; private int Yscale = 20; // X,Y轴上面的显示文字 private String[] Xlabel = { "0", "1", "2", "3", "4", "5", "6", "7", "8" }; private String[] Ylabel = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }; // 标题文本 private String Title; // 曲线数据 private int[] Data = { 1,2,3,4,5,6,7,8,9 }; public MyView(Context context, String[] xlabel, String[] ylabel, String title, int[] data) { super(context); this.Xlabel = xlabel; this.Ylabel = ylabel; this.Title = title; this.Data = data; } public MyView(Context context) { super(context); } // 初始化数据值 public void init() { Xpoint = this.Margin; Ypoint = this.getHeight() - this.Margin; Xscale = (this.getWidth() - 2 * this.Margin) / (this.Xlabel.length - 1); Yscale = (this.getHeight() - 2 * this.Margin) / (this.Ylabel.length - 1); } public int getMargin() { return Margin; } public void setMargin(int margin) { Margin = margin; } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); Paint p1 = new Paint(); p1.setStyle(Paint.Style.STROKE); p1.setAntiAlias(true); p1.setColor(Color.BLACK); p1.setStrokeWidth(2); init(); this.drawXLine(canvas, p1); this.drawYLine(canvas, p1); // this.drawTable(canvas); this.drawData(canvas); } // // 画表格 // private void drawTable(Canvas canvas) { // Paint paint = new Paint(); // paint.setStyle(Paint.Style.STROKE); // paint.setColor(Color.GRAY); // Path path = new Path(); // PathEffect effects = new DashPathEffect(new float[] { 5, 5, 5, 5 }, 1); // paint.setPathEffect(effects); // // 纵向线 // for (int i = 1; i * Xscale <= (this.getWidth() - this.Margin); i++) { // int startX = Xpoint + i * Xscale; // int startY = Ypoint; // int stopY = Ypoint - (this.Ylabel.length - 1) * Yscale; // path.moveTo(startX, startY); // path.lineTo(startX, stopY); // canvas.drawPath(path, paint); // } // // 横向线 // for (int i = 1; (Ypoint - i * Yscale) >= this.Margin; i++) { // int startX = Xpoint; // int startY = Ypoint - i * Yscale; // int stopX = Xpoint + (this.Xlabel.length - 1) * Xscale; // path.moveTo(startX, startY); // path.lineTo(stopX, startY); // paint.setColor(Color.DKGRAY); // canvas.drawPath(path, paint); // paint.setColor(Color.WHITE); // paint.setTextSize(this.Margin / 2); // canvas.drawText(this.Ylabel[i], this.Margin / 4, startY // + this.Margin / 4, paint); // } // } // 画横纵轴 private void drawXLine(Canvas canvas, Paint p) { canvas.drawLine(Xpoint, Ypoint, this.Margin, this.Margin, p); //canvas.drawLine(Xpoint, this.Margin, Xpoint - Xpoint / 3, this.Margin+ this.Margin / 3, p); //canvas.drawLine(Xpoint, this.Margin, Xpoint + Xpoint / 3, this.Margin+ this.Margin / 3, p); } private void drawYLine(Canvas canvas, Paint p) { canvas.drawLine(Xpoint, Ypoint, this.getWidth() - this.Margin, Ypoint,p); //canvas.drawLine(this.getWidth() - this.Margin, Ypoint, this.getWidth()- this.Margin - this.Margin / 3, Ypoint - this.Margin / 3, p); //canvas.drawLine(this.getWidth() - this.Margin, Ypoint, this.getWidth()- this.Margin - this.Margin / 3, Ypoint + this.Margin / 3, p); } // 画数据 private void drawData(Canvas canvas) { Paint p = new Paint(); p.setAntiAlias(true); p.setColor(Color.BLACK); p.setTextSize(this.Margin / 2); // 纵向线 for (int i = 1; i * Xscale <= (this.getWidth() - this.Margin); i++) { int startX = Xpoint + i * Xscale; canvas.drawText(this.Xlabel[i], startX - this.Margin / 4, this.getHeight() - this.Margin / 4, p); canvas.drawCircle(startX, calY(Data[i]), 4, p); canvas.drawLine(Xpoint + (i - 1) * Xscale, calY(Data[i - 1]), startX, calY(Data[i]), p); } } /** * * @param y * @return */ private int calY(int y) { int y0 = 0; int y1 = 0; // Log.i("zzzz", "y:"+y); try { y0 = Integer.parseInt(Ylabel[0]); // Log.i("zzzz", "y0"+y0); y1 = Integer.parseInt(Ylabel[1]); // Log.i("zzzz","y1"+y1); } catch (Exception e) { // Log.i("zzzz", "string changed is err"); return 0; } try { // Log.i("zzzz", "返回数据"+(Ypoint-(y-y0)*Yscale/(y1-y0)) ); return Ypoint - ((y - y0) * Yscale / (y1 - y0)); } catch (Exception e) { // Log.i("zzzz", "return is err"); return 0; } } }然后在layout文件中使用这些代码来使用这个自定义View:
<com.example.testview.MyView android:layout_width="wrap_content" android:layout_height="wrap_content"> </com.example.testview.MyView>这只是参考代码,实际在项目中使用的话,需要使用到大量变量,数组,然后在主线程中设置定时,隔一段时间刷新一次,数组中填充新数据,替换掉旧数据。
绘图这部分可能需要很多时间,所以尽力完成吧,要是时间实在不够就别做了。(毕竟我就没时间做了.....)
-
在eclipse的设置中,记得打开auto-import,详细:百度经验-Eclipse自动导包
-
也请记住Eclipse的部分常用快捷键:菜鸟教程-Eclipse快捷键
-
在这两个目录下:【2017年智能家居安卓Demo,2019年智能家居安卓Demo】是企想提供的安卓Demo文件,提供最基础的智能家居控制功能,可直接导入eclipse。
-
在这两个目录下:【2017年题库,2019年题库】是题库文件,在正式比赛时会由比赛组委会从中随机抽取一套,作为比赛试题,2017年我不知道抽取了哪一套,2019年抽取的是E卷作为最终试题,我感觉不算特别难,就是时间不咋够。。。
-
在这个目录下:2019年自己写的项目文件是我自己写的项目(有几个项目没找着,等开学了去学校电脑上再找找),要是嫌弃我的代码很辣鸡也能理解哈哈哈,项目名字对应的就是那一套题,基本完全按照试题要求的来,可直接导入eclipse。
-
在这个目录下:2019年我参考过的项目文件是我参考过的项目文件,解释一下每个项目大概是什么内容吧,可直接导入eclipse。
- Login_Sqlite是sqlite数据库的相关使用,主要被我用于实现账号登录,数据记录的相关功能。
- A-J套试题参考Demo在这个目录下的DemoA-DemoJ项目是当时我们的参考项目Demo,实现了绝大部分试题要求的功能(听说是找人做的),我当时主要参考了Fragment的使用,生命周期管理,Canvas的绘制等等,这些项目都完整的实现了试题要求的效果,但最大的问题就是,这些项目并不能正常控制智能家居设备,也就是说,做这套题的人应该并没有实体的设备,仅从理论上做出了这些项目,但这些项目对我帮助也算是很大的了,感谢。
- 绘制折线图顾名思义,这个项目是我用来实现折线图绘制的参考。
-
其他文件这个是其他的一些文件,比如说明文档啥的,还有lib库文件。
Email: [email protected]
QQ:2510355993 |
---|
好了,也再没有什么好说的了,希望这个repo可以被用上吧,最后祝以后参加这个比赛的同学可以取得一个理想的成绩咯,干巴爹!