【Android开发—电商系列】(三):缓存的使用
最后更新于:2022-04-01 10:02:20
### 导读
电商APP的首页是商品展示页面,在网络连接不可用时,如果不使用缓存,数据获取不到,那么首页就空白一片,这样用户体验很不好,所以这个时候我们要用到缓存。
### 思路
在有网的时候把数据存到缓存中,没网的时候数据从缓存中获得。
### 代码实现
~~~
public class AppContext extends Application {
/**
* 获得首页分类商品列表
* @return
* @throws AppException
*/
public HomePagesVo getHomePages() throws AppException {
HomePagesVo homePagesVo = null;
// 构建缓存文件的key
String key = "homePagesVo_1" ;
// 1.如果联网则首先从服务器获取数据
if (isNetworkConnected()) {
try {
//从服务器获取channelCategoryVo的集合
homePagesVo = ApiClient.GetHomePages(this);
// 如果能够获取到服务器中的数据则保存到本地缓存,只有保证数据不为空,且获取到的结果为true的时候才缓存到本地
if (homePagesVo != null
&& homePagesVo.getResult().equals("true")) {
homePagesVo.setCacheKey(key);
saveObject(homePagesVo, key);
}else{
homePagesVo=null;
}
} catch (AppException e) {
// 如果出现访问中途断网,则获取本地缓存中的数据
homePagesVo = (HomePagesVo)readObject(key);
}
}
// 2.如果未联网则从缓存中获取数据
else {
homePagesVo = (HomePagesVo) readObject(key);
}
return homePagesVo;
}
/**
* 保存对象
* @param ser
* @param file
* @throws IOException
*/
public boolean saveObject(Serializable ser, String file) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = openFileOutput(file, MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(ser);
oos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
oos.close();
} catch (Exception e) {
}
try {
fos.close();
} catch (Exception e) {
}
}
}
/**
* 读取对象
* @param file
* @return
* @throws IOException
*/
public Serializable readObject(String file) {
if (!isExistDataCache(file))
return null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = openFileInput(file);
ois = new ObjectInputStream(fis);
return (Serializable) ois.readObject();
} catch (FileNotFoundException e) {
} catch (Exception e) {
e.printStackTrace();
// 反序列化失败 - 删除缓存文件
if (e instanceof InvalidClassException) {
File data = getFileStreamPath(file);
data.delete();
}
} finally {
try {
ois.close();
} catch (Exception e) {
}
try {
fis.close();
} catch (Exception e) {
}
}
return null;
}
/**
* 检测网络是否可用
*
* @return
*/
public boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
return ni != null && ni.isConnectedOrConnecting();
}
}
~~~
待优化之处
在保存对象时应判断一下缓存是否存在,而且增加一个布尔类型参数表示用户是否要刷新数据。
如果用户没有刷新数据,且缓存已经存在,就不要再次保存对象(SaveObject),要尽量减少IO操作,提高效率;
【Android开发—电商系列】(二):仿淘宝商品属性标签页
最后更新于:2022-04-01 10:02:18
### 一睹为快
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d2d44cd.jpg "")
### 需求
1.动态加载属性,如尺码,颜色,款式等
由于每件商品的属性是不确定的,有的商品的属性是颜色和尺码,有的是口味,有的是大小,所以这些属性不能直接写死到页面上。
2.动态加载属性下的标签
每个属性下的标签个数也不是一定的,比如有的商品的尺码是是S,M,XL,有的是均码,也就是每种属性的具体的内容是不一定的。
### 技术点
自定义ViewGroup,使其中的TextView可以依据内容长短自动换行,如下图所示
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d30e800.jpg "")
### 实现
### 布局
通过ListView来显示商品所有属性,每种属性作为ListView的Item。
~~~
<!-- 商品规格列表 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFFFF"
>
<ListView
android:id="@+id/lv_property"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:divider="#C0C0C0"
android:dividerHeight="0.5px"
android:listSelector="#00000000">
</ListView>
</LinearLayout>
~~~
### 自定义ViewGroup
> 普通的LinearLayout只能横向和纵向显示控件,但是当一行显示不够时,无法自动换行,需要我们自定义布局容器。
~~~
package jczb.shoping.common;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
public class MyViewGroup extends ViewGroup {
private final static int VIEW_MARGIN=15;
public MyViewGroup(Context context, AttributeSet attrs){
super(context, attrs);
}
public MyViewGroup(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int stages = 1;
int stageHeight = 0;
int stageWidth = 0;
int wholeWidth = MeasureSpec.getSize(widthMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
final View child = getChildAt(i);
// measure
measureChild(child, widthMeasureSpec, heightMeasureSpec);
stageWidth += (child.getMeasuredWidth() + VIEW_MARGIN);
stageHeight = child.getMeasuredHeight();
if (stageWidth >= wholeWidth) {
stages++;
//reset stageWidth
stageWidth = child.getMeasuredWidth();
}
}
int wholeHeight = (stageHeight + VIEW_MARGIN) * stages;
// report this final dimension
setMeasuredDimension(resolveSize(wholeWidth, widthMeasureSpec),
resolveSize(wholeHeight, heightMeasureSpec));
}
private int jiange = 10;//按钮之间的间隔
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
final int count = getChildCount();
int row=0;// which row lay you view relative to parent
int lengthX=arg1 ; // right position of child relative to parent
int lengthY=arg2; // bottom position of child relative to parent
for(int i=0;i<count;i++){
final View child = this.getChildAt(i);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
if(i == 0){
lengthX+=width+VIEW_MARGIN;//第一个的时候不需要加
}else{
lengthX+=width+VIEW_MARGIN +jiange;//按钮之间的间隔
}
lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2;
//if it can't drawing on a same line , skip to next line
if(lengthX>arg3){
lengthX=width+VIEW_MARGIN+arg1;
row++;
lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2;
}
child.layout(lengthX-width, lengthY-height, lengthX, lengthY);
}
}
}
~~~
### ListView的Adapter
~~~
package jczb.shoping.adapter;
import java.util.ArrayList;
import java.util.HashMap;
import jczb.shoping.common.MyViewGroup;
import jczb.shoping.ui.R;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TableLayout;
import android.widget.TextView;
public class PropertyAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<HashMap<String,Object>> mList;
private ArrayList<HashMap<String,TextView[]>> mViewList;
private Drawable drawableNormal ;
private Drawable drawablePressed;
private Handler mHandler;
//用于保存用户的属性集合
private HashMap<String,String> selectProMap=new HashMap<String, String>();
/**
* 返回选中的属性
* @return
*/
public HashMap<String, String> getSelectProMap() {
return selectProMap;
}
public void setSelectProMap(HashMap<String, String> selectProMap) {
this.selectProMap = selectProMap;
}
public PropertyAdapter(Handler handler,Context context,ArrayList<HashMap<String,Object>> list){
super();
this.mHandler=handler;
this.mContext=context;
this.mList=list;
mViewList=new ArrayList<HashMap<String,TextView[]>>();
drawableNormal=mContext.getResources().getDrawable(R.drawable.tv_property_label);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mList.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return mList.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
// 获取list_item布局文件的视图
convertView = LayoutInflater.from(this.mContext).inflate(R.layout.lv_property_item, null,true);
holder = new ViewHolder();
// 获取控件对象
holder.tvPropName= (TextView) convertView
.findViewById(R.id.tv_property_name);
//holder.llPropContents=(LinearLayout)convertView.findViewById(R.id.ll_property_content);
//holder.tlPropContents=(TableLayout)convertView.findViewById(R.id.ll_property_content);
// 设置控件集到convertView
holder.vgPropContents= (MyViewGroup) convertView.findViewById(R.id.myviewgroup);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (this.mList != null) {
//HashMap<String,TextView[]> mapView=new HashMap<String, TextView[]>();
ArrayList<String> lables = (ArrayList<String>) this.mList.get(position).get("lable");
String type = (String) this.mList.get(position).get(
"type");
holder.tvPropName.setText(type);//规格名称
//动态加载标签
//判断布局中的子控件是否为0,如果不为0,就不添加了,防止ListView滚动时重复添加
if(holder.vgPropContents.getChildCount()==0){
TextView[] textViews = new TextView[lables.size()];
//设置每个标签的文本和布局
//TableRow tr=new TableRow(mContext);
for (int i = 0; i < lables.size(); i++) {
TextView textView = new TextView(mContext); textView.setGravity(17);
textView.setPadding(25,15,25,15);
textViews[i] = textView;
textViews[i].setBackgroundResource(R.drawable.tv_property_label);
textViews[i].setText(lables.get(i));
textViews[i].setTag(i);
//textViews[i].setBackgroundColor(Color.parseColor("#EE5500"));
//tr.addView(textViews[i]);
// holder.llPropContents.addView(textViews[i]);
holder.vgPropContents.addView(textViews[i]);
}
//holder.tlPropContents.addView(tr);
//绑定标签的Click事件
for(int j=0;j<textViews.length;j++){
textViews[j].setTag(textViews);
textViews[j].setOnClickListener(new LableClickListener(type));
}
//把控件存起来
// mapView.put(type, textViews);
// mViewList.add(mapView);
}
/**判断之前是否已选中标签*/
if(selectProMap.get(type)!=null){
for(int h=0;h<holder.vgPropContents.getChildCount();h++){
TextView v=(TextView) holder.vgPropContents.getChildAt(h);
if(selectProMap.get(type).equals(v.getText().toString())){
v.setBackgroundColor(Color.parseColor("#EE5500"));
v.setTextColor(Color.parseColor("#FFFFFF"));
selectProMap.put(type, v.getText().toString());
}
}
}
}
return convertView;
}
/*定义item对象*/
public class ViewHolder {
TextView tvPropName;
LinearLayout llPropContents;
MyViewGroup vgPropContents;
TableLayout tlPropContents;
}
class LableClickListener implements OnClickListener{
private String type;
public LableClickListener(String type){
this.type=type;
}
@Override
public void onClick(View v) {
TextView[] textViews=(TextView[])v.getTag();
TextView tv=(TextView)v;
for(int i=0;i<textViews.length;i++){
//让点击的标签背景变成橙色,字体颜色变为白色
if(tv.equals(textViews[i])){
textViews[i].setBackgroundColor(Color.parseColor("#EE5500"));
textViews[i].setTextColor(Color.parseColor("#FFFFFF"));
selectProMap.put(type, textViews[i].getText().toString());
}else{
//其他标签背景变成白色,字体颜色为黑色
//textViews[i].setBackgroundDrawable(drawableNormal);
textViews[i].setBackgroundResource(R.drawable.tv_property_label);
textViews[i].setTextColor(Color.parseColor("#000000"));
}
}
}
}
}
~~~
### 总结
> 这里关键就是实现自定义的ViewGroup,重写onMeasure和onLayout方法,判断新添加的控件有没有超出屏幕的宽度来决定是否要换行。
【Android开发—电商系列】(一):ListView,就这么美
最后更新于:2022-04-01 10:02:16
### 导读
在本篇文章中,你将学到:
- 如何实现广告位和列表的整体下拉刷新。
- ListView的两层嵌套。
- 如何让ListView中的一行显示多个Item。
-
### 先睹为快
项目的首页主要分为两部分:
1. 上部的轮播广告位
1. 下部的商品展示区,在每个商品类别(如,美食,服装,办公用品)下,最多展示6个商品。
> 效果图:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d234137.jpg "")
不同的分类:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d28117f.jpg "")
### 布局思路
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d2bf0a3.jpg "")
描述:每个ListView都由HeadView、主体和FooterView组成。
整体:PullToRefreshListView
广告位:PullToRefreshListView的HeaderView
商品展示区:PullToRefreshListView的主体
每个商品大类(如美食类):PullToRefreshListView的一个Item
每个大类下的六个商品:ExpandedListView
每个商品:ExpandedListView的Item
### 实现
### 布局文件
首页布局:active_main.xml
~~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_weight="1"
tools:context=".MainActivity" >
<!-- 顶部搜索框 -->
<LinearLayout
android:id="@+id/main_top_layout"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_alignParentTop="true"
android:background="@color/logoColor"
android:focusableInTouchMode="true"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<ImageView
android:id="@+id/main_top_logo"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:src="@drawable/main_top_logo" />
<EditText
android:id="@+id/main_search_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:layout_weight="1"
android:background="@drawable/bg_searchbox"
android:focusable="false"
android:focusableInTouchMode="true"
android:hint="查询商品"
android:padding="6dp"
android:textColor="@color/darkgray"
android:textSize="12sp"
android:windowSoftInputMode="stateVisible|adjustPan" />
<ImageView
android:id="@+id/index_search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp" />
</LinearLayout>
<!-- 广告位和商品分类列表(整体都在一个下拉刷新的ListView当中) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="45dp">
<jczb.shoping.widget.PullToRefreshListView
android:id="@+id/product_floor_listview"
style="@style/widget_listview"
android:scrollbars="none" />
</LinearLayout>
</RelativeLayout>
~~~
HeaderView布局:main_advertisement_header.xml
~~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="136dp"
android:background="#00FFFFFF">
<!-- 广告位中可以左右滑动的控件 -->
<android.support.v4.view.ViewPager
android:id="@+id/adv_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
</LinearLayout>
<!-- 广告位中的小圆点 -->
<LinearLayout
android:id="@+id/viewGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="115dp"
android:layout_marginRight="10dp"
android:gravity="right"
android:orientation="horizontal"
>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
~~~
商品大类Item布局:main_lv_item.xml
~~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFFFF">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
>
<!-- 商品分类名称 -->
<TextView
android:id="@+id/tv_category_name"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="美食"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dp"
android:layout_marginTop="7dp"
android:textSize="20dp"
/>
<ImageView
android:id="@+id/iv_category_color"
android:layout_width="7dp"
android:layout_height="match_parent"
android:background="#55D155"/>
<!--更多箭头 -->
<ImageView
android:id="@+id/iv_product_more"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_alignParentRight="true"
android:layout_marginTop="14dp"
android:layout_marginRight="5dp"
android:layout_marginLeft="3dp"
android:src="@drawable/tb_icon_actionbar_back1" />
<!--更多文本 -->
<TextView
android:id="@+id/tv_product_more"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="8dp"
android:layout_toLeftOf="@id/iv_product_more"
android:layout_marginTop="10dp"
android:text="更多"
android:textSize="16dp"
/>
</RelativeLayout>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#C0C0C0"
/>
<!--商品大类 -->
<jczb.shoping.adapter.ExpandedListView
android:id="@+id/elv_main_category"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#FFFFFFFF"
android:listSelector="#00000000" >
</jczb.shoping.adapter.ExpandedListView>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#C0C0C0"
/>
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="#F6F9FE"
/>
</LinearLayout>
~~~
每个商品的Item布局:main_lv_item_item.xml
~~~
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<!--每行展示 2个商品-->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="250dp"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" >
<ImageView
android:id="@+id/iv_main_product_1"
android:layout_width="match_parent"
android:layout_height="170dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:background="#FFFFFF"
android:scaleType="fitXY"
/>
<TextView
android:id="@+id/tv_main_productName_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_main_product_1"
android:layout_alignLeft="@id/iv_main_product_1"
android:layout_marginBottom="5dp"
android:layout_marginLeft="5dp"
android:textSize="13dp"
android:lines="2"
android:ellipsize="end"
/>
<TextView
android:id="@+id/tv_main_productPrice_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_main_productName_1"
android:layout_marginLeft="5dp"
android:textColor="#E2572D"
android:textSize="14dp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="2dp"
android:layout_height="fill_parent"
android:background="#FFFFFF"
>
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" >
<ImageView
android:id="@+id/iv_main_product_2"
android:layout_width="match_parent"
android:layout_height="170dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:background="#FFFFFF"
android:scaleType="fitXY" />
<TextView
android:id="@+id/tv_main_productName_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_main_product_2"
android:layout_marginLeft="5dp"
android:textSize="13dp"
android:lines="2"
android:ellipsize="end"
android:layout_marginBottom="5dp"
/>
<TextView
android:id="@+id/tv_main_productPrice_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_main_productName_2"
android:layout_marginLeft="5dp"
android:textColor="#E2572D"
android:textSize="14dp"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>
~~~
### ListView
- PullToRefreshListView(此处省略)
- ExpandedListView
~~~
package jczb.shoping.adapter;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.ListView;
/**
* 用于ListView的嵌套,这个ListView当中可以嵌套ListView
* @author xuchenyang
* 2015年11月9日21:43:01
*/
public class ExpandedListView extends ListView{
public ExpandedListView(Context context) {
super(context);
setBackgroundColor(Color.argb(100, 0, 55, 55));
}
public ExpandedListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public ExpandedListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
~~~
### Adapter
- PullToRefreshListView的Adapter
~~~
product_floor_listview.setAdapter(new BaseAdapter() {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View inflate = getLayoutInflater().inflate(R.layout.main_lv_item, null);
/*设置商品分类名称*/
//获得显示商品分类的控件
TextView tvCategoryName=(TextView)inflate.findViewById(R.id.tv_category_name);
//从数据集合中获得分类名称
String strCategoryName=lstHomePage.get(position).getName();
//设置控件文本
tvCategoryName.setText(strCategoryName);
/*设置商品分类名称的颜色*/
ImageView ivCategoryColor=(ImageView)inflate.findViewById(R.id.iv_category_color);
//黄,绿,红,蓝
String[] colorValues={"#FFFF77","#55D155","#DB3031","#6278D9"};
int colorIndex=position % 4;
int colorValue=Color.parseColor(colorValues[colorIndex]);
ivCategoryColor.setBackgroundColor(colorValue);
/*设置每个分类下的商品*/
List<Product> lstProduct=lstHomePage.get(position).getProduct();
ListView listView = (ListView) inflate.findViewById(R.id.elv_main_category);
productAdapter=new MainListViewAdapter(MainActivity.this,lstProduct);
listView.setAdapter(productAdapter);
return inflate;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Object getItem(int position) {
return lstHomePage.get(position);
}
/**
* 显示的商品类别数
*/
@Override
public int getCount() {
return lstHomePage.size();
}
});
/*添加下拉刷新事件*/
product_floor_listview.setOnRefreshListener(new OnRefreshListener(){
@Override
public void onRefresh() {
// Do work to refresh the list here.
new GetDataTask().execute();
}
});
}
~~~
- MyListViewAdapter
~~~
package jczb.shoping.adapter;
import java.util.List;
import jczb.shoping.common.UIHelper;
import jczb.shoping.model.Product;
import jczb.shoping.model.URLs;
import jczb.shoping.ui.ProductsInfoActivity;
import jczb.shoping.ui.R;
import net.tsz.afinal.FinalBitmap;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/**
* 首页每个分类下商品展示的ListView的Adapter
* @author xuchenyang
* 2015年11月9日20:11:10
*/
public class MainListViewAdapter extends BaseAdapter {
//某个商品分类下商品集合
private List<Product> list;
private Context context;
private FinalBitmap fb;
public MainListViewAdapter(Context context, List<Product> list) {
this.context = context;
fb = FinalBitmap.create(context);
this.list = list;
}
/**
* 获得记录的行数
*/
@Override
public int getCount() {
// 按每行2个显示,算出共显示的行数
if (list.size() % 2 == 0) {
return list.size() / 2;
}
return list.size() / 2 + 1;
}
/**
* 获得每个条目项
*/
@Override
public Object getItem(int position) {
return list.get(position);
}
/**
* 获得每个条目的Id
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* 获得每个条目的视图
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = View.inflate(context, R.layout.main_lv_item_item, null);
//商品的图片
viewHolder.iv_product1 = (ImageView) convertView
.findViewById(R.id.iv_main_product_1);
viewHolder.iv_product2 = (ImageView) convertView
.findViewById(R.id.iv_main_product_2);
//商品名称
viewHolder.tv_name1=(TextView)convertView.findViewById(R.id.tv_main_productName_1);
viewHolder.tv_name2=(TextView)convertView.findViewById(R.id.tv_main_productName_2);
//商品价格
viewHolder.tv_price1=(TextView)convertView.findViewById(R.id.tv_main_productPrice_1);
viewHolder.tv_price2=(TextView)convertView.findViewById(R.id.tv_main_productPrice_2);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
int position1 = position *2;
int position2 = position * 2 + 1;
Product product1=list.get(position1);
/********每行第一个产品的设置********/
//显示产品图片
String pic1 = URLs.imageUrlPrefix+product1.getPro_img();
fb.display(viewHolder.iv_product1, pic1);
//假数据
//viewHolder.iv_product1.setImageResource(R.drawable.clothing_04);
//显示产品名称
viewHolder.tv_name1.setText(product1.getPro_name());
//显示产品价格
viewHolder.tv_price1.setText("¥"+product1.getPro_price());
//把该产品的位置设为图片的tag值,为的是点击图片时能获得它的位置
//viewHolder.iv_product1.setTag(position1);
//把商品的Id保存到Tag中
viewHolder.iv_product1.setTag(product1.getUid());
/***********每行第二个产品的设置*******/
if (position2 < list.size()) {
Product product2=list.get(position2);
String pic2 =URLs.imageUrlPrefix+product2.getPro_img();
fb.display(viewHolder.iv_product2, pic2);
//viewHolder.iv_product2.setImageResource(R.drawable.clothing_0108);
//显示产品名称
viewHolder.tv_name2.setText(product2.getPro_name());
//显示产品价格
viewHolder.tv_price2.setText("¥"+product2.getPro_price());
//viewHolder.iv_product2.setTag(position2);
viewHolder.iv_product2.setTag(product2.getUid());
} else {
//viewHolder.iv_product2.setVisibility(View.GONE);
}
viewHolder.iv_product1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UIHelper.ToastMessage(context, v.getTag().toString());
//获得产品uid
int uid=Integer.parseInt(v.getTag().toString());
StartProductInfoActivity(uid);
}
});
viewHolder.iv_product2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(v.getTag()!=null){
UIHelper.ToastMessage(context, v.getTag().toString());
//获得产品uid
int uid=Integer.parseInt(v.getTag().toString());
StartProductInfoActivity(uid);
}
}
});
return convertView;
}
/**
* 每个条目项的视图集合
* @author xuchenyang
*
*/
class ViewHolder {
private ImageView iv_product1, iv_product2;
private TextView tv_name1,tv_name2,tv_price1,tv_price2;
}
/**
* 启动商品详情页
* @param uid 商品id
*/
public void StartProductInfoActivity(int uid){
Intent intent=new Intent();
intent.putExtra("uid", uid);
intent.setClass(context, ProductsInfoActivity.class);
context.startActivity(intent);
}
}
~~~
### 总结
- 实现广告位和列表的整体下拉刷新:
将广告位作为ListView的HeaderView。
- ListView的两层嵌套:
嵌套进去的ListView要重新实现OnMeasure方法,否则会报错。
- 如何让ListView中的一行显示多个Item:
在Adapter的getCount方法中进行处理。
基本的ListView我想大家都会实现,但是在这个用户体验度要求很高的Android世界里,怎么能不玩出一点花样呢?ListView,原来可以展现出这么好看的界面,太棒了!有没有豁然开朗的感觉?所以尽情想象吧,我已经陶醉了……
### 附:[下载源码](http://download.csdn.net/detail/u010924834/9270967)
【Android开发—智能家居系列】(四):UDP通信发送指令
最后更新于:2022-04-01 10:02:13
### 思路回顾
[【1】手机连接WIFI模块](http://blog.csdn.net/u010924834/article/details/49668623)
【2】UDP通信对WIFI模块发送指令,以和WIFI模块保持连接状态
【3】UDP通信对WIFI模块发送指令,让其搜索可用的无线网,返回WIFI列表
【4】发送指令,让WIFI模块接入指定路由
【5】手机连接路由
【6】发送指令,获得WIFI模块的动态IP地址
### UDP通信线程类
~~~
package com.jczb.smartlife.common;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import android.os.Handler;
import android.os.Message;
import com.jczb.smartlife.common.Tool;
public class GetInfoThread extends Thread {
private Handler handler;
private DatagramSocket socket;
private int msgType;
private final String IP = "255.255.255.255";//广播地址
private int PORT = 26000;
/**
* 48899端口:C32x系列的端口,用户可以用AT指令更改
* 49000端口:除C32x系列,其他WIFI模块的端口
* 1902端口:有人掌控宝系列产品的端口
*/
private int targetPort = 49000 ;
private boolean receive = true;
/**
*
* @param handler 传入监听此线程的Handler
* @param intMsg 传入监听的消息类型
*/
public GetInfoThread(Handler handler,int msgType) {
this.handler = handler;
this.msgType=msgType;
init();
}
public void init(){
try {
socket = new DatagramSocket(null);
socket.setBroadcast(true);
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(PORT));
} catch (SocketException e) {
e.printStackTrace();
sendErrorMsg("Search Thread init fail");
return;
}
}
public void run() {
if (socket == null) {
return;
}
try {
byte[] data = new byte[1024];
//创建一个空的DatagramPacket对象
DatagramPacket revPacket = new DatagramPacket(data, data.length);
while (receive) {
//服务端接收数据
socket.receive(revPacket);
if(null!=handler){
byte[] realData = new byte[revPacket.getLength()];
System.arraycopy(data, 0, realData,0, realData.length);
Message msg =handler.obtainMessage(msgType,realData);
handler.sendMessage(msg);
}
}
} catch (Exception e) {
e.printStackTrace();
socket.close();
}
}
public void close() {
if (socket == null)
return;
socket.close();
}
private void sendErrorMsg(String info){
}
/**
* 发送数据
* @param msg
*/
public void sendMsg(byte[] msg) {
if (socket != null) {
try {
System.out.println("targetPort------------------->"+targetPort);
DatagramPacket sendPacket = new DatagramPacket(msg, msg.length,
InetAddress.getByName(IP), targetPort);
socket.send(sendPacket);
} catch (UnknownHostException e) {
e.printStackTrace();
System.out.println("发送失败");
} catch (IOException e) {
e.printStackTrace();
System.out.println("发送失败");
}
}
}
public void setReceive(boolean receive) {
this.receive = receive;
}
public void setTargetPort(int targetPort) {
this.targetPort = targetPort;
}
public void setMsgType(int msgType){
this.msgType=msgType;
}
}
~~~
### 发送消息的线程类
~~~
/**
* 发送消息的队列,每次发送数据时,只需要调用putMsg(byte[] data)方法
*
* @author usr_liujinqi
*
*/
private class SendMsgThread extends Thread {
// 发送消息的队列
private Queue<byte[]> sendMsgQuene = new LinkedList<byte[]>();
// 是否发送消息
private boolean send = true;
private GetInfoThread ss;
public SendMsgThread(GetInfoThread ss) {
this.ss = ss;
}
public synchronized void putMsg(byte[] msg) {
// 唤醒线程
if (sendMsgQuene.size() == 0)
notify();
sendMsgQuene.offer(msg);
}
public void run() {
synchronized (this) {
while (send) {
// 当队列里的消息发送完毕后,线程等待
while (sendMsgQuene.size() > 0) {
byte[] msg = sendMsgQuene.poll();
if (ss != null)
ss.sendMsg(msg);
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void setSend(boolean send) {
this.send = send;
}
}
~~~
### 应用
【举例】发送搜索WIFI模块的指令
~~~
//1.用户获得温控器中WIFI模块的WIFI模块的IP地址,MAC地址,模块名称的指令
private final byte[] getInfoCode=new byte[]{(byte)0x48,0x46,0x2D,0x41,0x31,0x31,0x41,0x53,0x53,0x49,0x53,0x54,0x48,0x52,0x45,0x41,0x44};
public static final int REC_Module=0x04;//搜索WIFI模块的信息(包括IP、Mac、名称)
//实例化一个线程,用户获得模块信息(IP,Mac,名称)
//参数为监听为此线程的Handler,以及接收成功后,给Handler发送的消息类型
getInfoThread = new GetInfoThread(handler,Tool.REC_Module);
getInfoThread.start();
//发送消息的线程
smt = new SendMsgThread(getInfoThread);
smt.start();
//设置发送的目的端口号
getInfoThread.setTargetPort(Integer.parseInt("48899"));
smt.putMsg(getInfoCode);
~~~
> WIFI模块在接收到指令后,就会回复信息,以下的Handler就是针对回复过来的信息进行解析处理等操作。
~~~
//处理消息的Handler
private Handler handler= new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case Tool.REC_Module:// 解析接收到的数据
//设置发送的目的端口号
getInfoThread.setTargetPort(Integer.parseInt("48899"));
getInfoThread.setMsgType(Tool.REC_OK);
smt.putMsg(okCode);
SetServer();
SetDHCP();
byte[] data = (byte[]) msg.obj;
//将十进制的数据转化成十六进制数据
String strTemp= Tool.bytesToHexString(data);
//将十六进制的字符串去掉空格之后进行解析
String strdecode=Tool.DecodeHex(strTemp.replace(" ", ""));
//取出IP,Mac地址,模块名称
decodeIP(strdecode);
Toast.makeText(ConnectWifiActivity.this, "获得WIFI模块名称成功!"+ModuleName, Toast.LENGTH_SHORT).show();
break;
case Tool.REC_Server:
byte[] dataServer = (byte[])msg.obj;
//将十进制的数据转化成十六进制数据
String strServer= Tool.bytesToHexString(dataServer);
if("2b 6f 6b 0d 0a 0d 0a ".equals(strServer)){
Toast.makeText(ConnectWifiActivity.this, "设置服务器成功!", Toast.LENGTH_SHORT).show();
}
break;
case Tool.REC_SSID:
byte[] dataSSID=(byte[])msg.obj;
Tool.bytesToHexString(dataSSID);
decodeData(dataSSID);
break;
case Tool.REC_AT:
byte[] dataID = (byte[]) msg.obj;
//将十进制的数据转化成十六进制数据
String strTempID= Tool.bytesToHexString(dataID);
//将十六进制的字符串去掉空格之后进行解析
String strdecodeID=Tool.DecodeHex(strTempID.replace(" ", ""));
break;
case Tool.REC_DHCP:
byte[] dataDHCP = (byte[])msg.obj;
//将十进制的数据转化成十六进制数据
String strDHCP= Tool.bytesToHexString(dataDHCP);
if("2b 6f 6b 0d 0a 0d 0a ".equals(strDHCP)){
Toast.makeText(ConnectWifiActivity.this, "设置DHCP网络参数成功!", Toast.LENGTH_SHORT).show();
}
break;
default:
byte[] data1 = (byte[]) msg.obj;
//将十进制的数据转化成十六进制数据
String strTemp1= Tool.bytesToHexString(data1);
Toast.makeText(ConnectWifiActivity.this, strTemp1, Toast.LENGTH_SHORT).show();
break;
}
}
};
~~~
### 总结
凡是需要对WIFI模块发送指令的,就需要用到上述的两个线程类,还有一个对返回信息进行处理的Handler。只是发送的指令的code不一样,如上述表示的是搜索WIFI模块的十六进制的指令。不管WIFI模块在AP模式下还是STA模式,通信的最开始步骤都是先搜索模块,然后获得它的IP和Mac之后,立即回复+ok指令,就可以保持连接状态。
### Demo下载
[UDP通信发送指令Demo](http://download.csdn.net/detail/u010924834/9271385)
【Android开发—智能家居系列】(三):手机连接WIFI模块
最后更新于:2022-04-01 10:02:11
### 概述
实现连接WIFI的功能会用到一个工具类,源码可以点击链接下载。网上这些类似的工具类里的代码差不多是一样的。连接无线网主要有两个方法:
其中有一个Connect方法,还有一个connectSpecificAP方法,对于不需要密码就能链接的WIFI模块,我使用的是后者。
### 主要步骤
连接WIFI大致分为一下几个步骤:
【1】打开WIFI
openWifi
【2】配置网络信息
createWifiInfo返回WIFiConfig
【3】添加配置好的网络并连接
int netID = mWifiManager.addNetwork(wifiConfig);
boolean bRet = mWifiManager.enableNetwork(netID, false);
【4】判断连接是否成功
上面的enableNetWork方法返回成功并不能反映手机是否真的连接成功,所以还需要调用isConnect方法进行判断,针对自己的业务逻辑和场景,我又写了一个判断是否连接成功的方法:isWifiConnected的方法
### 应用
~~~
/**
* 手机接入模块的WIFI网络——BLACKANTS
*/
private void ConnectDisplay(){
//获得消息对象
Message msg=mainHandler.obtainMessage();
//是否连接成功的一个标记
Boolean isConnected=false;
//标志BLACKANTS的ScanResult是否存在,true存在,false不存在
Boolean isExist=false;
//用户存放BLACKANTS的WIFI信息
ScanResult srWifiInfo=null;
//声明一个用于临时存放SSID的变量
String strTempSSID;
/******************以下一段代码主要是为了获得BLACKANTS的ScanResult(包括了它的SSID,BSSID,capabilities)*****************/
//扫描WIFI
wifiAdmin.startScan();
//获得WIFI列表
List<ScanResult> lstWiFi= wifiAdmin.getWifiList();
//如果WIFI列表为空,则说明WIFI开关未打开,向Handler发送消息
if(lstWiFi==null || lstWiFi.size()==0){
msg.what=NotOpen;
mainHandler.sendMessage(msg);
return;
}
//如果列表存在,则对列表进行遍历
if(lstWiFi!=null & lstWiFi.size()>0){
//遍历列表,查看BLACKANTS是否存在
for(int i=0;i<lstWiFi.size();i++){
strTempSSID=lstWiFi.get(i).SSID;
//如果存在,则退出For循环
if((DisplaySSID).equals(strTempSSID.trim()) ){
//修改标志位为存在
isExist=true;
//将BLACKANTS的Wifi信息放入到变量srWifiInfo中
srWifiInfo=lstWiFi.get(i);
break;
}
}
}
/******************以上一段代码主要是为了获得BLACKANTS的ScanResult(包括了它的SSID,BSSID,capabilities)*****************/
//默认是失败
msg.what=Failure;
//如果存在,则让手机接入BLACKANTS-------------------V1.0
if(isExist){
if(wifiAdmin.connectSpecificAP(srWifiInfo)){
//判断是否连接上
if(wifiAdmin.isWifiConnected(InitActivity.this,DisplaySSID)){
msg.what=Success;
}
}
}
mainHandler.sendMessage(msg);
}
~~~
### 附:
[下载工具类(WIFIAdmin)](http://download.csdn.net/detail/u010924834/9271281)
【Android开发—智能家居系列】(二):用手机对WIFI模块进行配置
最后更新于:2022-04-01 10:02:09
>在实际开发中,我开发的这款APP是用来连接温控器,并对温控器进行控制的。有图为证,哈哈。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d14cd88.jpg "")
上一篇文章[【Android开发—智能家居系列】(一):智能家居原理 ](http://blog.csdn.net/u010924834/article/details/49491349)的文末总结中写到:
手机APP控制智能温控器就两步:一是通过手机,让WIFI模块接入网络,而是通过网络,使用手机对模块发送指令。在这篇文章中,我们来介绍第一个步骤。
### 【时序图】
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d1cfa41.jpg "")
### 【概念】
### 【两种模式】:
> AP:即无线接入点,是一个无线网络的中心节点。通常使用的无线路由器就是一个AP,其它无线终端可以通过AP相互连接。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d1e15c3.jpg "")
STA:即无线站点,是一个无线网络的终端。如笔记本电脑、PDA等。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d209719.jpg "")
### 【两个端口】
> 48899端口:我所用的WIFI模块(USR-WIFI232-S)的端口
49000端口:除去我用的WIFI(USR-WIFI232-S)模块,其他WIFI模块的端口
### 【IP地址】
> 广播地址:X.X.X.255(如:255.255.255.255)
WIFI模块默认IP:10.10.100.254
接入路由器之后,由路由器动态分配的IP。
### 【实现过程描述】
> 使用UDP广播方式搜索wifi模块
- 第一阶段是在WIFI模块处于AP模式下,对其进行搜索。
- 第二阶段是在WIFI模块处于STA模式下,对其进行搜索。
第一阶段:
模块工作在AP模式下时,会开启一个用于接收“快速联网协议命令”的UDP端口,端口号为48999(出厂设置默认IP为10.10.100.254)
1. 通过UDP广播(广播地址:x.x.x.255,端口48899),发送HF-A11ASSISTHREAD(该口令可用AT+ASWD命令设置)
(这里也可以用单播的形式搜索,目的地址:10.10.10.254,端口48899,命令不变)
1. 模块收到指令后,如果口令正确,向源地址(单播:端口48899)发送IP地址、Mac地址和模块名称。
1. 收到模块返回的IP地址及MAC地址后,立即回送一个“+ok”,模块收到后进入连接状态。
1. 进入连接状态后,模块可以正常接收网络AT指令。 如:设置网络协议参数:AT+NETP=TCP,CLIENT,30000,X.X.X.X 设置STA的网络参数:AT+WANN=DHCP
过渡阶段:
1. UDP广播(端口:49000)发送指令ff 00 01 01 02(十六进制)
1. 模块收到指令后返回路由列表
1. 将用户选择的SSID(路由名称)和密钥发送给模块(端口:49000),模块连入此路由,转化成STA模式,并回送一个指令ff 00 03 82 01 01 87
**第二阶段:**
在STA模式下,我们要找到此WIFI模块,并对其发送开、关等操作指令。步骤同第一阶段,但是返回的IP地址会不一样。模块作为STA链接到路由中,以为此时模块的IP地址是由路由器分配的,不是确定IP地址,所以手机无法与模块建立连接,故需要采用广播搜索,获取到模块在STA模式下的地址。
### 【总结】
有了基本的思路,了解并熟悉整个连接和通信过程,接下来就是比较简单的和基础的部分了,包括UDP通信以及字符和十六进制之间的转换等基本功了。整个开发过程中,对以上内容的理解是一点一点来的,刚开始组长给我讲一遍什么都不明白,给了我一堆资料和工具也不知道怎么用,但是开发着开发着就搞清楚了。开发过程中还请教了WIFI模块生产公司的工程师,解决了我的问题。遇到问题时,还是要多想办法,总会有解决的办法,看你有没有决心和信心要把它解决出来。
【Android开发—智能家居系列】(一):智能家居原理
最后更新于:2022-04-01 10:02:06
来到JCZB公司的第二天,就接到了开发类似于小米智能家庭APP的任务。组长让我在手机上安装上此款APP,给了我个小米智能插座,就让我开始了解需求。这便开启了我的智能家居旅程。说实话,我也真是out的无边无际,智能家居的概念起源很早,我却对它一无所知。所以一边在心里悄悄嘀咕“自己能完成任务吗?”,一边就开始查各种相关资料进行脑补。
### 【简介】
智能家居是在互联网的影响之下物联化体现。智能家居通过[物联网技术](http://baike.baidu.com/link?url=HQmd3TD1gF_k30gj9gM4c28g-lNSin1bJuCjEkUDtkz7U1V2_j_pbmFHwlWuHrJh2y3bHs978lkZ9cY-DbrmsPT9scRqlRwJ7oZ6d6ttF-a)将家中的各种设备连接到一起,提供各种控制或者定时控制的功能和手段。与普通家居相比,智能家居不仅具有传统的家庭居住功能,同时还兼备建筑、网络通信、信息家电、设备自动化功能,提供全方位的信息交互功能。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d0e3b1e.jpg "")
### 【两种方式】
实现智能控制的方法,我了解到的有两种:
一种是通过wifi或者2G、3G、4G网络将手机和智能设备连接起来,实现打开,关闭,设定等操作。
一种是通过蓝牙将手机和智能设备连接起来,实现设备的打开、关闭等操作。
### 【原理】
现在我以智能插座为例,按第一种方式(通过WIFI)介绍一下智能家居实现原理。
[WIFI插座(智能插座)](http://baike.baidu.com/link?url=gSn2EOQdu5voJgQrE6LR6djs4MDNmgG-ZG7aytl2gDnMKM0rD2JXSC76fB6dAEQVN8dIpCr8qz_0MGGQiwtTPK)工作原理:
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d10aa51.jpg "")
**注:**WIFI插座中嵌入了[WIFI模块](http://baike.baidu.com/link?url=3qkbZjHfwLXFRnePO8L8IhTOaC1nCdFEr-CrggPrFErFNUygF1T1Ov9bHYFEzsZi7QeXM8vi5etnPwI4KhzXZ_)
①.通过手机端APP中的配置程序,配置模块要连接的路由器的名称(SSID)和密钥;
②. 将WIFI模块连接路由器,路由器通过外网连接到服务器,如小米的服务器。完成连接工作。
③.手机等控制终端连接远程服务器,下达命令;
④.远程服务器将用户指令下发给住宅中的WiFi插座
⑤.WiFi插座完成相应的通、断电动作。
这样说,大家可能还是感觉比较晕乎。比如,我家里有个WIFI插座,我在第一次使用时用手机将WIFI插座和家里的路由连接好,家里的路由也能通外网。保证WIFI插座与路由,路由与外网之间的通信没有问题。然后在我下班到家之前,我想做一壶热水,我首先要把插座开关打开。然后我就打开手机上的APP,只要手机连着网(WIFI或者2G、3G、4G网都可以),点击界面上的“开”的按钮就可以让家里的插座通电了。
### 【总结】
智能家居系统
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d12b132.jpg "")
智能家居为我们提供了很多方便。炎炎夏日,在到家之前就把空调打开,进门之后,我们就能享受阵阵清凉。整个过程其实可看作两个阶段:一是通过手机,让WIFI模块接入网络,而是通过网络,使用手机对模块发送指令。
【Android系列】—入门实例(Activity+Intent)
最后更新于:2022-04-01 10:02:04
### 实例功能和效果
> 在主界面输入两个乘数,点击Button按钮,跳到另一个界面并显示运算结果。
点击菜单按钮显示退出和关于的菜单,点击退出菜单,退出程序。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d0b8480.jpg "")
### 步骤
### 准备
【1】新建Android Application Project
【2】新建2个Activity(MainActivity.java +Result.java)
【3】AndroidManifest.xml中配置Activity
【4】新建并编写布局文件(activity_main.xml+result.xml)
### 代码
【AndroidManifest.xml】
~~~
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activitycontrols"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Result"
android:label="@string/result" >
</activity>
</application>
</manifest>
~~~
【activity_main.xml】
~~~
<!--线性布局,且是垂直方向 android:orientation="vertical" -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android1="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:context="com.example.activitycontrols.MainActivity" >
<!--输入框输入第一个乘数 -->
<EditText
android:id="@+id/factorOne"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<!--显示文本:"乘以" -->
<TextView
android:id="@+id/symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<!--输入框输入第二个乘数 -->
<EditText
android:id="@+id/factorTwo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<!--计算按钮 -->
<Button
android:id="@+id/calculate"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
~~~
【result.xml】
~~~
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android1="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:context="com.example.activitycontrols.MainActivity" >
<!--显示运算结果 -->
<TextView
android:id="@+id/result"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
~~~
【strings.xml】
> 将控件中要显示的文本写到配置文件中,便于更改。
~~~
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">ActivityControls</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="result">Result</string>
<!--文本 -->
<string name="symbol">乘以</string>
<!--Button按钮的名称 -->
<string name="calculate">计算</string>
<!--菜单项的名称 -->
<string name="exit">退出</string>
<string name="about">关于</string>
</resources>
~~~
【MainActivity.java】
- 实现Button(计算)按钮的Click事件,并将数据传给Result.java
- 在界面添加退出和关于的菜单项,当选中“退出”该菜单项时,程序退出。
~~~
package com.example.activitycontrols;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
private EditText factorOne=null;
private EditText factorTwo=null;
private TextView symbol=null;
private Button myButton=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置该Activity的布局页面
setContentView(R.layout.activity_main);
//获得控件
factorOne= (EditText)findViewById(R.id.factorOne);
factorTwo=(EditText)findViewById(R.id.factorTwo);
symbol=(TextView)findViewById(R.id.symbol);
myButton=(Button)findViewById(R.id.calculate);
//设置控件的值,写死到程序中
// symbol.setText("乘以");
// myButton.setText("计算");
//设置控件的值,写到strings。xml中
symbol.setText(R.string.symbol);
myButton.setText(R.string.calculate);
//将监听器的对象绑定到按钮对象上面
myButton.setOnClickListener(new CalculateListener());
}
//当客户点击Menu按钮的时候,调用该方法,Menu不用在布局页中写
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//添加啊菜单项
menu.add(0,1,1,R.string.exit);
menu.add(0,2,2,R.string.about);
return super.onCreateOptionsMenu(menu);
}
//当客户点击菜单当中的某一个选项时,会调用该方法
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//当单击第一项(退出)时,退出程序
if(item.getItemId()==1){
finish();
}
return super.onOptionsItemSelected(item);
}
//设置Button按钮的监听器
class CalculateListener implements OnClickListener{
@Override
public void onClick(View v) {
//取得两个EditText控件的值
String factorOneStr=factorOne.getText().toString();
String factorTwoStr=factorTwo.getText().toString();
//将两个值存放到Intent对象当中
Intent intent=new Intent();
intent.putExtra("one", factorOneStr);
intent.putExtra("two", factorTwoStr);
intent.setClass(MainActivity.this,Result.class);
//使用这个Intent对象来启动Result
MainActivity.this.startActivity(intent);
//使用这个Intent对象来启动ResultActivity
}
}
}
~~~
【Result.java】
- 接收MainActivity传来的数据,并计算,将结果显示到界面上
~~~
package com.example.activitycontrols;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
public class Result extends Activity {
private TextView resultView=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.result);
//1.获得界面上的控件
resultView=(TextView)findViewById(R.id.result);
//2.得到Intent对象当中的值
Intent intent=getIntent();
//3.根据键来取值
String factorOne=intent.getStringExtra("one");
String factorTwo=intent.getStringExtra("two");
//4.转化成数值
int intFactorOne=Integer.parseInt(factorOne);
int intFactorTwo=Integer.parseInt(factorTwo);
//计算两个值的积
int result=intFactorOne*intFactorTwo;
//将值显示出来,result是整型,当它连接一个字符串时也会被转变为字符串
resultView.setText(result+"");
}
}
~~~
### 总结
【1】设置Activity的布局
~~~
setContentView(R.layout.result);
~~~
【2】获取控件及控件属性、设置控件属性
~~~
resultView=(TextView)findViewById(R.id.result);
symbol.setText("乘以");
symbol.setText(R.string.symbol);
String factorOneStr=factorOne.getText().toString();
~~~
【3】创建菜单和选中菜单项
~~~
public boolean onCreateOptionsMenu(Menu menu) {
//添加啊菜单项
menu.add(0,1,1,R.string.exit);
menu.add(0,2,2,R.string.about);
return super.onCreateOptionsMenu(menu);
}
public boolean onOptionsItemSelected(MenuItem item) {
//当单击第一项(退出)时,退出程序
if(item.getItemId()==1){
finish();
}
return super.onOptionsItemSelected(item);
}
~~~
【4】Intent的使用
~~~
//将两个值存放到Intent对象当中
Intent intent=new Intent();
//键值对的形式
intent.putExtra("one", factorOneStr);
intent.putExtra("two", factorTwoStr);
//设置intent传递方向:从MainActivity到Result
intent.setClass(MainActivity.this,Result.class);
//使用这个Intent对象来启动Result
MainActivity.this.startActivity(intent);
//2.得到Intent对象当中的值
Intent intent=getIntent();
//3.根据键来取值
String factorOne=intent.getStringExtra("one");
String factorTwo=intent.getStringExtra("two");
~~~
【Android系列】—Android世界全景观
最后更新于:2022-04-01 10:02:02
作为一个程序媛,能够不断的接触新鲜的事物,感到十分的幸福和快乐。这次,有幸来到Android的世界。下面就让我们一起来领略一下Android世界的风景吧!
### Android系统架构
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d01aeb4.jpg "")
我们从下往上看:
- Linux Kernel
操作系统。作为硬件和软件之间的抽象层,它隐藏具体硬件细节,而为上层提供统一的服务。负责和硬件交互。
- Libraries
核心库。Android包含一个C/C++库的集合,供Android系统的各个组件使用。这些功能通过Android的应用程序框架(Application Framework)暴露给开发者。
- Android Runtime
安卓运行时系统。在.net中运行时系统是CLR,Java的运行时系统是虚拟机。这里对安卓运行时系统不多做解释。不过,特地查了一下运行时系统的概念:也叫运行时环境,实现了一个计算机语言的核心行为。除了要支持语言的低级行为之外,一个运行时系统还要实现更高层次的行为,甚至要执行类型检查,调试,或者代码生成与优化。一般运行时系统跑在操作系统之上,为上层应用程序提供更高级,更抽象的服务。比如说,操作系统的API接口一般比较简单,常常是些C函数,而某些运行时系统却可以向上层直接提供面向对象的编程和调用接口。
- Application Framework
开发平台。提供一组服务。使开发者能够利用它编制极其丰富和新颖的应用程序。
- Applications
应用程序的集合,就是我们可以看到的一个个软件。
### 精髓
Android思想:随时随地为每个人提供信息。
### 四大组件
- Activity
应用程序界面,软件的一个个界面,可视化的,就像“WebForm”。
- Intent
应用程序之间传递数据。
- Service
提供服务支持。
- ContentProvider
接口,提供数据。
【注】:小编刚入门,Service和ContentProvider还没使用,学了之后再回来对他们的概念再补充。
### 开发工具和环境
#### 1.工具
Eclipse或Android Studio
#### 2.环境搭建
SDK+(ADT)+AVD/真机SDK:Software Develop Kit ,包括一些基础类库,调试工具和命令等。ADT:Eclipse插件,提升安卓开发效率。如果用AndroidStudio开发,这个省略。AVD:Android Virtual Device,Android模拟器。如果在PC机上运行Android程序必须有模拟器,否则就要用自己的手机测试程序。
#### 3.搭建环境采用的方式
上面的东西单独下载,下载ADT特别慢建议:下载集成好的Eclipse,只需再安装SDK和system image(创建虚拟机时用到)
[附:下载链接](http://www.androiddevtools.cn/)
———
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d03cd9b.jpg "")
———
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d05bb81.jpg "")
———
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d070dfb.jpg "")
### 总结
最后,附上一张图,感谢大家的阅读。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-17_56ea50d092679.jpg "")
前言
最后更新于:2022-04-01 10:02:00
> 原文出处:[安卓项目实践](http://blog.csdn.net/column/details/android1010.html)
>作者:[u010924834](http://blog.csdn.net/u010924834)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 安卓项目实践
> 通过智能家居和校园购物两个APP开发的亲身实战,介绍安卓开发中的常用技巧和功能实现。