安卓开发笔记(十一)—— 方向传感器,实现摇一摇动画&音效,GPS定位
中山大学数据科学与计算机学院本科生实验报告
(2018年秋季学期)
一、实验题目
第十六周任务
传感器
第十六周实验目的
- 学会使用加速度传感器
- 学会使用地磁传感器
- 学会获取经纬度
- 学习动画效果
二、实现内容
实现一个简单的传感器应用
|
|
---|---|
|
|
- 该项目属于选作项目
- 页面2的数值和图片需要动态变化
- 整体验收流程如上,需要保证跳转→返回→再跳转后应用不会崩溃且显示正确(如经纬度朝向发生改变后再次跳转显示的是改变后的值)
- 加分项为摇一摇动画效果,可不做,不做的主界面居中展示“摇一摇跳转”即可
- 跳转的动画可以自由发挥,有能力增加音效的也可以加入
三、实验结果
(1)实验截图
- 手机初始页面显示
- 摇动手机后,展示动画以及Toast,并实现了音效
-
进入地图的初始状态
-
改变手机的位置,显示经纬度的实时改变
- 改变手机的方向,改变图标以及旋转角度
(2)实验步骤以及关键代码
a.设置传感器
先获取传感器的管理者,再通过这个管理者获取加速度传感器,这里直接用getDefaultSensor获取缺省即可,里面的参数表示传感器的类型是加速度传感器
//获得传感器的管理器
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
//获得加速度传感器
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
b.注册与注销监听器
这里在页面切换出去的时候,注销监听,减少内存的消耗,释放资源。当切换回来再重新注册这一个监听器,分别在onPause与onResume函数中实现。
@Override
protected void onPause() {
sensorManager.unregisterListener(sensorEventListener);
super.onPause();
}
@Override
protected void onResume() {
//参数一传感器监听 参数二:监听的传感器对象
//注册摇一摇事件
sensorManager.registerListener(sensorEventListener,sensor,SensorManager.SENSOR_DELAY_NORMAL);
super.onResume();
}
c.设置事件监听器
这里重载onSensorChanged函数,当传感器接受到改变时就会触发这个函数。这里实现摇一摇的事件,不是一有轻微的变动就触发,而是需要超过一定的变化的范围。
这里通过事件的返回值来设置变化的范围,当x,y,z三轴的变化有一个大于15的时候,触发摇一摇事件,播放动画,产生震动,显示Toast等事件。
动画与音乐的部分待加分项处详述。
/*传感器事件监听器*/
sensorEventListener = new SensorEventListener() {
//当值发生改变的时候调用
@Override
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;
//获取控件的值,设置触发条件
float x = values[0];
float y = values[1];
float z = values[2];
if(x > 15 || y > 15 || z > 15){//表示摇一摇
if(flag) {//正在执行动画的同时不能再次触发
//播放动画
imageUp.startAnimation(upAnimationSet);
imageDown.startAnimation(downAnimationSet);
soundPool.play(soundId,1.0f,1.0f,1,1,1.0f);
//震动
//long[] pattern 1,第一次震动延迟的时间 2,第一次震动的持续时间
//int repeat震动的重复次数 -1表示不重复
// 仅一次震动,不延迟,震动3s即可。
vibrator.vibrate(new long[]{0,300},-1);
// 显示TOAST
Toast.makeText(MainActivity.this,
"摇一摇",Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
d.实现方向传感器,注册与注销监听器
与上面类似,先获取管理者,再获取传感器。这里需要地磁传感器和加速度传感器这两个。
// 传感器管理者
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
// 地磁传感器和加速度传感器
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
magnetic = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
注册与注销也是类似在onResume与onPause函数中实现
@Override
protected void onResume() {
sensorManager.registerListener(sensorEventListener, accelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(sensorEventListener, magnetic,
SensorManager.SENSOR_DELAY_NORMAL);
super.onResume();
}
@Override
protected void onPause() {
sensorManager.unregisterListener(sensorEventListener);
super.onPause();
}
e.设置事件的监听器
这里两个传感器但是共有同一个事件监听器,所有在触发onSensorChanged的时候要判断是哪一种传感器的事件。这里先用if语句来判断类型,然后将响应的values值clone过去,一定要使用clone函数来赋值,不然accelerometerValues 和 magneticValues 将会指向同一个引用。
接着就是将这些值用getOrientation来获取方向,然后将这个值转化为角度,改变TextView的相应值。
至于图片角度的改变这里需要用到动画RotateAnimation,第一个参数表示开始旋转的角度,第二个参数表示到达的角度。后面四个参数是用来确定旋转中心。
setFillAfter表示动画结束,维持在最后的一帧
// 传感器事件监听器
sensorEventListener = new SensorEventListener() {
//当值发生改变的时候调用
float[] accelerometerValues = new float[3];
float[] maneticValues = new float[3];
private float lastDegree;
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
accelerometerValues = sensorEvent.values.clone();
}
else if (sensorEvent.sensor.getType() ==
Sensor.TYPE_MAGNETIC_FIELD){
maneticValues = sensorEvent.values.clone();
}
float[] R = new float[9];
float[] values = new float[3];
SensorManager.getRotationMatrix(R,null,accelerometerValues,maneticValues);
SensorManager.getOrientation(R,values);
float rotateDegree = -(float) Math.toDegrees(values[0]);
rotate.setText(Float.toString(rotateDegree));
if (Math.abs(rotateDegree - lastDegree) > 1){
RotateAnimation animation = new
RotateAnimation(lastDegree,rotateDegree,
Animation.RELATIVE_TO_SELF,0.5f,
Animation.RELATIVE_TO_SELF,0.5f);
animation.setFillAfter(true);
arrow.startAnimation(animation);
lastDegree = rotateDegree;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
f.位置的获取
先获取位置的管理者,获得LOCATION_SERVICE,然后我使用的是网络定位,这里要注意权限的设置以及开启手机的GPS, 且允许该应用打开位置服务
// 位置管理者
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// 利用网络定位
final String provider = LocationManager.GPS_PROVIDER;
// 设置时间变化频率
// 产生位置改变事件的条件设定为距离改变10米,时间间隔为2秒
locationManager.requestLocationUpdates
(provider, 2000, 10, locationListener);
Location location = locationManager.getLastKnownLocation
(LocationManager.GPS_PROVIDER);
// 更新经纬度
updateLocation(location);
Manifest权限设置
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-feature android:name="android.hardware.location.gps"/>
g.位置变化监听函数
这里仅仅需要在onLocationChanged变化的时候来更新位置即可,这里直接调用我之前设置的函数,更改相应位置的TextView.
// 定义位置的监听函数
locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// 更新位置
updateLocation(location);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
(3)实验遇到的困难以及解决思路
a.监听器获取为null
我在获取管理者后,注册监听器,里面需要用到传感器,但是由于函数的先后顺序,没有进行赋值。故必须在注册监听器前给传感器与管理者赋值。
逻辑顺序如下所示
sensorManager -> sensor -> registerListener -> new SensorEventListener
sensorManager.registerListener(sensorEventListener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
b. 摇一摇后动画未结束,直接跳转
起初,我处理跳转事件是在传感器的监听函数中处理的,当传感器收到信号就进行跳转事件,这样导致动画仍未播放完毕。
正确的处理是在动画AnimationListener中的重构onAnimationEnd来处理跳转的逻辑。
@Override
public void onAnimationEnd(Animation animation) {
flag = true;
// 设置跳转
Intent intent = new Intent();
intent.setClass(MainActivity.this,MapActivity.class);
startActivity(intent);
}
c.跳转后摇一摇页面的传感器仍工作
起初我在onDestroy函数中来注销传感器监听器,却忽略了页面跳转后,起初的页面仍存在活动栈中仍未被destroy,故该传感器的监听函数一直在工作,导致出现我在第二个页面摇动手机也会触发事件。
解决方法:分析活动的进程,在onPause函数中来注销,在onResume函数中来注册
@Override
protected void onPause() {
sensorManager.unregisterListener(sensorEventListener);
super.onPause();
}
四、实验思考及感想
1.加分项
a. 摇一摇产生动画
这里用到了AnimationSet,TranslateAnimation两个类。
然后分别定义上部分图片的向上移动以及向下移动,下部分图片的向下移动以及向上移动这四个动画效果,注意设置顺序与设置动画时间,以及设置动画的延迟时间。
//初始化动画() 两个图片同时进行不能共用,
//图片最终需要回到原点,因此使用补间动画
//上面图片动画集合
// true 表示 几个动画共用一个插值器
upAnimationSet = new AnimationSet(true);
//上面图片动画
//1.先上移
TranslateAnimation upUptranslateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -0.5f);
//设置时间
upUptranslateAnimation.setDuration(500);
//upUptranslateAnimation.setDuration(0);
//1.后下移
TranslateAnimation upDowntranslateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, -0.5f, Animation.RELATIVE_TO_SELF, 0);
upDowntranslateAnimation.setDuration(500);
//upDowntranslateAnimation.setDuration(0);
//设置启动延迟,300ms后开始启动
upDowntranslateAnimation.setStartOffset(300);
upAnimationSet.addAnimation(upUptranslateAnimation);
upAnimationSet.addAnimation(upDowntranslateAnimation);
upAnimationSet.setDuration(1200);
upAnimationSet.setStartOffset(200);
downAnimationSet = new AnimationSet(true);
//下面图片的动画
//1.先上移
TranslateAnimation downUptranslateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0);
downUptranslateAnimation.setDuration(500);
downUptranslateAnimation.setStartOffset(300);
//1.后下移
TranslateAnimation downDowntranslateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0.5f);
downDowntranslateAnimation.setDuration(500);
downAnimationSet.addAnimation(downUptranslateAnimation);
downAnimationSet.addAnimation(downDowntranslateAnimation);
downAnimationSet.setDuration(1200);
downAnimationSet.setStartOffset(200);
动画的播放,两张图片imageView分别利用startAnimation函数执行对应的动画集即可
if(flag) {//正在执行动画的同时不能再次触发
//播放动画
imageUp.startAnimation(upAnimationSet);
imageDown.startAnimation(downAnimationSet);
···
}
b.摇一摇产生音乐效果
这里没有用到我们之前学过的MediaPlayer,而是采用soundPool,soundPool的主要的优点是开销少,反应迅速,一般用于手机的一些短音效。
这里先new一个soundpool然后运用load函数来加载音乐,这里的音乐我存放在raw文件夹中,后面的1表示该音乐只播放一次。
而播放就使用play函数,将这个音乐id资源播放,第二个参数与第三个参数表示左右声道,这里需要设成1.0f,不然在某些机型无法实现播放,第四个参数表示优先级,第五个参数表示循环播放次数,第六个参数表示播放的速度
//int maxStreams参数一:表示音乐池数量
//int streamType 参数二:类型
// int srcQuality参数三:资源的质量
soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
//将音乐加载到soundPool
soundId = soundPool.load(this, R.raw.dingdong, 1);
···
// 播放
soundPool.play(soundId,1.0f,1.0f,1,1,1.0f);
2.感想
虽然这次实验是选做,但是本人对安卓开发方向也颇有兴趣,且传感器方面的内容在日常生活也是经常用到,所以就完成这一次的实验。实验过程并不难,主要也只是注册传感器,判断传感器返回数据,监听事件处理等等。遇到的困难多在动画与音效方面,以及一些权限的设置。这次不仅学习到传感器的用法,还对平移动画,旋转动画等有了一定的了解,更加清楚的了解活动生命周期。