zoukankan      html  css  js  c++  java
  • 中级实训Android学习记录——实训报告

    软工实训报告

    17326032 林文锋 18级软件工程

    实验环境

    实验完成时间:2020/12/23 18:00

    Windows 10系统详细配置如下:

    img

    笔记本详细配置如下:

    img

    Android Studio版本:

    image-20201223183633302

    其他信息如工程gradle信息都在工程文件中可以找到。

    实验摘要

    通过对少量百度地图API的调用以及对传感器的使用,达到摇一摇定位的目的

    实验目的

    • 学会使用加速度传感器
    • 学会使用地磁传感器
    • 学会获取经纬度
    • 接入百度地图 API
    • 掌握少量的百度地图 API 接口

    实验内容

    • 初始界面仍为摇一摇
    • 跳转后的界面为百度地图
    • 地图定位在目前的经纬度

    增加内容:

    • 摇一摇跳转至附近的娱乐场所
    • 记录摇一摇得到的娱乐场所列表
    • 点击摇一摇得到的娱乐场所可以调用百度地图API的步行导航功能

    实验步骤

    一、 获取百度地图API使用权限,申请百度地图AK

    获取开发密钥(AK)

    image-20201213224019543

    • 通过android studio的AndroidManifest.xml文件查看PackageName

      image-20201213224429114

    • 通过命令行查看SHA1(默认没有密码,口令直接回车即可)

      image-20201213224542526

      上面找到的其实是debug(开发板SHA1),发布版SHA1其实是另一种方法:

      1. 使用任意android studio工程生成apk,步骤:Build->Build Bundle(s)/APK(s)->Build APK(s)

        image-20201223184820207

      2. 在工程目录/app/build/outputs/apk/debug中更改apk的后缀为zip文件并解压

        image-20201223184858249

      3. 进入解压后的META-INF目录

        image-20201223184942741

      4. 在META-INF目录下打开cmd,输入命令 :keytool -printcert -file CERT.RSA 这里将会显示出MD5和SHA1签名

        image-20201223185132764

      5. 使用上面的SHA1重新申请key,PackageName:AndroidMainifest.xml中的包名。

    • 输入得到的PackageName和SHA1,并点击提交

      image-20201213224644442

    • 就得到了访问应用的AK

      image-20201213224723188

    二、 使用百度地图API 显示地图、定位

    1. 应用申请权限

      <!-- 以下权限开启地图服务 -->
      <!-- 访问网络,进行地图相关业务数据请求,包括地图数据,路线规划,POI检索等 -->
      <uses-permission android:name="android.permission.INTERNET" /> <!-- 获取网络状态,根据网络状态切换进行数据请求网络转换 -->
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 读取外置存储。如果开发者使用了so动态加载功能并且把so文件放在了外置存储区域,则需要申请该权限,否则不需要 -->
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 写外置存储。如果开发者使用了离线地图,并且数据写在外置存储区域,则需要申请该权限 -->
      <uses-permission android:name="android.permission.VIBRATE" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      
      <!-- 以下权限用于开启定位服务 -->
      <!-- 这个权限用于进行网络定位 -->
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <!-- 这个权限用于访问GPS定位 -->
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      
      <!-- 以下权限用于开启定位服务 -->
        <service android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote"/>
      
    2. 初始化SDK组件

      //在使用SDK各组件之前初始化context信息,传入ApplicationContext
      SDKInitializer.initialize(this);
      //自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
      //包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
      SDKInitializer.setCoordType(CoordType.BD09LL);
      

      注意最好在任何其他组件都没有使用百度地图API之前就初始化

    3. 使用百度地图API

      1. 初始化

                // ----------------------------------- 初始化百度地图 --------------------------------- //
        
                mMapView = findViewById(R.id.bmapView);
                mTextView = findViewById(R.id.tv);
        
                mPoiSearch = PoiSearch.newInstance();
                mPoiSearch.setOnGetPoiSearchResultListener(this);
        
                // 更改地图类型
                mBaiduMap = mMapView.getMap();
                mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
        
                // 设置定位服务开始
                mBaiduMap.setMyLocationEnabled(true);
        
                //定位初始化
                mLocationClient = new LocationClient(getApplicationContext());
        
                //通过LocationClientOption设置LocationClient相关参数
                LocationClientOption option = new LocationClientOption();
                option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);  // 定位模式是仅限设备模式,也就是仅允许GPS来定位。
                option.setOpenGps(true); // 打开gps
                option.setCoorType("bd09ll"); // 设置坐标类型
                option.setScanSpan(1000);
                //设置打开自动回调位置模式,该开关打开后,期间只要定位SDK检测到位置变化就会主动回调给开发者
        
                option.setIsNeedAddress(true);
                //可选,是否需要地址信息,默认为不需要,即参数为false
                //如果开发者需要获得当前点的地址信息,此处必须为true
                option.setNeedNewVersionRgc(true);
                //可选,设置是否需要最新版本的地址信息。默认需要,即参数为true
        
        
                // 自定义定位指针
        //        MyLocationConfiguration.LocationMode mCurrentMode = MyLocationConfiguration.LocationMode.COMPASS;
        //        BitmapDescriptor mCurrentMarker = BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher_background);
        //        MyLocationConfiguration myLocationConfiguration = new MyLocationConfiguration(mCurrentMode, true,
        //                mCurrentMarker, 0xAAFFFF88, 0xAA00FF00);
        //        mBaiduMap.setMyLocationConfiguration(myLocationConfiguration);
        
                //设置locationClientOption
                mLocationClient.setLocOption(option);
        
                //注册LocationListener监听器
                MyLocationListener myLocationListener = new MyLocationListener();
                mLocationClient.registerLocationListener(myLocationListener);
        
                //开启地图定位图层
                mLocationClient.start();
        
                // 搜索时不重复,使用HashSet来支持
                mHashLocStr = new HashSet<>();
        
                // 一开始搜索的半径和接收的结果数
                mRadius = 1000;
                mPageCapacity = 10;
        
      2. 声明地址更改Listener并在Listener中更新位置信息

        public class MainActivity extends AppCompatActivity implements OnGetPoiSearchResultListener, SensorEventListener{
            ...
        
            /**
             * LocationListener 不断接收定位的回调并改变当前的位置信息
             */
            public class MyLocationListener extends BDAbstractLocationListener {
                private  boolean isFirstLocate = true;
        
                @Override
                public void onReceiveLocation(BDLocation location) {
                    //mapView 销毁后不在处理新接收的位置
                    if (location == null || mMapView == null){
                        return;
                    }
        
                    mBDLocation = location;
                    addressAdapter.setStartLocation(mBDLocation);
        
                    // 如果是第一次定位
                    LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
                    if (isFirstLocate) {
                        isFirstLocate = false;
                        //给地图设置状态
                        mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLng(ll));
                    }
        
                    // 编辑LocData并改变当前地图的信息
                    MyLocationData locData = new MyLocationData.Builder()
                            .accuracy(location.getRadius())
                            // 此处设置开发者获取到的方向信息,顺时针0-360
                            .direction(location.getDirection()).latitude(location.getLatitude())
                            .longitude(location.getLongitude()).build();
        
                    mBaiduMap.setMyLocationData(locData);
                    Log.d("0", "onReceiveLocation: 定位到 " + location.getAddrStr());
        
                    mCity = location.getCity();
        
                    // 显示当前信息
                    StringBuilder stringBuilder = new StringBuilder();
                    stringBuilder.append("
        当前地址:" + location.getAddrStr());
                    stringBuilder.append("
        经度:" + location.getLatitude());
                    stringBuilder.append("
        纬度:"+ location.getLongitude());
        
        //            stringBuilder.append("
        状态码:"+ location.getLocType());
        //            stringBuilder.append("
        国家:" + location.getCountry());
        //            stringBuilder.append("
        城市:"+ location.getCity());
        //            stringBuilder.append("
        区:" + location.getDistrict());
        //            stringBuilder.append("
        街道:" + location.getStreet());
        
                    mTextView.setText(stringBuilder.toString());
                }
            }
        }
        

        MainActivity声明为OnGetPoiSearchResultListener来支持Poi检索功能,声明MyLocationListener来支持定位功能

      3. 生命周期管理

            @Override
            protected void onResume() {
                mMapView.onResume();
                super.onResume();
            }
        
            @Override
            protected void onPause() {
                mMapView.onPause();
                // 务必要在pause中注销 mSensorManager
                // 否则会造成界面退出后摇一摇依旧生效的bug
                if (mSensorManager != null) {
                    mSensorManager.unregisterListener(this);
                }
                super.onPause();
            }
        
            @Override
            protected void onDestroy() {
                mLocationClient.stop();
                mBaiduMap.setMyLocationEnabled(false);
                mMapView.onDestroy();
                mMapView = null;
                super.onDestroy();
            }
        
        

        在进行百度地图API的使用,需要特别注意生命周期的管理,否则可能造成定位不准,后台耗电量巨大的隐患。

    三、 整合摇一摇来调用百度地图API

    1. 初始化摇一摇组件

      // ---------------------------------初始化摇一摇--------------------------------------- //
      
              mHandler = new MyHandler(this);
      
              //初始化SoundPool
              mSoundPool = new SoundPool(1, AudioManager.STREAM_SYSTEM, 5);
              mWeiChatAudio = mSoundPool.load(this, R.raw.weichat_audio, 1);
              //获取Vibrator震动服务
              mVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
      
              // 摇一摇的两个分离界面
              mTopLayout = findViewById(R.id.top_layout);
              mBottomLayout = findViewById(R.id.bottom_layout);
      
              //获取 SensorManager 负责管理传感器
              mSensorManager = ((SensorManager) getSystemService(SENSOR_SERVICE));
              if (mSensorManager != null) {
                  //获取加速度传感器
                  mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
                  if (mAccelerometerSensor != null) {
                      mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_UI);
                  }
              }
      

      摇一摇调用sensorManager来监听手机的位置变化,如果三维的位置变化达到一定程度,我们就认为手机被摇晃了,此时监听位置变化的函数是onSensorChanged函数,在这个函数中进行调整即可。

    2. 调用传感器来调用百度地图API和动画

          @Override
          public void onSensorChanged(SensorEvent event) {
              int type = event.sensor.getType();
      
              if (type == Sensor.TYPE_ACCELEROMETER) {
                  //获取三个方向值
                  float[] values = event.values;
                  float x = values[0];
                  float y = values[1];
                  float z = values[2];
      
                  if ((Math.abs(x) > 17 || Math.abs(y) > 17 || Math
                          .abs(z) > 17) && !isShake) {
                      isShake = true;
                      Thread thread = new Thread() {
                          @Override
                          public void run() {
      
                              super.run();
                              try {
                                  Log.d(TAG, "onSensorChanged: 摇动");
                                  searchPoiNearBy();
                                  //开始震动 发出提示音 展示动画效果
                                  mHandler.obtainMessage(START_SHAKE).sendToTarget();
                                  Thread.sleep(500);
                                  //再来一次震动提示
                                  mHandler.obtainMessage(AGAIN_SHAKE).sendToTarget();
                                  Thread.sleep(500);
                                  mHandler.obtainMessage(END_SHAKE).sendToTarget();
      
      
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          }
                      };
                      thread.start();
                  }
              }
          }
         @Override
         public void onAccuracyChanged(Sensor sensor, int accuracy) {
         }
      
         /**
          * handler 接收摇一摇产生的信息并作出对应的操作
          * START_SHAKE 开始摇晃
          * AGAIN_SHAKE 没结束摇晃之前再次摇晃
          * END_SHAKE 结束摇晃
          */
         private static class MyHandler extends Handler {
             private WeakReference<MainActivity> mReference;
             private MainActivity mActivity;
                public MyHandler(MainActivity activity) {
             mReference = new WeakReference<MainActivity>(activity);
             if (mReference != null) {
                 mActivity = mReference.get();
             }
         }
      
         @Override
         public void handleMessage(Message msg) {
             super.handleMessage(msg);
             switch (msg.what) {
                 case START_SHAKE:
                     //This method requires the caller to hold the permission VIBRATE.
                     mActivity.mVibrator.vibrate(300);
                     //发出提示音
                     mActivity.mSoundPool.play(mActivity.mWeiChatAudio, 1, 1, 0, 0, 1);
      
                     mActivity.startAnimation(false);//参数含义: (不是回来) 也就是说两张图片分散开的动画
                     break;
                 case AGAIN_SHAKE:
                     mActivity.mVibrator.vibrate(300);
                     break;
                 case END_SHAKE:
                     //整体效果结束, 将震动设置为false
                     mActivity.isShake = false;
                     // 展示上下两种图片回来的效果
                     mActivity.startAnimation(true);
                     break;
             }
         }
      }
      

      如上代码,我们在onSensorChanged中调用了调用百度地图API的函数searchPoiNearBy来寻找附近的娱乐场所:

          // 搜索附近的娱乐设施
          private void searchPoiNearBy() {
              String cityStr = mCity;
              // 获取检索关键字
              String keyWordStr = "娱乐";
              if (TextUtils.isEmpty(cityStr) || TextUtils.isEmpty(keyWordStr)) {
                  return;
              }
      
              LatLng ll = new LatLng(mBDLocation.getLatitude(), mBDLocation.getLongitude());
      
              // 搜索附近的娱乐场所
              mPoiSearch.searchNearby((new PoiNearbySearchOption())
                      .location(ll)
                      .keyword(keyWordStr)
                      .pageCapacity(mPageCapacity)
                      .pageNum(0)
                      .radius(mRadius));
          }
      

      该函数通过API searchNearby来查找附近的娱乐设施,最后通过本身的查找结果函数onGetPoiResult来定位到该位置并加入到recycler view中去:

          @Override
          // 在poiSearch完成之后对得到的结果进行处理并展示到recycler View中
          public void onGetPoiResult(PoiResult poiResult) {
              List<PoiInfo> poiInfos = poiResult.getAllPoi();
              if (poiInfos.size() <= 0 || poiResult.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
                  Toast.makeText(MainActivity.this, "未找到结果", Toast.LENGTH_LONG).show();
                  // 没找到,说明附近娱乐场所很少
                  mRadius *= 2;
                  mPageCapacity *= 2;
                  return;
              }
      
              // 将地图平移到 latLng 位置
              int index = (int)(Math.random() * poiInfos.size()) % poiInfos.size();
              PoiInfo poiInfo = poiInfos.get(index);
      
              int isSelected = 0;
              for (int i = 0; i < poiInfos.size(); i ++)
              {
                  index = (int)(Math.random() * poiInfos.size()) % poiInfos.size();
                  poiInfo = poiInfos.get(index);
                  if (!mHashLocStr.contains(poiInfo.getName()))
                  {
                      isSelected = 1;
                      break;
                  }
              }
      
              if (isSelected == 0)
              {
                  // 找到了但是差不多都输出过,说明已经摇了很多次
                  mRadius *= 2;
                  mPageCapacity *= 2;
                  Toast.makeText(this, "你是真的挑三拣四,建议卸载本APP", Toast.LENGTH_SHORT).show();
                  return;
              }
      
              // 加入HashSet以避免重复
              mHashLocStr.add(poiInfo.getName());
              // 加入AddressAdapter以显示摇出的地址
              addressAdapter.addItem(poiInfo);
      
              // 定位到摇到的地址的位置
              LatLng latLng = poiInfos.get(index).getLocation();
              MapStatusUpdate mapStatusUpdate = MapStatusUpdateFactory.newLatLng(latLng);
              mBaiduMap.setMapStatus(mapStatusUpdate);
      
              // 添加指示标志
              MarkerOptions markerOptions = new MarkerOptions()
                      .position(poiInfo.getLocation())
                      .icon(mBitmapDescWaterDrop);
      
              InfoWindow infoWindow = getPoiInfoWindow(poiInfo);
              markerOptions.infoWindow(infoWindow);
      
              Marker marker = (Marker) mBaiduMap.addOverlay(markerOptions);
          }
      
          // 在摇到的地址上方显示当前地址的名字
          private InfoWindow getPoiInfoWindow(PoiInfo poiInfo) {
              TextView textView = new TextView(this);
              textView.setText(poiInfo.getName());
              textView.setPadding(10, 5, 10, 5);
              textView.setBackground(this.getResources().getDrawable(R.drawable.bg_info));
              InfoWindow infoWindow = new InfoWindow(textView, poiInfo.getLocation(), -150);
              return infoWindow;
          }
      

      至于RecyclerView就是简单的线性view对ArrayList进行的封装。

    3. 生命周期管理

      传感器是一个非常耗电的设备,并且极易影响到其他APP的使用,所以它的生命周期管理也十分重要:

      @Override
      protected void onStart() {
          super.onStart();
          //获取 SensorManager 负责管理传感器
          mSensorManager = ((SensorManager) getSystemService(SENSOR_SERVICE));
          if (mSensorManager != null) {
              //获取加速度传感器
              mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
              if (mAccelerometerSensor != null) {
                  mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_UI);
              }
          }
      }
      
      @Override
      protected void onResume() {
          mMapView.onResume();
          super.onResume();
      }
      
      @Override
      protected void onPause() {
          mMapView.onPause();
          // 务必要在pause中注销 mSensorManager
          // 否则会造成界面退出后摇一摇依旧生效的bug
          if (mSensorManager != null) {
              mSensorManager.unregisterListener(this);
          }
          super.onPause();
      }
      
      @Override
      protected void onDestroy() {
          mLocationClient.stop();
          mBaiduMap.setMyLocationEnabled(false);
          mMapView.onDestroy();
          mMapView = null;
          super.onDestroy();
      }
      

      首先,和百度地图API一样的是在APP开始时初始化,在APP关闭时摧毁,不同的是,在应用未被关闭而是切换(Pause)期间,传感器必须被注销,否则在其他APP使用时仍然有摇一摇的功能,在重新回到该APP(Start)时,必须检查传感器的状态以保证摇一摇的功能可用。

    实验结果

    APP图标 APP初始界面
    image-20201223191503921 image-20201223191523333
    摇晃一次 调用百度地图进行导航
    image-20201223191608099 image-20201223191637337
    摇晃三次
    image-20201223191659419
    • 视频演示

    注意事项

    • 得到的APP不能使用Android Studio上的虚拟手机端来跑,因为虚拟的手机是没有GPS的,不能够正常使用定位的功能

    • 得到的源码不一定能够在其他电脑上跑的通,因为百度地图API限制了SHA1和包名,所以如果想要正常的使用本APP,可以使用项目工程目录中./app/build/outputs/apk/debug/app-debug.apk这个文件来安装并运行

      或者通过更改AndroidManifest.xml中的百度地图APIkey,用自己的百度地图APIkey来调试本源码

  • 相关阅读:
    Conntect Bluetooth devices in iOS.
    Why NSAttributedString import html must be on main thread?
    IOS7 SDK 几宗罪
    How to browse the entire documentation using XCode 5 Documentation and API Reference ?
    High Precision Timers in iOS / OS X
    [UWP]抄抄《CSS 故障艺术》的动画
    [Microsoft Teams]使用连接器接收Azure DevOps的通知
    [WPF 自定义控件]自定义一个“传统”的 Validation.ErrorTemplate
    [WPF 自定义控件]在MenuItem上使用RadioButton
    [WPF 自定义控件]创建包含CheckBox的ListBoxItem
  • 原文地址:https://www.cnblogs.com/lwfing/p/14180760.html
Copyright © 2011-2022 走看看