显示标签为“Android”的博文。显示所有博文
显示标签为“Android”的博文。显示所有博文

2016年1月18日星期一

android:layout_gravity 和 android:gravity 的区别

为了item的小部件调整操碎了心,发现一篇好文章。转载之,原链接在这里

gravity 这个英文单词是重心的意思,在这里就表示停靠位置的意思。
android:layout_gravity 和 android:gravity 的区别
从名字上可以看到,android:gravity是对元素本身说的,元素本身的文本显示在什么地方靠着换个属性设置,不过不设置默认是在左侧的。
android:layout_gravity是相对与它的父元素说的,说明元素显示在父元素的什么位置。
比如说button: android:layout_gravity 表示按钮在界面上的位置。 android:gravity表示button上的字在button上的位置。

可选值
这两个属性可选的值有:top、bottom、left、right、center_vertical、fill_vertical、center_horizontal、fill_horizontal、center、fill、clip_vertical。
而且这些属性是可以多选的,用“|”分开。
默认这个的值是:Gravity.LEFT
对这些属性的描述:
出自:
ValueDescription
topPut the object at the top of its container, not changing its size.
将对象放在其容器的顶部,不改变其大小.
bottomPut the object at the bottom of its container, not changing its size.
将对象放在其容器的底部,不改变其大小.
leftPut the object at the left edge of its container, not changing its size.
将对象放在其容器的左侧,不改变其大小.
rightPut the object at the right edge of its container, not changing its size.
将对象放在其容器的右侧,不改变其大小.
center_verticalPlace object in the vertical center of its container, not changing its size.
将对象纵向居中,不改变其大小.
垂直对齐方式:垂直方向上居中对齐。
fill_verticalGrow the vertical size of the object if needed so it completely fills its container.
必要的时候增加对象的纵向大小,以完全充满其容器.
垂直方向填充
center_horizontalPlace object in the horizontal center of its container, not changing its size.
将对象横向居中,不改变其大小.
水平对齐方式:水平方向上居中对齐
fill_horizontalGrow the horizontal size of the object if needed so it completely fills its container.
必要的时候增加对象的横向大小,以完全充满其容器.
水平方向填充
centerPlace the object in the center of its container in both the vertical and horizontal axis, not changing its size.
将对象横纵居中,不改变其大小.
fillGrow the horizontal and vertical size of the object if needed so it completely fills its container. This is the default.
必要的时候增加对象的横纵向大小,以完全充满其容器.
clip_verticalAdditional option that can be set to have the top and/or bottom edges of the child clipped to its container's bounds. The clip is based on the vertical gravity: a top gravity clips the bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
附加选项,用于按照容器的边来剪切对象的顶部和/或底部的内容. 剪切基于其纵向对齐设置:顶部对齐时,剪切底部;底部对齐时剪切顶部;除此之外剪切顶部和底部.
垂直方向裁剪
clip_horizontalAdditional option that can be set to have the left and/or right edges of the child clipped to its container's bounds. The clip is based on the horizontal gravity: a left gravity clips the right edge, a right gravity clips the left edge, and neither clips both edges.
附加选项,用于按照容器的边来剪切对象的左侧和/或右侧的内容. 剪切基于其横向对齐设置:左侧对齐时,剪切右侧;右侧对齐时剪切左侧;除此之外剪切左侧和右侧.
水平方向裁剪
简单记忆 : horizontal 都是操作的水平方向,即横向, vertical 都是炒作的垂直方向,即纵向。
对于LinearLayout何时生效的问题
参看:也谈layout_gravity和gravity
http://www.lephone.net/viewthread.php?tid=325
对于 LinearLayout
当 android:orientation="vertical"  时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。即:left,right,center_horizontal 是生效的。
当 android:orientation="horizontal" 时, 只有垂直方向的设置才起作用,水平方向的设置不起作用。即:top,bottom,center_vertical 是生效的。

2015年8月27日星期四

更改Back建行为

安卓的Back键即返回键,顾名思义就是返回的意思。平常使用返回键,可见其在当前的界面与逻辑上一个界面切换,保证了我们的完整使用体验。

问题来了,在上一篇文章《省市县选择位置页面的逻辑》中,默认状态下,我们按下返回键就会一下子回到了桌面,这可大事不妙。我们想要的是返回逻辑上一层选择列表,县列表->市列表->省列表,这可如何是好呢。

方法来了。

public void onBackPressed() {
if (currentLevel == LEVEL_COUNTY) {
queryCities();
} else if (currentLevel == LEVEL_CITY) {
queryProvinces();
} else {
finish();
}

}


这里我们重定义onBackPressed()方法来覆盖默认返回键的功能。通过判断当前的creerntLevel来决定返回的是哪个选择列表,如当前为县列表(LEVEL_COUNTY),就返回城市查询列表(queryCities())。是不是很机智呢,不得不感谢currentLevel了!

LOL!

省市县选择位置页面的逻辑

现在的APP,涉及到位置查询的,直接就是输入某个城市,甚至某个区就可以直达。如果有些API不支持这样的一键搜索直达,只支持按梯度的检索怎么办呢?比如,我想查找昆山市,只支持  江苏->苏州->昆山 这样的查找逻辑呢?

学习嘛,有时候笨笨的方法还是不得不学。

做一个天气预报APP的时候,就是这么个一步一步从 省->市->县 来检索位置的,看看它的逻辑是怎么实现的吧。废话不多说,先上代码!


public class ChooseAreaActivity extends Activity {

public static final int LEVEL_PROVINCE = 0;
public static final int LEVEL_CITY = 1;
public static final int LEVEL_COUNTY = 2;

private ProgressDialog progressDialog;
private TextView titleText;
private ListView listView;
private ArrayAdapter<String> adapter;
private WeatherDB weatherDB;
private List<String> dataList = new ArrayList<String>();
private List<Province> provinceList;

private List<City> cityList;

private List<County> countyList;

private Province selectedProvince;

private City selectedCity;

private County selestedCounty;

private int currentLevel;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.choose_area);
listView = (ListView) findViewById(R.id.list_view);
titleText = (TextView) findViewById(R.id.title_text);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, dataList);
listView.setAdapter(adapter);
weatherDB = WeatherDB.getInstance(this);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view, int index,
long arg3) {
if (currentLevel == LEVEL_PROVINCE) {
selectedProvince = provinceList.get(index);
queryCities();
} else if (currentLevel == LEVEL_CITY) {
selectedCity = cityList.get(index);
queryCounties();
}

}
});
queryProvinces(); // 加载省级数据
}

/**
* 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器上查询
*/
private void queryProvinces() {
provinceList = weatherDB.loadProvinces();// 从数据库中读取省级数据
if (provinceList.size() > 0) { // 数据库中读到,将数据显示到街面上
dataList.clear();
for (Province province : provinceList) {
dataList.add(province.getProvinceName());
}
adapter.notifyDataSetChanged();
listView.setSelection(0);
titleText.setText("中国");
currentLevel = LEVEL_PROVINCE;
} else { // 数据库中未读到,调用queryFromServer从服务器查询数据
queryFromServer(null, "province");
}
}

/**
* 查询选中省内的城市,优先从数据库中查询,如果没有查询到再去服务器上查询
*/
private void queryCities() {
cityList = weatherDB.loadCities(selectedProvince.getId());
if (cityList.size() > 0) {
dataList.clear();
for (City city : cityList) {
dataList.add(city.getCityName());
}
adapter.notifyDataSetChanged();
listView.setSelection(0);
titleText.setText(selectedProvince.getProvinceName());
currentLevel = LEVEL_CITY;
} else {
queryFromServer(selectedProvince.getProvinceCode(), "city");
}
}

/**
* 查询选中市内所有的县,优先从数据库查询,如果没有查询到再去服务器上查询
*/
private void queryCounties() {
countyList = weatherDB.loadCounties(selectedCity.getId());
if (countyList.size() > 0) {
dataList.clear();
for (County county : countyList) {
dataList.add(county.getCountyName());
}
adapter.notifyDataSetChanged();
listView.setSelection(0);
titleText.setText(selectedCity.getCityName());
currentLevel = LEVEL_COUNTY;
} else {
queryFromServer(selectedCity.getCityCode(), "county");
}
}

private void queryFromServer(final String code, final String type) {
String address;
if (!TextUtils.isEmpty(code)) {
address = "http://www.weather.com.cn/data/list3/city" + code
+ ".xml";
} else {
address = "http://www.weather.com.cn/data/list3/city.xml";
}
showProgressDialog();
// 向服务器发送请求,相应的数据回调到onFinish()方法中
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
boolean result = false;
if ("province".equals(type)) {
result = Utility.handleProvincesResponse(weatherDB,
response);// 解析和处理服务器返回的数据
} else if ("city".equals(type)) {
result = Utility.handleCititesResponse(weatherDB, response,
selectedProvince.getId());
} else if ("county".equals(type)) {
result = Utility.handleCountiesResponse(weatherDB,
response, selectedCity.getId());
}
if (result) {
// 通过runOnUiThread()方法回到主线程处理逻辑,子线程切换到主线程(异步消息处理机制)
runOnUiThread(new Runnable() {
@Override 
public void run() {
closeProgressDialog();
if ("province".equals(type)) {
queryProvinces(); // 再次调用queryProvinces()方法,由于牵涉UI操作,故必须在主线程中调用
} else if ("city".equals(type)) {
queryCities();
} else if ("county".equals(type)) {
queryCounties();
}
}
});
}
}

@Override
public void onError(Exception e) {
// 通过runOnUiThread方法回到主线程处理逻辑
runOnUiThread(new Runnable() {
@Override
public void run() {
closeProgressDialog();
Toast.makeText(ChooseAreaActivity.this, "加载失败",
Toast.LENGTH_LONG).show();
}
});
}
});
}

/**
* 显示进度对话框
*/
private void showProgressDialog() {
if (progressDialog == null) {
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("正在加载...");
progressDialog.setCanceledOnTouchOutside(false);
}
progressDialog.show();
}

/**
* 关闭进度对话框
*/
private void closeProgressDialog() {
if (progressDialog != null) {
progressDialog.dismiss();
}
}

@Override
public void onBackPressed() {
if (currentLevel == LEVEL_COUNTY) {
queryCities();
} else if (currentLevel == LEVEL_CITY) {
queryProvinces();
} else {
finish();
}

}


}


打开应用,这就是我们的首Activity,选择区域位置,然后才好从服务器处理具体的天气预报信息。

1. onCreate()方法中,各种初始化如获取控件示例,初始化ArrayAdapter等(不是本篇文章的重点)后,最后调用queryProvinces()方法,开始加载省级数据。

2. queryProvinces()方法中,weatherDB.loadProvinces()从数据库中读取省级数据,通过provinceList.size() 判断是否存在。若provinceList.size() >0,即读取到数据,将其显示在界面上,更新currentLEVEL为LEVEL_PROVINCE;否则,调用queryFromServer()方法,传入两个参数,分别是code(此时为null)和String type(此处为“province”)

3. queryFromServer()方法,首先根据传入的code拼装好查询地址,然后调用HttpUtil.sendHttpRequest向服务器发送请求,响应的数据会回调到onFinish()中
  
  3.1 onFinish()方法中,条件语句匹配type,当前我们的type为“province”,Utility.handleProvincesResponse解析和处理服务器返回的数据,存储在result中

 3.2 进入if(result),通过runOnUiThread()方法回到主线程处理逻辑,子线程切换到主线程(异步消息处理机制),此时的type仍为“province”,故会再次调用queryProvinces()方法,此时数据库中已经存在了数据,所以此时数据会直接显示在界面上,即省份数据。

4.  当我们选择了某一个具体的省份时,会进入ListView的onItemClick()方法中,此时会根据当前的currentLevel来判断进入哪个query,如,当前currentLevel为LEVEL_PROVINCE,就进入了queryCities()方法中,从此,进入下一次的查询逻辑。这里我们需要注意的时,每次传递的参数在变化。

我的文字叙述应该是比较详尽了,有一个UML图的话会更直观,不过很抱歉,现在就是没有,所以耐心地看代码看注释吧。

LOL! 跑步去了


2015年8月24日星期一

R.layout.XXX无法正常调用

半个月前的项目,写好了XXX布局,这次重新找出来继续完善onCreate部分时,R.layout.XXX却怎么也调用不成功。卡壳半天,做了一发饭回来,一一排除。

XXX布局,名字没错,layout位置没错,重新检查一遍,布局写得很好,没有BUG;

再看Activity部分,只是用Eclipse的自动补全勾勒出了onCreate,onPause等方法,正一一待我去完成;

import部分呢?这里是import了android.R,是不是它的问题呢?先删掉,重新Ctrl+Shift+O,蹦出载入选项,还有另一个是com.包名.app.R,选择之。保存,报错消息不见了,我的问题解决了。

总之,检查import是一个常见的排除Bug的利器。

LOL! 吃饭去了

2015年8月2日星期日

再说Android网络编程——优化

之前有写到HttpURLConnection和HttpClient的用法,知道了如何发起HTTP请求,此乃基本功。随着应用开发学习的深入,面临着这个么一个问题,应用程序不止一个地方会遇上网络功能,而每次都重新编写网络编程部分代码,是一个费力又愚蠢的做法。

所以,我们需要把通用的网络操作提取到一个公共类中,并提供一个静态方法,使用时只需简单调用即可。上代码!

public class HttpUtil {
public static String sendHttpRequest(String address) {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}

}

当需要发起一条HTTP请求时,可以这样写,

String address = "http://www.baidu.com"
String response = HttpUtil.sendHttpRequest(address);

我们获取到服务器的响应后就可以对其进行解析和处理了。但我们需要注意的是,网络请求通常都属于耗时操作,而senHttpRequest()方法内部并没有开启线程,这样可能导致调用sendHttpRequest()方法时使得主线程被阻塞。

倘若我们想在sendHttpRequest()内部开启一个子线程来发起HTTP请求,那么所有的耗时操作逻辑都在子线程内完成,sendHttpReques()方法会在服务器还来得及反应的时候就执行结束了,自然无法返回相应的数据,描述的应该还算比较清楚了吧。所以该怎么解决这个难题呢?

Java的回调机制!

首先,定义一个名为HttpCallbackListener的接口,

public interface HttpCallbackListener {

void onFinish(String response);

void onError(Exception e);

}

onFinish()方法表示当服务器成功相应请求的时候调用,参数response代表服务器返回的数据。onError()方法则表示出现错误时调用,参数e记录错误信息。

修改前边HttpUtil的代码,

public class HttpUtil {

public static void sendHttpRequest(final String address,
final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
if (listener != null) {
// 回调onFinish()方法
listener.onFinish(response.toString());
}
} catch (Exception e) {
if (listener != null) {
// 回调onError()方法
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}

}

这里给sendHttpRequest()方法添加了一个HttpCallbackListener的参数,并在方法内开启了一个子线程,然后在子线程内执行具体的网络操作。注意这里的子线程是无法通过return来返回数据,所以把服务器响应的数据传入HttpCallbackListener的onFinish()方法中,异常错误则传入onError()方法中。

成功达成目的,快来看看怎么使用HttpUtil吧,

HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
// 在这里根据返回内容执行具体的逻辑
}
@Override
public void onError(Exception e) {
// 在这里对异常情况进行处理
}

});

一定注意在使用的时候要把HttpCallbackListener的实例传入!

LOL !




2015年7月31日星期五

定制自己的Log日志工具

为了方便调试,编程的时候写入了很多的Log。如果项目完成的时候,一一删除是一个头疼的事情,可是不删除的话又会输出大量我们不愿展现出来的信息,可能会是机密,况且这也很拖累运行效率。

so, what should we do?

Is there anyway to solve this problem?

很简单,把所有的Log都写进一个单独的类中,类中实现自定义方法用于输出,然后项目中再需要打印的时候再调用此类中方法。不用的时候,单独处理此类即可。貌似描述得有点干吧,上代码!

import android.util.Log;

public class LogUtil {
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
public static final int LEVEL = VERBOSE;

public static void v(String tag, String msg) {
if (LEVEL <= VERBOSE) {
Log.v(tag, msg);
}
}

public static void d(String tag, String msg) {
if (LEVEL <= DEBUG) {
Log.d(tag, msg);
}
}

public static void i(String tag, String msg) {
if (LEVEL <= INFO) {
Log.i(tag, msg);
}
}

public static void w(String tag, String msg) {
if (LEVEL <= WARN) {
Log.w(tag, msg);
}
}

public static void e(String tag, String msg) {
if (LEVEL <= ERROR) {
Log.e(tag, msg);
}
}

}

在LogUtil中定义了VERBOSE、DEBUG、INFO、WARN、ERROR、NOTHING这六个整型常量,其对应的值都是递增的。然后定义一个LEVEL常量,其值可以指定为上面六常量之一。

LogUtil实现了v(),d(),i(),w(),e()五个自定义的Log方法,在其内部分别调用Log.v()等来打印日志。在自定义的方法中加入了if条件判断,当LEVEL常量满足某一条件时才会打印。

这就是Log日志工具。

在项目中,当需要打印DEBUG级别的日志时可以这样写,

LogUtil.d("TAG", "debug log");

控制LEVEL的值可以控制日志的打印行为,如将LEVEL定义为VERBOSE可以将所有的日志都打印出来,使其等于WARN可以将警告以上级别的日志打印出来,使其等于NOTHING可以将所有的日志都屏蔽掉,这就解决了文前提高的问题。

LOL !




Android中的context浅析

在Android系统中,有很多的service。我们的程序如果用到系统功能,一般都是调用服务间接完成的。也就是在Android系统中存在许多C/S架构。context的作用,就是android应用连接service的桥梁。比如Activity中有一个方法,getSystemService()。这个方法调到最后,实际上是调用的ContextImpl的getSystemService()方法,而ContextImpl是对Context的实现。

      Context不是函数而是一个类——如果不太了解面向对象,可以把“类”看做一种数据类型,就像int,不过类型为“类”的数据(称为对象)可能储存远比int多的信息,比如这里的类型为Context的对象就储存关于程序、窗口的一些资源。

        有些函数调用时需要一个Context参数,比如Toast.makeText,因为函数需要知道是在哪个界面中显示的Toast。

        再比如,Button myButton = new Button(this); 这里也需要Context参数(this),表示这个按钮是在“this”这个屏幕中显示的。

        Android开发使用(纯粹的)面向对象语言,一切都是对象,就连我们写的函数都是对象的函数。

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toast.makeText(this,
                "OK!",
                Toast.LENGTH_LONG).show();
        Button button1 = (Button)findViewById(R.id.button1);
        button1.setOnClickListener(new Button.OnClickListener(){
            public void onClick(View v)
            {
                Toast.makeText(MainActivity.this,
                        "Hello, world!",
                        Toast.LENGTH_LONG).show();
            }
        });
    }
}
      这里OnCreate就是MainActivity的对象的函数(MainActivity是类),所以这个函数中的this就表示当前的、包含这个函数的MainActivity对象。

      MainActivity extends Activity,意思是MainActivity 继承 Activity,即MainActivity 是 Activity 的一种,所有的MainActivity 都是 Activity。同样,在Android文档中Activity继承ContextThemeWrapper,ContextThemeWrapper继承ContextWrapper,ContextWrapper继承Context。所以this这个MainActivity也是Context,把this传入Toast.makeText表示“OK!”是在当前的MainActivity对象(也是Context)中显示的。

      对于显示"Hello, world!"的Toast.makeText,这个函数在onClick中,而onClick是new Button.OnClickListener(){...}这个没有名字的类的函数,this表示匿名类的对象,不表示MainActivity对象,所以这里用MainActivity.this,强制选择外面一层MainActivity的this。



2015年7月30日星期四

调用手机光照传感器测光强

娘希匹的,昨天就写好了这个blog,发布的时候翻墙失败,也没保存成功,现在重写。

简单调用手机的光照传感器来检测环境的光强,很easy哒。

Android的每个传感器的用法都很相似,首先要获取SensorManager的实例,

1.获取SensorManager实例

SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

2.调用具体的传感器

SensorManager是系统所有传感器的管理器,有了它的实例后就可以调用getDefultSensor()方法来得到任意的传感器类型,

Sesnor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);//这里是光照传感器

4.对传感器输出的信号进行监听

private SensorEventListener listener = new SensorEventListener() {

@Override
public void onSensorChanged(SensorEvent event) {
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};

这里的SensorEventListener接口定义了两个方法,当传感器精度发生变化时调用onAccuracyChanged方法,当传感器检测到的数值变化时调用onSensorChanged()方法。
onSensorChanged()方法传入了一个名为SensorEvent的参数,其中包含了一个values数组,所有传感器的信息都存放在values数组里。

5.注册传感器的监听器

接下来需要调用SensorManager的registerListener()方法来注册SensorEventListener才能使其生效,这里的registerListener()方法接收了三个参数,第一个为SensorEventListener的实例,第二个为Sensor实例,第三个为传感器输出信息的更新速度,有ENSOR_DELAY_UI、SENSOR_DELAY_NORMAL、SENSOR_DELAY_GAME和SENSOR_DELAY_FASTEST四个可选,它们的更新速度依次递增。

sensorManager.registerListener(listener, sensor,
SensorManager.SENSOR_DELAY_NORMAL);

这就是(光线)传感器的简单用法,接下来上实例吧~~

-----------------------------------------------------

1.布局文件只要一个TextView,简单粗暴,能显示光强即可。

    <TextView
        android:id="@+id/light_level"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        android:layout_centerInParent="true" 
        android:textSize="20sp"/>

2.MainActivity部分代码,

public class MainActivity extends Activity {

private SensorManager sensorManager;

private TextView lightLevel;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lightLevel = (TextView) findViewById(R.id.light_level);// 获取TextView实例
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);// 获取光照传感器实例
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
sensorManager.registerListener(listener, sensor,
SensorManager.SENSOR_DELAY_NORMAL);// 注册光照传感器的监听器
}

@Override
protected void onDestroy() {
super.onDestroy();
if (sensorManager != null) {
sensorManager.unregisterListener(listener);// 释放传感器
}
}

private SensorEventListener listener = new SensorEventListener() {

@Override
public void onSensorChanged(SensorEvent event) {
// values数组中第一个下标的值就是当前的光照强度
float value = event.values[0];
lightLevel.setText("Current light level is " + value + "lx");
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 当传感器光强变化时调用
}
};
}

注意,这里的values中只有一个值——光强,存储在values[0]中。

3.运行之



595.01lx(勒克斯),这个值是我测到的座位附近最大值,它是不断变化的呦,这个要感谢可爱的onSensorChanged()方法~

LOL !

2015年7月29日星期三

百度地图baidumapSDK入门之地图显示

摸索了半下午的baidumapSDK_v3.1.0的使用。

1.申请API KEY

地址http://lbsyun.baidu.com/apiconsole/key

填写安全码的时候注意下,其格式为SHA1+";"+包名,

在eclipse->preferences->android->build可找到SHAI fingerprint,

2.配置SDK

先下载之http://developer.baidu.com/map/index.php?title=androidsdk/sdkandev-download,建议一键下载,jdk和docs文档和sample都有了,想要啥有啥.....其实我想单选下载的时候老下载不成功

lib中的baidumapapi_v3_1_0.jar即jar文件放入工程的libs中,

libBaiduMapSDK_v3_1_0.so放入libs->armeabi中,注意,armeabi需要我们自己新建

Properties->Jaca Build Path->Libraies->add JARs 添加我们的baidumapapi_v3_1_0.jar

Properties->Jaca Build Path->Order and Export选中baidumapapi_v3_1_0.jar

Project clean -> clean all即可

3.在AndroidManifest.xml的application中添加API Key

   <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <meta-data 
            android:name="com.baidu.lbsapi.API_KEY"

            android:value="xzBRnhNM24BPN8bPBQVvaio2"/>
         .......
    </application>

添加所需权限

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_GPS" />

触目惊心好多权限.....

4.布局activity_main.xml文件里简单添加一个地图控件mapView,

  <com.baidu.mapapi.map.MapView
     android:id="@+id/map_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"

     android:clickable="true"/>

5.修改MainActivity.java文件

public class MainActivity extends Activity {

private MapView mapView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 在使用SDK各组件之前初始化context信息,传入ApplicationContext
// 注意该方法要在setContentView方法之前实现
// 注意,在SDK各功能组件使用之前都需要调用SDKInitializer.initialize(getApplicationContext())
// 因此我们建议该方法放在Application的初始化方法中
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
mapView = (MapView) findViewById(R.id.map_view);
}

@Override
protected void onResume() {
super.onResume();
// 在activity执行onResume时执行mapView.onResume(),实现地图生命周期管理
mapView.onResume();

}

@Override
protected void onPause() {
super.onPause();
// 在activity执行onPause时执行mapView.onPause(),实现地图生命周期管理
mapView.onPause();

}

@Override
protected void onDestroy() {
super.onDestroy();
// 在activity执行onDestroy时执行mapView.onDestroy(),实现地图生命周期管理
mapView.onDestroy();
}


}

详细的解释我都写在了代码注释里了,真是一个好习惯

6.运行之,


LOL !

eclipse中devices识别不了手机的adb server didn't ACK问题

经常会遇到电脑连上手机,eclipse中的devices空空如也的情况。娘希匹的,怎么又不识别了,老子的驱动明明安装正常,USB调试也明明打开了,反复开关几次eclipse他奶奶的还是不出来!到底是怎么回事儿,太影响享受APP开发调试成功的快感了——就像射精前后边有人给你踹了一脚立刻萎了精液回流了哪儿都疼尤其蛋疼一样。

我决心解决这个问题!

1.cmd开启控制台!

adb devices  启动adb服务



纳尼,ADB server didn't ACK??!!!

度娘说一般是端口绑定失败,那好,老子再来,adb nodaemon server,查看端口绑定信息,



connot bind 'tcp:5037',果真如此

那再看看是谁占用了5037端口好啦,netstat -ano | findstr "5037"



简直了!

打开任务管理器,查看PID,瞅瞅谁是



好尴尬,竟然是adb.exe,典型的站着茅坑不拉屎,关闭之!

再次运行adb devices,大力出奇迹啦!



重启eclipse,我的亲爱的抹茶回来啦!



总结!

1.遇到问题,首先重启adb.exe,像我这样就饶了一大圈
2.5037可能会被好多坏东东占用,包括豌豆荚91之流,tasklist /fi "pid eq ****" 也可以方便地查询
3.还没想到

LOL !