安卓开发笔记(十一)—— 方向传感器,实现摇一摇动画&音效,GPS定位

版权声明: https://blog.csdn.net/dickdick111/article/details/85254883

中山大学数据科学与计算机学院本科生实验报告

(2018年秋季学期)

项目源码:Github传送门


一、实验题目

第十六周任务

传感器


第十六周实验目的

  1. 学会使用加速度传感器
  2. 学会使用地磁传感器
  3. 学会获取经纬度
  4. 学习动画效果

二、实现内容

实现一个简单的传感器应用

img打开程序主页面 img摇一摇晃动手机展示动画与震动与弹Toast提示并跳转。其中展示图片与动画属于加分项可不做
img页面2,展示指南针和经纬度,可以点击跳转返回上一界面 img其中数值和图片需要动态变化,指南针的图片的指向需要大致正确
  • 该项目属于选作项目
  • 页面2的数值和图片需要动态变化
  • 整体验收流程如上,需要保证跳转→返回→再跳转后应用不会崩溃且显示正确(如经纬度朝向发生改变后再次跳转显示的是改变后的值)
  • 加分项为摇一摇动画效果,可不做,不做的主界面居中展示“摇一摇跳转”即可
  • 跳转的动画可以自由发挥,有能力增加音效的也可以加入

三、实验结果

(1)实验截图

  1. 手机初始页面显示

1

  1. 摇动手机后,展示动画以及Toast,并实现了音效

2

  1. 进入地图的初始状态
    3

  2. 改变手机的位置,显示经纬度的实时改变

4

  1. 改变手机的方向,改变图标以及旋转角度

5

(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等事件。

扫描二维码关注公众号,回复: 5318823 查看本文章

动画与音乐的部分待加分项处详述。

/*传感器事件监听器*/
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.感想

虽然这次实验是选做,但是本人对安卓开发方向也颇有兴趣,且传感器方面的内容在日常生活也是经常用到,所以就完成这一次的实验。实验过程并不难,主要也只是注册传感器,判断传感器返回数据,监听事件处理等等。遇到的困难多在动画与音效方面,以及一些权限的设置。这次不仅学习到传感器的用法,还对平移动画,旋转动画等有了一定的了解,更加清楚的了解活动生命周期。

猜你喜欢

转载自blog.csdn.net/dickdick111/article/details/85254883
今日推荐