2019年全国技能大赛“智能家居安装与维护”B卷 Android部分

2019年智能家居安装与维护B卷 Android部分

1、登录部分

1.1、实现背景色为半透明灰色#66666666

该功能使用drawable下xml中layer_list,可以支持两层,实现背景透明灰色。实现代码如下:

drawable--->Transparent_login.xml

<<!--?xml version="1.0" encoding="utf-8"?-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/b_background">	<!--背景层,最底层-->
   <item>
       <shape android:shape="rectangle"> <!--填充的形状,默认是矩形-->
           <solid android:color="" #66666666"=""><!--指定内部填充颜色-->
       </solid></shape>
    </item>
</item></layer-list>

1.1.1 drawable关键字介绍

关键字 用途
shape 图形,shape的形状,默认为矩形,可以设置为矩形(rectangle)、椭圆形(oval)、线性形状(line)、环形(ring)
solid 指定填充的颜色,搭配颜色一起使用
stroke 描边,可以定义描边的宽度、颜色、虚线(dashGap间隔)和实线
gradient 定义渐变色,可以定义两色、或者三色见渐变
corners 圆角属性

1.1.2颜色透明表

颜色透明表

1.2、滑块解锁

界面添加SeekBar实现滑动解锁

<seekbar android:progress="0" android:max="100" android:progressdrawable="<!--设置背景的seekbar的背景颜色透明--">
		android:background= <!--设置背景图片-->
		android:maxHeight=""<!--thumb(拇指)滑动的图标的大小最大高度-->
		android:thumb= <!--thumb(拇指)滑动的图标-->
		android:thumbOffset= <!--滑动按钮开始的位置-->
/>

Activity中的设置Seekbar的监听事件OnSeekBarChangeListener

  • 达到最大值解锁

  • 未达到最大值,自动归位置。

    mSeekLockBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    			@Override
    			public void onStopTrackingTouch(SeekBar seekBar) {
    				// TODO Auto-generated method stub
    				if(seekBar.getProgress()<100){
    					seekBar.setProgress(0);
    					Toast.makeText(LoginActivity.this, "解锁失败", 1).show();
    				}
    			}
    			@Override
    			public void onStartTrackingTouch(SeekBar seekBar) {
    				// TODO Auto-generated method stub
    				
    			}
    			
    			@Override
    			public void onProgressChanged(SeekBar seekBar, int progress,
    					boolean fromUser) {
    				// TODO Auto-generated method stub
    				if(progress==100){
    					Toast.makeText(LoginActivity.this, "解锁成功", 1).show();
    				}else {
    					
    				}
    			}
    		});
    

2、加载部分

2.1进度条加载颜色

<!--?xml version="1.0" encoding="utf-8"?-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <clip> <!--使用裁剪-->
            <shape>
                <solid android:color="#0099FF"><颜色填充>
            </solid></shape>
        </clip>
    </item>
</layer-list>

2.2内部类用于ProgressBar加1.

class myProgress implements Runnable{
		@Override
		public void run() {
			// TODO Auto-generated method stub
			mProgressBar.setProgress(mProgressBar.getProgress()+1);
			Log.d("TAG", mProgressBar.getProgress()+"");
			mHandler.sendEmptyMessage(0); //发送一个空消息,给HandlerMessage。
		}
		
	}

2.2.2HandlerMessage处理消息,更新界面

public Handler mHandler=new Handler(){
		public void handleMessage(android.os.Message msg) {
			if(mProgressBar.getProgress()<100){
				mHandler.postDelayed(mProgress, 10L);
				mTvProgress.setText(mProgressBar.getProgress()+"%");
			}
		};
	};

2.2.3在onCreate中发送空消息

mHandler.sendEmptyMessage(0); //发送一个空消息,给HandlerMessage。

2.3 弹窗

AlertDialog设置弹窗

AlertDialog.Builder builder=new AlertDialog.Builder(ProgressActivity.this);
builder.setTitle("登录成功").setMessage("欢迎回来").setNeutralButton("ok",new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        // TODO Auto-generated method stub
        Toast.makeText(ProgressActivity.this,"进入系统", 1).show();
    }
}).show();

3主界面部分

实现四个界面Fragment在一个activity中来回切换。

3.1界面滑动

3.1.1建立4个Fragment。

继承import android.support.v4.app.Fragment;包下的Fragment。重写onCreateView方法

需要重写的方法

  • onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)

    获取上下文

    加载布局文件

    返回view对象

@SuppressLint("NewApi")
public class RoomControlFragment extends Fragment {
		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
             //布局文件转成View 
 			//参数1:Fragment需要加载的布局文件 
  			//参数2:加载layout的父ViewGroup 
  			//参数3:是否返回父ViewGroup对象,false为不 
			View view=inflater.inflate(R.layout.manager_layout, container,false);
			return view;
		}
}

3.1.2 新建ViewPager容器的适配器Adapter

创建适配器,新建类(MyAdapter),继承FragmentPagerAdapter

viewpager 滑动原理FragmentPagerAdapter、FragmentPagerStateAdapter

解决ViewPager滑动中视图被销毁

重写的方法说明

  • 构造函数 ViewPagerMyAdapter(FragmentManager fm,List listfragment)

    args1:用于得到Fragment的管理器

    args2:获得存放4个Fragment的list集合

  • Fragment getItem(int arg0) 用于创建Fragment

  • int getCount() 要创建几个页面

public class ViewPagerMyAdapter extends FragmentPagerAdapter {
	List<fragment> listFragment;
	public ViewPagerMyAdapter(FragmentManager fm,List<fragment> listfragment) {
		super(fm);
		// TODO Auto-generated constructor stub
		this.listFragment=listfragment;
	}
	@Override
	public Fragment getItem(int arg0) {
		// TODO Auto-generated method stub
        //返回页面内容 
		return listFragment.get(arg0);
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
        //一共多少个页面 
		return listFragment.size();
}

3.1.3 MainActivty中添加适配器

  • 添加四个Fragment到list集合中。
private List<fragment> listFragments;
listFragments=new ArrayList<fragment>();
listFragments.add(new RoomManagerFrgment());
listFragments.add(new RoomControlFragment());
listFragments.add(new RoomLinkAgeFragment());
listFragments.add(new RoomTemperateFragment());
  • 将集合初始化到ViewPagerAdapter适配器
ViewPagerMyAdapter myAdapter=new ViewPagerMyAdapter(getSupportFragmentManager(),listFragments);
mViewPager.setAdapter(myAdapter);

//注意:
有时在使用getSupportFragmentManager()时,发现已经添加了v-4包支持,但是依旧找不到getSupportFragmentManager(),此时要注意是否是在Activity中使用,因为Activity中并没有此方法的定义,必须是继承FragmentActivity或者AppCompatActivity,然后使用。
  • 添加ViewPager的监听事件

    听实现接口OnPageChangeListener

    ViewPager,添加监听方法 setOnPageChangeListener() 方法已经被 AS 移除了,建议使用 addOnPageChangeListener() 方法。

public class MainActivity extends  FragmentActivity implements ViewPager.OnPageChangeListener {
	private ViewPager mViewPager;
    protected void onCreate(Bundle savedInstanceState) {
		 super.onCreate(savedInstanceState);
		 setContentView(R.layout.activity_main);
		 mViewPager=(ViewPager) findViewById(R.id.mainViewPager);
          mViewPager.setOnPageChangeListener(this);
          mViewPager.setOffscreenPageLimit(3); //设置显示页,左边和右边最大预加载的页数,默认为1,防止视图被销毁。
    }
    @Override
	public void onPageScrollStateChanged(int arg0) {
		// TODO Auto-generated method stub
        //state:滑动状态 
		//当页面停止的时候该参数为0,页面开始滑动的时候变成了1, 
		//当手指从屏幕上抬起变为了2(无论页面是否从1跳到了2),当页面静止后又变成了0 
		//只在0,1,2三个数字之间切换. 
		
	}

	@Override
	public void onPageScrolled(int arg0, float arg1, int arg2) {
		// TODO Auto-generated method stub
        // position-点击滑动的界面的位置 
		// positionOffset-点击页面占整个屏幕的百分比 
		// positionOffsetPixels-屏幕像素的位置 
		
	}

	@Override
	public void onPageSelected(int arg0) {
		// TODO Auto-generated method stub
        //滑动到第几页 
		Toast.makeText(MainActivity.this,"第"+(arg0+1)+"页",Toast.LENGTH_SHORT).show();
	}
}

3.1.4 按钮跳转

点击按钮让跳转到对应的fragment。

//往布局文件中的button按钮添加点击事件方法。
`android:onClick="operateRoom"`
//在RoomManagerFragment中写operateRoom方法
 public void operSateRoom(View v){
    swtich(v.getId()){
        case R.id.btn_manager:
        	mViewPager.setCurrentItem(0,true);//设置对应的页面
        break;
        case R.id.btn_control:
        	mViewPager.setCurrentItem(1,true);
        break;
        case R.id.btn_linkage:
        	mViewPager.setCurrentItem(2,true);
        break;
        case R.id.btn_temperate:
        	mViewPager.setCurrentItem(3,true);
        break;
    }
}

3.1.5 总结

PS:以上很多都使用了 android.support.v4.* 这个包下的类,是为了向下兼容

ViewPager有三个适配器,适配不同的数据源。

1、继承自PagerAdapter的适配器适配List<view 的数据="" 2、继承自fragmentadapter的适配器适配list<fragment="" 3、继承自fragmentstateadapter的适配器适配list<fragment="" 其中后两种的区别是:2没有创建和销毁的过程,3有="" https:="" blog.csdn.net="" zbq1334="" article="" details="" 78632454="" ##="" 4="" 添加管理房间="" 使用listview控件、baseadapter适配器。添加16个房间。="" ###="" 4.1="" 添加listview控件、以及写listview中每个item中布局文件。="" 在activity_main.xml中添加listview控件="" ```xml="" <listview="" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/lv_room">


写`ListView`中每个`Item`的布局文件`list_view_layout.xml`

```xml
<!--一个item(一行)四个房间,使用LinearLayout布局-->
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal">
    <textview android:layout_width="0dp" android:layout_height="100dp" android:layout_weight="1" android:gravity="center" android:id="@+id/room_id_1">
    <textview android:layout_width="0dp" android:layout_height="100dp" android:layout_weight="1" android:id="@+id/room_id_2" android:gravity="center">
    <textview android:layout_width="0dp" android:layout_height="100dp" android:layout_weight="1" android:id="@+id/room_id_3" android:gravity="center">
    <textview android:layout_width="0dp" android:layout_height="100dp" android:layout_weight="1" android:id="@+id/room_id_4" android:gravity="center">
</textview></textview></textview></textview></linearlayout>

4.2 ListView适配器

重写一个类,继承BaseAdapter类(会这一个数据适配器就行了,通用型),初始化16个房间

  • public int getCount(): 适配器中数据集的数据个数;

  • public Object getItem(int position): 获取数据集中与索引对应的数据项;

    • public long getItemId(int position): 获取指定行对应的ID;
  • public View getView(int position,View convertView,ViewGroup parent): 获取每一行Item的显示内容。加载视图

public class ListViewMyAdapter extends BaseAdapter{
    //主要重写其中的四个方法
    //public int getCount(): 适配器中数据集的数据个数;
    //public Object getItem(int position): 获取数据集中与索引对应的数据项;
    //public long getItemId(int position): 获取指定行对应的ID;
    //public View getView(int position,View convertView,ViewGroup parent): 获取每一行Item的显示内容。
    
    //1、需要一个String类型的数组list集合。用于存放每一层的房间。
    private List<string []=""> roomIdList;
    private String[] flootRoomId
    //2、需要一个上下文,将这个适配器布置到哪个场景中。
    private Context mcontext;
    public ListViewMyAdapter(Context context,List<string[]> room_id) {
		// TODO Auto-generated constructor stub
		this.mcontext=context;
		this.mInflater=LayoutInflater.from(context);//加载使用场景的布局
		this.mroom_id_Items=room_id;
	}
    @Override
	public int getCount() {
		// TODO Auto-generated method stub
        //设置一共有多少个items.
		return mroom_id_Items.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		//获取数据集中与索引对应的数据项;
		return null;
	}

	@Override
	public long getItemId(int position) {
		
		// TODO Auto-generated method stub
		//获取指定行对应的ID;
		return 0;
	}
    // 3 建立内部类ViewHolder存放item布局文件中的控件信息。
    static class ViewHolder{
		public TextView mTvRoom1, mTvRoom2, mTvRoom3, mTvRoom4;
	}
    // 4 类似activity中oncreat方法。初始化控件。
    @Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		ViewHolder holder=null;
		final int floor=position;
		if(convertView==null){
             //4.1将布局文件转化为View对象
			convertView=mInflater.inflate(R.layout.list_view_room, null);
			holder=new ViewHolder();
            /**
        	 * 找到item布局文件中对应的控件 
        	 */
			holder.mTvRoom1=(TextView) convertView.findViewById(R.id.room_id_1);
			holder.mTvRoom2=(TextView) convertView.findViewById(R.id.room_id_2);
			holder.mTvRoom3=(TextView) convertView.findViewById(R.id.room_id_3);
			holder.mTvRoom4=(TextView) convertView.findViewById(R.id.room_id_4);
			// 4.3 把holder对象传递到view的tag中,这样下次就不会在实例化。
            convertView.setTag(holder);
		}else {
			holder=(ViewHolder) convertView.getTag();
		}
    
}

4.3 添加房间id

通过getview中的position位置来设置每一行的房间号码

//framgemnt中初始化
mRoomList=new ArrayList<string[]>();
mRoomList.add(new String[]{"8101","8102","8103","8104"});
mRoomList.add(new String[]{"8201","8202","8203","8204"});
mRoomList.add(new String[]{"8301","8302","8303","8304"});
mRoomList.add(new String[]{"8401","8402","8403","8404"});
//adapter中初始化
mroom_id=mroom_id_Items.get(position);
holder.mTvRoom1.setText(mroom_id[0]);
holder.mTvRoom2.setText(mroom_id[1]);
holder.mTvRoom3.setText(mroom_id[2]);
holder.mTvRoom4.setText(mroom_id[3]);

4.4 添加点击事件

ListViewAdapter.java中的Textview添加点击事件。

问题:注意,如果在ListViewAdapter.java直接添加点击事件,会导致,点击事件是响应最后一行items的事件。

比如,当viewcreate后,点击四个按钮显示房间号,无论你点击第几行的textview按钮都只会显示最后一个。

{"8401","8402","8403","8404"}

解决办法:写一个点击事件的接口ItemTextViewClickListenerListViewAdapter.java中,负责传递View视图,在

FragmentManagerRoom中实现接口的重写。此时的View获得整个Items的Textview。

//接口代码
Public interface onTextViewItemClickListener{
    void TextviewItemClickListener(View v);
}
//ListviewAdapter.java 代码
public class ListViewAdapter extends FragmentPagerAdapter implements View.OnClickListener{
    private onTextViewItemClickListener TextViewItemClick;
    @Override
    public void onClick(View v){
        this.TextViewItemClick.TextviewItemClickListener(v);
    }
    public setTextviewItemClickListener(onTextViewItemClickListener listener){
         this.TextViewItemClick=listener;
    }
    public View getView(int position, View convertView,ViewGroup parent){
         holder.mTvRoom1.setOnClickListener(this);
		holder.mTvRoom2.setOnClickListener(this);
		holder.mTvRoom3.setOnClickListener(this);
		holder.mTvRoom4.setOnClickListener(this);
    }
}
// RoomManagerFragment.java 代码
public class RoomManagerFrgment extends Fragment implements InnerItemOnClickListener  {
	private ListView mListroom;
	private myDialog dialog;
	private TextView mTvRoom2;
	private TextView mTvRoom1;
	private TextView mTvRoom3;
	private TextView mTvRoom4;
	public void onViewCreated(View view, Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onViewCreated(view, savedInstanceState);
		mListroom=(ListView) view.findViewById(R.id.roomlv);
		dialog=new myDialog(view.getContext());
		ListViewMyAdapter listViewMyAdapter= new ListViewMyAdapter(view.getContext());
		listViewMyAdapter.setOnInnerItemOnClickListener(this);
		mListroom.setAdapter(listViewMyAdapter);
    }
    @Override
	public void itemClick(View v) {
		// TODO Auto-generated method stub
		mTvRoom1=(TextView) v.findViewById(R.id.room_id_1);
		mTvRoom2=(TextView) v.findViewById(R.id.room_id_2);
		mTvRoom3=(TextView) v.findViewById(R.id.room_id_3);
		mTvRoom4=(TextView) v.findViewById(R.id.room_id_4);
		switch (v.getId()) {
		case R.id.room_id_1:
			//Log.d("TAG", mTvRoom1.getText()+"");
			mTvRoom1.setTag(R.id.room_id_1,"i am room_id_1");
			dialog.setRoominfo(mTvRoom1.getText()+"").show();
			dialog.setRoomStateListener(new RoomStateListener() {
				
				@Override
				public void RoomState(int state) {
					// TODO Auto-generated method stub
					setRoomState(state, mTvRoom1);
				}
			});
			break;
		case R.id.room_id_2:
			dialog.setRoominfo(mTvRoom2.getText()+"").show();
			dialog.setRoomStateListener(new RoomStateListener() {		
				@Override
				public void RoomState(int state) {
					// TODO Auto-generated method stub
					setRoomState(state, mTvRoom2);
				}
			});
			break;
		case R.id.room_id_3:
			dialog.setRoominfo(mTvRoom3.getText()+"").show();
			dialog.setRoomStateListener(new RoomStateListener() {		
				@Override
				public void RoomState(int state) {
					// TODO Auto-generated method stub
					setRoomState(state, mTvRoom3);
				}
			});
			break;
		case R.id.room_id_4:
			dialog.setRoominfo(mTvRoom4.getText()+"").show();
			dialog.setRoomStateListener(new RoomStateListener() {		
				@Override
				public void RoomState(int state) {
					// TODO Auto-generated method stub
					setRoomState(state, mTvRoom4);
				}
			});
			break;

	
	}
	}
}

4.5 自定义Dialog

用于显示弹窗中各个房间的信息。

4.5.1 布局文件xml

只展示其中一行,其他行的布局都是一样的。

问题: 展示的控件显示不全

解决方法: 每一行的height不要使用weight权重,全部默认高度就可以了。

 <!--第二行  -->
   <linearlayout android:layout_width="match_parent" android:layout_height="30dp" android:orientation="horizontal" android:gravity="center_horizontal">
       <linearlayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginleft="20dp" android:layout_weight="1">
	       <textview android:layout_width="wrap_content" android:layout_height="match_parent" android:textsize="16sp" android:text="照度 : ">
	       <textview android:layout_width="wrap_content" android:layout_height="match_parent" android:textsize="16sp" android:id="@+id/tv_illumination" android:text="Null">
       </textview></textview></linearlayout>
        <linearlayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_marginleft="20dp" android:layout_weight="1">
	       <textview android:layout_width="wrap_content" android:layout_height="match_parent" android:textsize="16sp" android:text="CO2 : ">
	       <textview android:layout_width="wrap_content" android:layout_height="match_parent" android:textsize="16sp" android:id="@+id/tv_co2" android:text="Null">
       </textview></textview></linearlayout>
   </linearlayout>

4.5.2 创建自定义dialog类

该类继承自dialog类,需要重写的方法有

  • onCreate(Bundle savedInstanceState) 布局文件的加载、控件的加载和创建方法

  • void RoomState(int state) 实现接口方法,用于更改房间的状态。

    public interface RoomStateListener {
    	void RoomState(int state);
    }
    

实现提示框里面的信息更新

  • 使用handler,handleMessage(android.os.Message msg)更新控件信息。注意收发。

    private Handler mHandler =new Handler(){
    		public void handleMessage(android.os.Message msg) {
                //根据发送的消息,更新dialog界面
          }
        	
    
  • 提供房间信息类传递方法

    //传递消息
    	public myDialog setRoominfo(String roomid) {
    		this.roomid = roomid;
    		mHandler.sendEmptyMessage(0);//发送
    		Log.d("myDialog",roomid+"");
    		return this;
    	}
    

创建一个房间信息类,用于存放房间的所有信息,如房间号、房间的状态、房间的传感器信息。

public class myDialog extends Dialog implements View.OnClickListener{
	private String roomid,cancel,confirm;
	private TextView testRoomText;
    public myDialog(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	private Button mbtn_On,mbtn_unClean,mbtn_Off;
	private TextView mbtn_Closeshow,mtv_roomid,mtv_illumination,mtv_co2,mtv_humidity;
	private DialogItemClickListener dialogListener;
	private DialogItemTextListener dialogTextListener;
	private myRunnable mRunnable=new myRunnable();
	private RoomInfo info;
	private Handler mHandler =new Handler(){
		public void handleMessage(android.os.Message msg) {
			//info=(RoomInfo) msg.obj;
			//Log.e("utils",info.getId()); 
			if(mtv_roomid!=null){
				mtv_roomid.setText(roomid+"");
			}else {
				Log.d("myDialog",roomid+"");
			}
			
		};
	};
    //传递消息
	public myDialog setRoominfo(String roomid) {
		this.roomid = roomid;
		mHandler.sendEmptyMessage(0);
		Log.d("myDialog",roomid+"");
		return this;
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.dialog_layout);
		//按空白处不能取消动画
		setCanceledOnTouchOutside(false);
		mbtn_Closeshow=(TextView) findViewById(R.id.tv_close);
		mbtn_On=(Button) findViewById(R.id.tv_on);
		mbtn_unClean=(Button) findViewById(R.id.tv_unclean);
		mbtn_Off=(Button)findViewById(R.id.tv_off);
		mtv_roomid=(TextView) findViewById(R.id.tv_roomid);
		mtv_illumination=(TextView) findViewById(R.id.tv_illumination);
		mtv_co2=(TextView) findViewById(R.id.tv_co2);
		mtv_humidity=(TextView) findViewById(R.id.tv_humidity);
		mbtn_Closeshow.setOnClickListener(this);
		mbtn_On.setOnClickListener(this);
		mbtn_unClean.setOnClickListener(this);
		mbtn_Off.setOnClickListener(this);
		
	}
    @Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
		case R.id.tv_close:
			this.cancel();
			this.dismiss();
			break;

		case R.id.tv_on:
			 dialogRoomStateListener.RoomState(RoomInfo.ONROOM);
			break;
		case R.id.tv_off:
			dialogRoomStateListener.RoomState(RoomInfo.OFFROOM);
			break;
		case R.id.tv_unclean:
			dialogRoomStateListener.RoomState(RoomInfo.UNCLEAN);
			break;
		}
	}
}

4.5.3 定义RoomInfo类

public class RoomInfo {
	public static final int ONROOM=1;
	public static final int UNCLEAN=2;
	public static final int OFFROOM=0;
	/*<color name="green">#33FF00</color>  
    <color name="red">#FF0000</color>  
      <color name="gry">#808080</color> */
	private String Temperate,Humidity,Id;
	public static List<string []=""> mListRoom=new ArrayList<string[]>(){{
			add(new String[]{"8101","8102","8103","8104"});
			add(new String[]{"8201","8202","8203","8204"});
			add(new String[]{"8301","8302","8303","8304"});
			add(new String[]{"8401","8402","8403","8404"});}};
	public String getTemperate() {
		return Temperate;
	}
	public void setTemperate(String temperate) {
		Temperate = temperate;
	}
	public String getHumidity() {
		return Humidity;
	}
	public void setHumidity(String humidity) {
		Humidity = humidity;
	}
	public String getId() {
		return Id;
	}
	public void setId(String id) {
		Id = id;
	}
	private int ROOMSTATE=OFFROOM;
	public RoomInfo(String temperate, String humidity, String id, int rOOMSTATE) {
		super();
		Temperate = temperate;
		Humidity = humidity;
		Id = id;
		ROOMSTATE = rOOMSTATE;
	}
	
	
}

4.5.4 传递Roominfo信息到Dialog中。

  • 点击房间按钮把房间信息Roominfo的对象传递给dialog
  • dialog在handlermessage中更新文本的信息。
  • 界面信息的刷新。

4.6 添加数据库

4.6.1创建数据库RoomData.db

​ 使用初始化方法创建

public class RoomSqlDao extends SQLiteOpenHelper{
    final static String ROOM_TABLE="RoomInfo_tb";
	final static String ROOM_DB_NAME="RoomData.db";
	SQLiteDatabase Room_db;
    public RoomSqlDao(Context context,String name,CursorFactory factory,int version){
             //参数2:数据库名称
    		//如果只有一个数据库名称,那么这个数据库的位置会在私有的目录中。/data/项目名称
    		//如果带sd卡路径,那么数据库位置则在指定的路径下
    		//参数3:查询的游标工厂,null会默认一个游标
    		//参数4:版本号
        super(context,ROOM_DB_NAME,null,1);
        	//用于获取数据库库对象。每次操作都是对象完成的
			//1.数据库存在,则直接打开数据库
			//2.数据库不存在,则创建数据库,在打开。
			//3.数据库包升级的时候,会调用升级方法。
        Room_db=super.getReadableDatabase();
    }
}

4.6.2 创建表格RoomInfo_tb

使用sql语句创建一个表。

create table RoomInfo_tb(
       _id integer primary key autoincrement,-- 主键,自增。不可更改。就算删除了,也会从删除的下面自增。
       RoomId char(10),
       RoomState integer,
       Temperatrue float(10,2),--浮点数,共10位,小数保留两位。
       Humidity  float(10,2),
       Illumination float(10,2),
       Smoke float(10,2),
       Gas float(10,2),
       Pm  float(10,2),
       Co  float(10,2),
       AirPressure float(10,2))

在onCreate中创建,如果已经有了,下次则不在创建。

@Override
	public void onCreate(SQLiteDatabase db) {
		// TODO Auto-generated method stub
		String create_tb_sql="create table RoomInfo_tb(_id integer primary key autoincrement," +
				"RoomId char(10)," +
				"RoomState integer," +
				"Temperature float(10,2)," +
				"Humidity  float(10,2)," +
				"Illumination float(10,2)," +
				"Smoke float(10,2)," +
				"Gas float(10,2)," +
				"Pm  float(10,2)," +
				"Co  float(10,2)," +
				"AirPressure float(10,2))";
		db.execSQL(create_tb_sql);
		Log.d("RoomInfo_tb", "表创建成功");
	}

4.6.3 添加一条数据

把房间信息封装成一个RoomInfo对象,作为参数传递。

添加的sql语句

insert into RoomInfo_tb RoomId values(RoomId) --                      列名	        值--不填列名,默认填充表格中所有记录(一行)--可以在值后面 加入条件。

使用数据库操作对象自带的api函数insert添加

public long addOneRoomInfo(RoomInfo info){
			  ContentValues values=new ContentValues();
			  values.put("RoomId", info.getRoomId());
			  values.put("RoomState", Integer.parseInt(info.getRoomState()));
			  values.put("Temperature", Float.parseFloat(info.getTemperature()));
			  values.put("Humidity", Float.parseFloat(info.getHumidity()));
			  values.put("Illumination", Float.parseFloat(info.getIllumination()));
			  values.put("Smoke", Float.parseFloat(info.getSmoke()));
			  values.put("Gas", Float.parseFloat(info.getGas()));
			  values.put("Pm", Float.parseFloat(info.getPM25()));
			  values.put("Co", Float.parseFloat(info.getCO2()));
			  values.put("AirPressure", Float.parseFloat(info.getAirpressure()));
    			// args1: table, args2:column,args3:ContextValues(可以存一个map类型数据)
			  long count=Room_db.insert(ROOM_TABLE, null, values);
			  if(count==1){
				  Log.d("添加成功"+info.getRoomId(),count+"");
			  }
			  return count;
		}

4.6.4 自动更新传感器数据

拿到查询一个房间的数据与实际的传感器数据做对比,如果变化,就自动更新。

	//5、自动更新传感器数据
		/*
		 * update RoomInfo_tb set "Sensortype"=? where RoomId=?
		 */
	  public int autoUpdateSenorData(String senorType,String senorValue,String RoomId){
		  ContentValues values=new ContentValues();
		  values.put(senorType, senorValue);
		  int count=Room_db.update(ROOM_TABLE, values, "RoomId=?", new String[]{RoomId});
		  if(count==1){
			  Log.d("房间"+RoomId,senorType+"更新成功");
		  }
		  return count;
	  }

4.6.5 更新房间状态数据

//4、更新房间状态		/*		 * update RoomInfo_tb set RoomState=? where RoomId=?		 */		  public int updateOneRoomState(String RoomId,int RoomState){			  ContentValues values=new ContentValues();			  values.put("RoomState",RoomState);			  int count=Room_db.update(ROOM_TABLE, values, "RoomId=?", new String[]{RoomId});			  if(count==1){				  Log.d(RoomId+"房间状态更新",RoomState+"");			  }			  return count;		  }

4.6.6 查询一个房间的数据

使用sql语句查询,对应的数据库操作对象方法是rawQuery()返回了是一个Cursor对象结果。

select *  from RoomInfo_tb where RoomId=?
 public Cursor getOneRoomInfo(String RoomId){		  String bsql="select * from "+ROOM_TABLE+" where RoomId="+RoomId+"";		  Cursor oneRoomCursor=Room_db.rawQuery(bsql, null);		  if(oneRoomCursor.getCount()==1){			  Log.d("房间号"+RoomId,oneRoomCursor.getCount()+"" );		  }		  return oneRoomCursor;	  }

4.6.7 查询所有房间的状态

使用sql语句查询,对应的数据库操作对象方法是rawQuery()返回了是一个Cursor对象结果。

	/*	 * select RoomState from RoomInfo_tb;	 */	  public Cursor getAllRoomInfo(){		  String asql="select RoomState from "+ROOM_TABLE;		  Cursor roomCursor= Room_db.rawQuery(asql, null);		  if(roomCursor.getCount()>0){			  Log.d("查询到",roomCursor.getCount()+"" );		  }		  return roomCursor;	  }

4.6.8 清空表格,复位主键

  • 清空表格

    delete from RoomInfo_tb
    
  • 清空主键

    update sqlite_sequence SET seq = 0 Where name =RoomInfo_tb
    
    public void deleteRoomInfo(){			String deletesql="delete from "+ROOM_TABLE;			Room_db.execSQL(deletesql);			String deletesqll="update sqlite_sequence SET seq = 0 WHERE name = '"+ROOM_TABLE+"'";			Room_db.execSQL(deletesqll);			Log.d("deleteRoomInfo","清空表格成功,并且重新复位");		}
    

4.7 初始化加载界面

当我们进入房间管理页面,系统自动从数据库查询所有房间的状态,进行颜色的切换。

4.7.1 数据库数据记录总数大于0

配合数据库模块中的4.6.7 查询所有房间的状态。对控件进行初始化

public static List<int []="">listroomstate=new ArrayList<int[]>(){{			add(new int[]{1,1,1,1});			add(new int[]{1,1,1,1});			add(new int[]{1,1,1,1});			add(new int[]{1,1,1,1});		}};Cursor scRoomInfo= rSDao.getAllRoomInfoState();if(scRoomInfo.getCount()>0){    int i=0;    while (scRoomInfo.moveToNext()) {        int j = i/4;//取得list集合中的索引位置        switch (j) {            case 0:                //Cursor的结果集,0表示每一条记录的第一个属性。                //i%4取得list集合中int数组的索引。                RoomInfo.listroomstate.get(0)[i%4]=scRoomInfo.getInt(0);                break;            case 1:                RoomInfo.listroomstate.get(1)[i%4]=scRoomInfo.getInt(0);                break;            case 2:                RoomInfo.listroomstate.get(2)[i%4]=scRoomInfo.getInt(0);                break;            case 3:                RoomInfo.listroomstate.get(3)[i%4]=scRoomInfo.getInt(0);                break;        }        i++;	    }

4.7.2 数据库记录总数大于0小于16,或者等于0

RoomInfo_db数据库中RoomInfo_tb表里是空的,需要拿到传感器的数据、每个房间默认的状态、每个房间默认门牌号进行初始化。

传感器的初始数据,需要在线程当中同步。

  • 需要等待线程中的所有对map赋值的代码运行结束才能初始化
  • 并且传感器map对象要同步 synchronized
try {    while (RoomInfo.mapRoomInfo.size()!=8) {        try {            Thread.sleep(10L);            Log.d("initRoomState阻塞着", "正在等待传感器数据同步……");        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }				}    Log.d("initRoomState", "阻塞着,正在等待传感器数据同步……");    handSenorDataThread.join();} catch (InterruptedException e) {    // TODO Auto-generated catch block    e.printStackTrace();}Log.d("initRoomState", "传感器数据同步成功,正在首次初始化……");outer:for (int i = 0; i < RoomInfo.mListRoom.size(); i++) {    for (int j = 0; j <roominfo.mlistroom.get(i).length; j++)="" {="" roominfo.maproominfo.put("roomid",="" roominfo.mlistroom.get(i)[j]);="" roominfo.maproominfo.put("roomstate",="" "1");="" 10="" 值。="" if(roominfo.maproominfo.size()="=10){" rinfo="new" roominfo(roominfo.maproominfo);="" rsdao.addoneroominfo(rinfo);="" }else="" log.d("initroomstate",="" roominfo.maproominfo.size()+"有误");="" "首次初始化失败");="" break="" outer;="" }="" }}="" 标志log.d("initroomstate",="" "首次初始化成功");="" ```="" ###="" 4.8="" 线程加载处理数据。="" 根据企想给的库文件,客户端获取服务器的传感器数据都是通过接口回调的方式来处理的。="" **问题1**="" `getdata`接口调会在什么时候被触发?="" **解答1:**="" 要解释这个问题,先弄清楚两个概念,第一个就是输入流和输出流,第二个就是`socket`通信="" ####="" 4.8.1="" 输入流和输出流="" |="" 加载一个文件到jvm的内存中="" inputstream(输入流,读取)="" ---------------------------="" ----------------------------="" 从jvm内存中把数据保存成文件="" outoutstream(输出流,写入)="" 4.8.2="" socoket通信="" 这是一种使用数据量比较大的数据传输协议,客户端只要通过服务器的ip地址和port端口号,就能连接上服务器。两者就能建立通信的管道(input和output)。="" 4.8.3企想提供的源码解决="" ```java="" 1、定义的泛型接口="" public="" abstract="" interface="" datacallback<t="">
{
  public abstract void onResult(T paramT);
}
//2、接口在SocketClient中的重写。
public void getData(DataCallback callback)
  {
    mDataCallback = callback;
  }
//3、ReadThread线程中读取服务器发来的数据
Socket socket = (Socket)this.mWeakSocket.get();//拿到连接的服务器对象 
 BufferedReader DataRead = new BufferedReader(new InputStreamReader(socket.getInputStream()));
if (socket != null) {
    try
    {
        String response = "";
        BufferedReader DataRead = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //这段代码就一直读取服务器的数据,只要读到数据就调用setData方法。是阻塞的。   
        do
            {
                while ((response = DataRead.readLine()) != null)
                {
                    JSONObject obj = new JSONObject(response);
                    setData(obj);
                }
                if (socket.isClosed()) {
                    break;
                }
            } while (this.isStart);
        }
    catch (JSONException e1)
    {
        e1.printStackTrace();
    }
    catch (Exception e1)
    {
        this.isStart = false;
        SocketClient.this.mSocket = null;
        e1.printStackTrace();
    }
 // 4、setdata方法
    public void setData(JSONObject obj)
      throws JSONException
    {
        //……处理数据的代码
      DeviceBean bean = new DeviceBean();
      if (obj.has("Data")) {
      		if (SocketClient.mDataCallback != null) {
                		//重写接口。
        				SocketClient.mDataCallback.onResult(bean);
      }
    }
  }
 // 5、接口调用的重写
    public void initData(){
        Log.d("initData","数据采集活动已经开启");
        ControlUtils.getData();
        SocketClient.getInstance().getData(new DataCallback<devicebean>() {
            //此处就传递一个DataCallBack对象到SocketClient中,用于接收SocketClient类的信息。
            
            @Override
            public void onResult(final DeviceBean bean) {
                // TODO Auto-generated method stub
				//然后我们把数据处理放在一个线程当中操作。
                handSenorDataThread=new MainActivity.HandSenorDataThread(bean);
                handSenorDataThread.start();

            }
        });
    }

4.8.4数据变化自动更新

通过上面的上面的代码我们已经直到了,服务器只要数据发生变化,就会直接调用DataCallBac接口。所以我们的数据自动更新就在这个接口中完成。完成此功能,需要配合上面的4.6.4的代码完成。

  • 当dialog打开一个房间以后,数据变化,界面就要完成更新,自动发生变化的数据到更新到数据库。

    if(!RoomInfo.mapRoomInfo.get(key).equals(values)&&!roomManagerFrgment.manageRoomid.isEmpty()){
        RoomInfo.mapRoomInfo.put(key, values);//更新map中某个传感器的值。
        mrooInfo=new RoomInfo(RoomInfo.mapRoomInfo);
        //roomLinkAgeFragment.getSenorValue(mrooInfo);
        Log.d("autoUpdateSensor",RoomInfo.mapRoomInfo.size()+"");
        //然后将map封建成房间信息对象发送给dilog,更新界面信息。
        roomManagerFrgment.dialog.updateRoominfo(mrooInfo,key,values);
    }
    //dialog 完成数据库的更新操作
    public myDialog updateRoominfo(RoomInfo info,String key,String values) {
    		this.roomInfo = info;
    		Message msg=Message.obtain();
    		//msg.obj=roomInfo;
    		mHandler.sendMessage(msg);
    		rSDao.autoUpdateSenorData(key, values, RoomManagerFrgment.manageRoomid);
    		return this;
    	}
    
  • 当dialog关闭以后,其他界面的操作期间,发生的数据变化,自动发生变化的数据到更新到数据库。

    dialog关闭以后(dialog对象也被清理了)管理房间的号码已经被清零了(用于判断却别两种状态)。需要通过别的来完成数据的更新,

    if(!RoomInfo.mapRoomInfo.get(key).equals(values)&&roomManagerFrgment.manageRoomid.isEmpty()
    					&&!roomControlFragment.ControlRoomid.isEmpty()) {
    				/*数据与之间保存的发生了变化&&房间管理的Roomid是空的&&房间控制的RoomId不是空的
    				 *说明:已经打开了房间, dialog关闭后,manageroomid被清0了,在此期间发生了数据的更新,应该得到保存。
    				 *  考虑到,viewpager的加载模式,让临近fragment处理。我们交给ControlFragment来保存。
    				 */
    			       // mrooInfo=new RoomInfo(RoomInfo.mapRoomInfo);
    					 //roomLinkAgeFragment.getSenorValue(mrooInfo);
    				 RoomInfo.mapRoomInfo.put(key, values);
    				 mrooInfo=new RoomInfo(RoomInfo.mapRoomInfo);
    				 roomLinkAgeFragment.updateRoominfo(mrooInfo, key, values);
    				 Log.d("autoUpdateSensor","传感器:"+key+"发生变化,但"+"房间号是空的");
    			}
    
    
    public void updateRoominfo(RoomInfo info,String key,String values) {
    			this.roomInfo = info;
    			mHandler.sendEmptyMessage(0);
    			Log.d("linkage","通过linkage自动更新了数据");
    			rSDao.autoUpdateSenorData(key, values, linkAgeRoomid);
    			
    }
    
    

4.9 dialog按钮点击

dialog 有未打扫、已入住、未入住三个按钮。

实现功能,点击不同的按钮,就把对应房间的状态写入到数据库,改变房间的颜色。

  • 定义接口,

    public interface DialogItemClickListener{
        void dialogItemClick(View v);
    }
    public  void setRoomStateListener(RoomStateListener listener){
    		this.dialogRoomStateListener=listener;
    	} 
    
  • mydialog 点击按钮,重写RoomState方法。

    case R.id.tv_on:
    			 dialogRoomStateListener.RoomState(RoomInfo.OFFROOM);
    			 rSDao.updateOneRoomState(RoomManagerFrgment.manageRoomid, RoomInfo.OFFROOM);
    			break;
    case R.id.tv_off:
    			dialogRoomStateListener.RoomState(RoomInfo.ONROOM);
    			rSDao.updateOneRoomState(RoomManagerFrgment.manageRoomid, RoomInfo.ONROOM);
    			break;
    case R.id.tv_unclean:
    			dialogRoomStateListener.RoomState(RoomInfo.UNCLEAN);
    			rSDao.updateOneRoomState(RoomManagerFrgment.manageRoomid, RoomInfo.UNCLEAN);
    			break;
    
  • RoomManager中根据结果,改变颜色,以及同步数据到数据库。

    public void setRoomState(int state,TextView tv) {
    		// TODO Auto-generated method stub
    		switch (state) {
    		case RoomInfo.ONROOM:
    			tv.setBackgroundColor(Color.GREEN);
    			break;
    		case RoomInfo.OFFROOM:
    			tv.setBackgroundColor(Color.RED);
    			break;
    		case RoomInfo.UNCLEAN:
    			tv.setBackgroundColor(Color.GRAY);
    		     break;
    	}
        //
        case R.id.room_id_2:
    			mrooInfo=new RoomInfo(rSDao.getOneRoomInfo(mTvRoom2.getText()+""));
    			manageRoomid=mTvRoom2.getText()+"";
    			RoomInfo.mapRoomInfo.put("RoomId", manageRoomid);
    			dialog.setRoominfo(mrooInfo).show();
    			
    			dialog.setRoomStateListener(new RoomStateListener() {		
    				@Override
    				public void RoomState(int state) {
    					// TODO Auto-generated method stub
    					setRoomState(state, mTvRoom2);
    				}
    			});
    			break;
    

房间管理的部分结束。

5 房间控制部分

此部分比较简单,说一下几个逻辑代码

5.1 实现按钮文本点击后的自动切换

public Boolean setButtonText(Button btn,View v,boolean flag,String SensorType, String Channel, String Command){
			Boolean setflag=flag;
			if(!TextUtils.isEmpty(ControlRoomid)){
				if(!flag){
					btn.setText("关");
					ControlUtils.control(SensorType, Channel, Command);
					setflag= true;
				}else {
					btn.setText("开");
					ControlUtils.control(SensorType, Channel, Command);
					setflag= false;
				}
				
			}else {
				AlertDialog.Builder dialog=new AlertDialog.Builder(v.getContext());
				dialog.setTitle("警告").setMessage("请选择房间后在控制").setNeutralButton("好的", new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						// TODO Auto-generated method stub
					     dialog.dismiss();
					}
				}).show();
			}
			return setflag;
			
		}

每个按钮,使用此方法提供代码的复用性

5.2Android:onClick属性失效了。

java.lang.IllegalStateException: Could not find method xxx in a parent or ancestor Context for android:onClick attribute defined on view class
//问题就出在Fragment身上,前面说了它不是布局器,所以它不具备渲染视图的能力,它管理的布局器最终要加载到一个ViewGroup对象内,由ViewGroup对象来渲染。而视图树并不知道每一个子控件来源于哪里,这就导致了一个结果:不管是在什么地方定义的onClick属性,都必须在包含该Button的Activity中去寻找OnClick()方法。fragment有生命周期,是视图的管理者,并不是context,

解决办法:实现Onclick接口。可以在RoomControl本类里面写,

6、房间联动部分

6.1 新控件的使用

两个新控件的使用Spinner(下拉框)Switch(开关)

spinner的使用方法

<!--定义数组-->
values——>StringArray
<resources>
    <string-array name="senortype">
        <item>温度</item>
        <item>湿度</item>
        <item>光照</item>
    </string-array>
     <string-array name="symbol">
        <item><!--[CDATA[ -->]]></item>
        <item><!--[CDATA[ <]]--></item>
    </string-array>
    <string-array name="controltype">
        <item>风扇</item>
        <item>射灯</item>
        <item>窗帘</item>
    </string-array>
    <string-array name="controlstate">
        <item>开</item>
        <item>关</item>
    </string-array>
    	
</resources>
<!--使用数组-->
<spinner android:layout_width="100dp" android:layout_height="wrap_content" android:textsize="16sp" android:id="@+id/sp_controlstate" android:entries="@array/controlstate">
setOnItemSelectedListener
 @Override
		public void onItemSelected(AdapterView<!--?--> parent, View view,
				int position, long id) {
    //parent	AdapterView: The AdapterView where the click happened.
   //view	View: The view within the AdapterView that was clicked (this will be a view provided by     the adapter)
   //position	int: The position of the view in the adapter.
   //id	      long: The row id of the item that was clicked.

		}

		@Override
		public void onNothingSelected(AdapterView<!--?--> parent) {
			// TODO Auto-generated method stub
			
		} 
<uses-sdk android:minsdkversion="17" android:targetsdkversion="17">

6.2联动功能实现

从界面的布局,我们可以看出

  • 联动按钮负责联动功能的开关
  • “>”和“<”负责条件的判断。是否成立
  • "开"和“关”负责设备的开关

举个例子,当光照小于50,自动开灯,大于50,自动关闭。

这个案例当中,肯定需要一个循环或者是延时执行的函数。我们使用的handler.postdelay函数。延时执行。

6.2.1 触发联动功能

//这个功能要在swtich开关当中完成,true打开联动线程,false关闭联动线程。
mSwState.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				// TODO Auto-generated method stub
				if(isChecked){
					Log.d("mSwState",isChecked+"");
					linkHandler.postDelayed(linkmode, 1000L);
				}else {
					Log.d("mSwState",isChecked+"");
					Log.d("actionMode", "联动已停止");
					if(scenThread!=null){
						linkHandler.removeCallbacks(linkmode);
					}
				}
			}
		});

6.2.2 逻辑代码分析

6.2.2.1 第一层(联动开关层)

条件 成立
房间号不为空 true
选择的符号不为空 true
选择传感器不为空 true
选择控制器不为空 true
选择的控制器开关不能空 true
阈值不能空 true
控制的指令不能空 true

6.2.2.2 第二层(符合与比值)

第一层条件成立执行

结果 符号 比值结果 说明
true > A>B 9>5? true:false 符号==比值结果。条件成立执行控制
false < A<b 4="">5 ? true:false 符号==比值结果。条件成立执行控制

6.2.2.3 开关层

第二层条件成立或者第一层条件成立执行。

现在的(条件成立)光照<50,自动开灯,(条件不成立)光照>50,自动关灯

第二层 开关 说明
true True开,False关 开关值正常
false True关,Flase开 开关值取反

6.3 代码实现

@Override
public void run() {
    // TODO Auto-generated method stub
    Log.d("actionMode", x+++"");
 if (lInfo!=null&&controlCmd!=null&&!TextUtils.isEmpty(linkAgeRoomid)
         &&!select_symbol.isEmpty()&&!TextUtils.isEmpty(metSenorValues.getText())
              &&!select_State.isEmpty()&&!select_value.isEmpty()) {//第一层
        //boolean actionMode=!TextUtils.isEmpty(linkAgeRoomid)&&!TextUtils.isEmpty(metSenorValues.getText())?true:false;
        //Toast.makeText(getActivity().getApplicationContext(), "联动执行", 1).show();
        Log.d("actionMode", "联动执行中");
        boolean symbol=select_symbol.equals(">")?true:false;
        boolean var=Float.parseFloat(select_value)>Float.parseFloat(metSenorValues.getText()
                                                                    .toString())?true:false;
       boolean swtichCmd=select_State.equals("开")?true:false;
        if(var==symbol){//第二层
            if (swtichCmd) {//第三层
                Log.d("actionMode", "True开启设备:"+controlCmd.get(0)[0]+":"+controlCmd.get(0)[1]);
                ControlUtils.control(controlCmd.get(0)[0], controlCmd.get(0)[1], ConstantUtil.OPEN);
            } else {
                Log.d("actionMode", "True关闭设备"+controlCmd.get(0)[0]+":"+controlCmd.get(0)[1]);
                ControlUtils.control(controlCmd.get(0)[0], controlCmd.get(0)[1], ConstantUtil.CLOSE);
            }
        }else {
            Log.d("actionMode", "条件不成立");
            //Toast.makeText(getActivity().getApplicationContext(), "条件不成立", 1).show();
            if (!swtichCmd) {
                Log.d("actionMode", "false开启设备:"+controlCmd.get(0)[0]+":"+controlCmd.get(0)[1]);
                ControlUtils.control(controlCmd.get(0)[0], controlCmd.get(0)[1], ConstantUtil.OPEN);
            } else {
                Log.d("actionMode", "false关闭设备:"+controlCmd.get(0)[0]+":"+controlCmd.get(0)[1]);
                ControlUtils.control(controlCmd.get(0)[0], controlCmd.get(0)[1], ConstantUtil.CLOSE);
            }
        }

    }else {
        Log.d("actionMode", "请检查配置");
        //Toast.makeText(getActivity().getApplicationContext(), "请检查配置", 1).show();
    }	

    linkHandler.postDelayed(linkmode, 1000L);
    //linkHandler.removeCallbacks(linkmode);

}

7 温度图表绘制

7.1 自定义View画

通过继承view类来自定义一个ChartView控件,用于显示图表。

大致分为三步

  • 新建一个类,继承view类。把三个构造函数引用出来,是为了外面的布局器能够加载到。

  • 重写ondraw方法。在ondraw方法中,一点点绘制。

  • 在xml文件中,使用我们的控件。

    <com.lpc.util.mychartview <!--包名+类名--="">
            android:layout_width="800dp"
            android:layout_height="400dp"
            android:gravity="center"
            android:id="@+id/cv_chart"
            />
    

7.1.2 初始化绘画工具

工具 用途
ChartPoint.class 用于保存点的x、y坐标
xLength、yLength x和y的长度
ChartPoint xyIntersection x和y的交点
ChartPoint xStopPoint x的箭头的位置
ChartPoint yStopPoint y轴箭头的位置
xSpance、ySpance x和y轴上的间距
xScale、yScale x和y的刻度的个数
ymaxvalue 根据y的最大值来平分刻度
Paint 画笔

ChartPoint.class

public class chartPoint {
	float x,y;
	
	public chartPoint(float x, float y) {
		super();
		this.x = x;
		this.y = y;
	}
}

初始化工具

public void initTools(){
		//1、x、y轴的长高和他们的交点。
		x_Length=500;
		y_Length=300;
		xyIntersection=new chartPoint(100, 350);
		yStopPoint=new chartPoint(xyIntersection.x, xyIntersection.y-y_Length);
		xStopPoint=new chartPoint(xyIntersection.x+x_Length, xyIntersection.y);
		//2、xy的间距和他们共有多少刻度
		x_space=40;
		x_scale=x_Length/x_space;
		y_space=40;
		y_scale=y_Length/y_space;
		ymaxValue=600;
		//yScaleText=15;
		y_get_Ratio=ymaxValue/y_scale/y_space;
		
//		for (int i = 0; i <x_scale; i++)="" {="" x_scale_array[i]="i+1+"";" }="" 3、坐标画笔、点的画笔、线条的画笔="" coordinate_paint="new" paint();="" coordinate_paint.setcolor(color.black);="" coordinate_paint.setstrokewidth(2);="" coordinate_paint.setstyle(paint.style.fill);="" 消除齿距="" coordinate_paint.setantialias(true);="" point_paint="new" point_paint.setcolor(color.blue);="" point_paint.setstrokewidth(1);="" point_paint.setstyle(paint.style.stroke);="" point_paint.setantialias(true);="" temp_h_paint="new" temp_h_paint.setcolor(color.green);="" temp_h_paint.setstrokewidth(4);="" temp_h_paint.setstyle(paint.style.fill);="" temp_h_paint.setantialias(true);="" temp_l_paint="new" temp_l_paint.setcolor(color.red);="" temp_l_paint.setstrokewidth(4);="" temp_l_paint.setstyle(paint.style.fill);="" temp_l_paint.setantialias(true);="" point_list="new" arraylist<chartpoint="">();
		
	}

绘制坐标、箭头

public void drawCoordinate(Canvas canvas) {
		// TODO Auto-generated method stub
		//1、画x和y的线和箭头
		canvas.drawLine(xyIntersection.x, xyIntersection.y, xStopPoint.x , xStopPoint.y, coordinate_Paint);
		canvas.drawLine(xStopPoint.x, xStopPoint.y, xStopPoint.x-8, xStopPoint.y+5, coordinate_Paint);
		canvas.drawLine(xStopPoint.x, xStopPoint.y, xStopPoint.x-8, xStopPoint.y-5, coordinate_Paint);
		canvas.drawText("x", xStopPoint.x+10,xStopPoint.y+5, coordinate_Paint);
		
		canvas.drawLine(xyIntersection.x, xyIntersection.y, yStopPoint.x , yStopPoint.y, coordinate_Paint);
		canvas.drawLine(yStopPoint.x, yStopPoint.y, yStopPoint.x-5, yStopPoint.y+8, coordinate_Paint);
		canvas.drawLine(yStopPoint.x, yStopPoint.y, yStopPoint.x+5, yStopPoint.y+8, coordinate_Paint);
		canvas.drawText("Y", yStopPoint.x-5,yStopPoint.y-10, coordinate_Paint);
		
		canvas.drawText("0", xyIntersection.x-20,xyIntersection.y+20 ,coordinate_Paint);
		//2、标记刻度
		  //x轴的刻度
			for (int i = 1; i < x_scale; i++) {
				canvas.drawLine(xyIntersection.x+x_space*i, xyIntersection.y, xyIntersection.x+x_space*i, xyIntersection.y-5, coordinate_Paint);
				canvas.drawText(i+"",  xyIntersection.x+x_space*i-5, xyIntersection.y+20, coordinate_Paint);
			}
		  //y轴的刻度
			for (int i = 1; i <y_scale; i++)="" {="" canvas.drawtext(string.format("%1.0f",="" y_get_ratio*y_space*i),="" xyintersection.x-50,="" xyintersection.y-x_space*i+10,="" coordinate_paint);="" canvas.drawline(xyintersection.x,="" xyintersection.y-x_space*i,="" xyintersection.x+5,="" }="" ```="" ####="" 7.1.3根据传递的数值画点、连线="" 我们的传进来的数据需要经过等比转换后才能准确的显示在位置上。="" 画点:我们y坐标的长度是300,间距是40的单位,那么我们能拿到7个刻度值。现在我们设置y的最大坐标是700,我们计算后得出。1oo为间距,100只有40个单位来表示,那么我们的精度ratio就是100="" 40="2.5,也就是一个单位等于2.5.这样显示比较精准。另外,还需要拿交点的y坐标减去转换后的y坐标,才能正确显示位置。我们设置的y坐标和自带的坐标是相反的。" 连线:遍历用于保存chartpointlist,新建一个新的chartpot对象,用于保存上一次的点,在下次的时候,保存的点作为起始点,这次的点作为停止点……。="" ```java="" public="" void="" drawpoint(canvas="" canvas){="" int="" i="1;" chartpoint="" linepoint="new" chartpoint(0,="" 0);="" for="" (chartpoint="" point="" :="" point_list)="" float="" ydata="xyIntersection.y-point.y/y_get_Ratio;" canvas.drawcircle(xyintersection.x+i*x_space,="" ydata,="" 3,="" point_paint);="" canvas.drawtext(string.format("%1.0f",point.y),="" xyintersection.x+i*x_space-15,="" ydata-20,="" if(i="">1){
				if(point.y>15){
					canvas.drawLine(linePoint.x,linePoint.y,xyIntersection.x+i*x_space,ydata, temp_H_Paint);
				}else {
					canvas.drawLine(linePoint.x,linePoint.y,xyIntersection.x+i*x_space, ydata, temp_L_Paint);
				}
			}
			
			linePoint.x=xyIntersection.x+i*x_space;
			linePoint.y=ydata;
			Log.d("drawPoint", i+"");
			i++;
		}
	}

7.2 动态绘图

加载完自定义的图表,我们的TempertureFramengemt,在拿到房间号后和滑动此界面的界面开始动态绘制。

  • ​ 动态绘制,我们使用handler来循环延时执行一个Runable,用于保存传感器的数值,并且如果点的数量超过x的刻度,我们就删除第一个坐标。我们把这个list作为静态对象放到mychartview中。因为我们一直在对这个list在进行赋值操作。所以绘制点那里永远不会停止。除非线程停止。并且,每次绘制调用invalidate,用于下次重新绘制。
myRunable chartRunable=new myRunable();
	class myRunable implements Runnable{
		int i=0;
		float x,y;
		Message msg=Message.obtain();
		@Override
		public void run() {
			// TODO Auto-generated method stub
			if(tInfo!=null){
				x=Float.parseFloat(i+1+"");
				y=Float.parseFloat(tInfo.getIllumination());
				mcv_chart.point_list.add(new chartPoint(x, y));
				Log.d("chartRunable", "温度"+tInfo.getIllumination()+"");
				mHandler.sendEmptyMessage(0);
			}else {
				mHandler.removeCallbacks(chartRunable);
			}
			if (mcv_chart.point_list.size()>12) {
				//i=11;
				mcv_chart.point_list.remove(0);
			   
			} 
			Log.d("chartRunable", "list长度"+mcv_chart.point_list.size()+"");
			mHandler.postDelayed(chartRunable, 2000L);
		}
		
	}
  • 界面停留图表界面,启动延时执行,其他页面暂停。

    通过主界面调用temp界面中handle发送message来实现。

//mainactivity
if(arg0!=3&&pagestate==3){//pagestate 用于标记是否进入了3页面。如果没有进入3页面,就不需要要发送消息
    Message msg=new Message();
    pagestate=0;
    msg.arg1=1;
    roomTemperateFragment.mHandler.sendMessage(msg);
		}
//tempertureFragment
if(msg.arg1==1&&!tempRoomid.isEmpty()){
    Toast.makeText(getActivity().getApplicationContext(), "温度监控已暂停",1 ).show();
    Log.d("chartRunable", "暂停");
    mHandler.removeCallbacks(chartRunable);
					
}

至此所有的此app的相关代码全部结束。

8 小的bug处理

8.1数据发生变化,才能执行联动和温度监控

原因: 处理数据的线程中,发送了一个空的roominfo对象出来了。

if(RoomInfo.mapRoomInfo.size()>=8){//第一次启动,map的长度是8个传感器,当点击dialog后,会put一个roomid,变成9个
					//Log.d("mrooInfo","我是空的");
    mrooInfo=mrooInfo=new RoomInfo(RoomInfo.mapRoomInfo);//少了这句封装的代码
    roomLinkAgeFragment.getSenorValue(mrooInfo);//类似接口回调发送
    roomTemperateFragment.getRoomInfo(mrooInfo);//类似接口回调发送
}

8.2 逻辑优化

先前所有获取服务器的代码全部放在了房间管理的fragment中,这么做不利于其他fragment的数据的传递,所以放到了MainActivity中。

</y_scale;></x_scale;></com.lpc.util.mychartview></roominfo.mlistroom.get(i).length;></int[]></string[]></string[]></string[]>

posted @ 2021-06-30 01:20  FirstReed  阅读(197)  评论(0编辑  收藏  举报