美文网首页
百度地图 SDK(3)---- 界面美化,图层处理

百度地图 SDK(3)---- 界面美化,图层处理

作者: 做梦枯岛醒 | 来源:发表于2017-08-01 13:42 被阅读208次

    上两篇文章我们介绍使用了百度地图的一些功能,这一篇文章我打算在原来的基础上对界面做一些美化,当然这个美化是基于我们百度地图,高德地图的UI界面来处理,我们对这个界面进行模仿。
    我也希望这个Demo最后不仅仅是个Demo,而是能拿的出手的一个像样的APP。
    首先我们看一下百度的主界面。


    百度地图 SDK(3)---- 界面美化,图层处理

    我们分析一下主界面,一个搜索框,一个罗盘图标,一排功能键,一个定位按钮和上报按钮,一个路线按钮和一个底栏。当然我们今天不是从做UI入手,而是先解决一些和地图相关的界面细节。

    1.定位图层相关

    上一篇文章我们实现了定位图层,也就是那个小蓝点,可是小蓝点还是不够的,我们还能更进一步操作。
    先看一下修改后的代码

                lati_ = bdLocation.getLatitude();
                long_ = bdLocation.getLongitude();
                 //显示定位图标
                MyLocationData locData = new MyLocationData.Builder()
                      //  不显示半径圆圈
                        .accuracy(0)
                        // 此处设置开发者获取到的方向信息,顺时针0-360
                        .direction(bdLocation.getDirection()).latitude(lati_)
                        .longitude(long_).build();
               //使用自定义图标
                BitmapDescriptor mIconLocation = BitmapDescriptorFactory.fromResource(R.drawable.main_icon_follow);
             //配置(定位模式,允许显示方向,图标)
                MyLocationConfiguration configuration
                        =new MyLocationConfiguration(LocationMode.COMPASS,true,mIconLocation);
                //设置定位图层配置信息,只有先允许定位图层后设置定位图层配置信息才会生效,参见 setMyLocationEnabled(boolean)
                baidumap.setMyLocationConfiguration(configuration);
                baidumap.setMyLocationData(locData);
    

    首先我们看到我们的 .accuracy(0)是修改过的,这里是控制半径的显示,这里设置0为不显示,
    其次可以看到修改后的代码主要增加了MyLocationConfiguration,
    而我们来详细看一下这个对象构造方法所接受的3个参数,第一个是定位模式,这里主要分为3种模式

    LocationMode.FOLLOWING   //跟随:小圆点箭头显示方向
    LocationMode.NORMAL         //默认:小圆点,没有方向显示
    LocationMode.COMPASS      //罗盘:带东南西北的虚线圈
    

    我们一般会选择罗盘模式,反正我在实际生活中使用地图就觉得罗盘模式很方便,哈哈。(老掉向啦!)

    第二个参数是我们设置是否允许显示方向,true的话是允许,然后我们就可以看到方向,但是还不要着急,这里面有大文章呢。

    第三个是一个图标配置,我这里用了下面这张图


    定位.png

    这个常见的定位图标。当然如果我们传入null,也是会显示这个图标的,只不过是百度带的


    定位图标

    这里我们看到我们的效果是这个样纸滴。(分辨率还可以,我是把他放在xhdpi文件夹里的)

    那么我们再来讨论一下这个方向的问题。
    细心的读者可能已经看到 .direction(bdLocation.getDirection())代码上面的注释了,开发者设置获取到的方向信息,那么这句话的意思是什么?我们不是已经给了bdLocation.getDirection()了么?
    如果有跟着我们一步一步坐下来的可能就会发现,我们的图标根本不会随着方向动,就算不写在if条件里面也不会。确实是这样的。
    因为我们bdLocation.getDirection()是有要求的,那就是option,我们要给他允许,他才能获取方向。

     option.setNeedDeviceDirect(true);   //需要设备方向
    

    当你设置这一句之后(注意暂时把定位图层代码移到if语句外面,我们要看本次的运行结果),你再运行发现,一卡一卡的。 确实因为你设置的定位时间span,导致它一卡一卡不能实时获取方向信息,那我们能怎么解决这个问题呢?

    答案是利用手机自带的方向传感器。

    2017年8月2日13:45:27 继续更新
    昨天陆陆续续的遇到一些小问题,不过现在已经陆续解决了,由于我也是边学边写,所以有什么问题还请大神多多指教。

    那么我们回到正题,我们怎么才能让百度地图和手机传感器结合到一起呢?
    首先肯定是我们的传感信息获取,这部分代码是参考鸿洋大神在慕课视频写的,在这里面我们提供了一个start方法和一个stop方法,分别控制传感器的启动和结束,在启动方法里我们主要是获取方向传感器并注册,stop方法主要是解除注册,在重写的方法里监听方向变化,通过接口来对外公布数据。

    package com.surine.onemap;
    
    import android.content.Context;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;
    
    /**
     * Created by surine on 2017/8/1.
     */
    
    public class MyOrientationListener implements SensorEventListener {
        //传感器管理
        private SensorManager mySensorManager;
        //传感器
        private Sensor mySensor;
        private Context myContext;
    
        //x轴坐标
        private float lastX;
        //监听器
        private onOrientationListener myOrientationListener;
    
        public void start(){//开启方向传感器
            //先通过系统服务来得到传感器管理对象mySensorManager
            mySensorManager=(SensorManager) myContext.getSystemService(Context.SENSOR_SERVICE);
            if (mySensorManager!=null) {//如果传感器管理对象不为空,则可以通过传感器管理对象来获得方向传感器对象
                //获得方向传感器对象
                mySensor=mySensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
            }
            if (mySensor!=null) {//如果方向传感器不为空,则给该方向传感器注册监听事件
                //传入,监听器,传感器对象,传感精度
                mySensorManager.registerListener(this, mySensor, SensorManager.SENSOR_DELAY_GAME);
            }
        }
    
        public void stop(){//解除注册方向传感器监听事件
            mySensorManager.unregisterListener(this);
        }
    
        //构造方法
        public MyOrientationListener(Context myContext) {//方向传感器的一个构造器
            super();
            this.myContext = myContext;
        }
    
        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            // TODO Auto-generated method stub
        }
        //监听方向发生变化
        @Override
        public void onSensorChanged(SensorEvent event) {//精度发生改变的时候
            if (event.sensor.getType()==Sensor.TYPE_ORIENTATION) {//如果是方向传感器
                float x=event.values[SensorManager.DATA_X];//获得传感器的X轴的坐标,可以返回3个值,即X轴的坐标,Y轴坐标,Z轴坐标,我们只需要X轴坐标
                if (Math.abs(x-lastX)>1.0) {//对比本次的X坐标的变化比上一次的变化差大于1.0就说明方向发生改变
                    if (myOrientationListener!=null) {//说明主界面已经注册了事件,即不为空,然后产生一个回调将实时变化X轴的坐标传入
                        //通过一个回调方法,通知主界面去更新UI
                        myOrientationListener.onOrientationChanged(lastX);//就需要把上一次的X轴坐标传入,在MainActivity中的回调方法中去获取即可
                    }
                }
                lastX=x;
            }
        }
    
        public void setMyOrientationListener(onOrientationListener myOrientationListener) {
            this.myOrientationListener = myOrientationListener;
        }
        //写一个接口实现方向改变的监听产生的回调
        public interface onOrientationListener{
            void onOrientationChanged(float x);//回调的方法
        }
    }
    

    那么怎么使用呢?
    首先我们在主活动管理传感器,

     @Override
        protected void onStop() {
            super.onStop();
             ···省略代码
            myOrientationListener.stop();
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            myOrientationListener.start();
        }
    

    我们让传感器的启动与停止符合活动的周期,然后增加一个方法来监听传感数据

     /**
         * 定位结合方向传感器,从而可以实时监测到X轴坐标的变化,从而就可以检测到
         * 定位图标方向变化,只需要将这个动态变化的X轴的坐标更新myCurrentX值,
         * 最后在MyLocationData data.driection(myCurrentX);
         * */
        private void useLocationOrientationListener() {
            myOrientationListener=new MyOrientationListener(MainActivity.this);
            myOrientationListener.setMyOrientationListener(new onOrientationListener() {
                @Override
                public void onOrientationChanged(float x) {//监听方向的改变,方向改变时,需要得到地图上方向图标的位置
                    //声明一个double型的临时变量,记录传感方向
                    myCurrentX=x;
                }
            });
        }
    

    这个方法在初始化client的时候调用即可。
    最后我们看看拿到的值怎么使用

     MyLocationData locData = new MyLocationData.Builder()
                    .accuracy(0)
                    // 此处设置开发者获取到的方向信息,myCurrentX
                    .direction(myCurrentX).latitude(lati_)
                    .longitude(long_).build();
            baidumap.setMyLocationData(locData);
    

    可以看到,这样我们的传感器就和地图结合起来了,我们赶紧来运行一下。
    咦?怎么一卡一卡的?
    是我手机配置不行么?可是我看人家百度地图好好的啊,非常顺畅。
    卡就对了,而且还会根据span的周期卡顿呢!
    我这么一说你可能明白点什么,我们把MyLocationData 这一段代码写在了moveTo方法里了,而这段代码是谁调用的,答案就是位置监听器LocationListener,而我们之前说过,为了节省电量,这个类的接收函数不会一直运行,而是根据span间隔运行,而span最低数为1000,也就是一秒,所以你的方向也会跟着一秒卡一下一秒卡一下,那么这样我们也就知道这段代码该往哪里写了,
    当然是方向传感器的监听器里面啦,老铁!

    此时我们的代码变成了这样,

     private void useLocationOrientationListener() {
            myOrientationListener=new MyOrientationListener(MainActivity.this);
            myOrientationListener.setMyOrientationListener(new onOrientationListener() {
                @Override
                public void onOrientationChanged(float x) {//监听方向的改变,方向改变时,需要得到地图上方向图标的位置
                    myCurrentX=x;
                    MyLocationData locData = new MyLocationData.Builder()
                            .accuracy(0)
                            // 此处设置开发者获取到的方向信息
                            .direction(myCurrentX).latitude(lati_)
                            .longitude(long_).build();
                    baidumap.setMyLocationData(locData);
    
                }
            });
        }
    

    再次运行,哇哦,那丝滑……简直不敢想象,想要更明显的看到,切换到罗盘模式看看效果。
    可以说到这,我们已经实现了方向的显示,下面是一段小插曲。

    过场休息……

    我们也玩了这么久的百度地图了,也遇到不少问题,我觉得头头都要大了(努力卖萌,喵~),我们不如做一点放松的事情,美化一下界面。

    美化

    嗯哼,还是有点百度地图的意思,不过咱这个更Material Design一点,可以看出状态栏的效果,顶部栏的效果,都是朝着MD的感觉走的,那么我们看看源代码。

    我这里是在Android Studio里面直接使用了抽屉那个界面的模板
    它在这里是分文件写的。

    【文件:content_main.xml】
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context="com.surine.onemap.MainActivity"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <com.baidu.mapapi.map.MapView
            android:id="@+id/map"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"
            />
    
        <TextView
            android:id="@+id/first"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="Hello World!"
            android:visibility="gone"
            />
    
        <ImageView
            android:id="@+id/my_location"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_marginBottom="30dp"
            android:layout_marginLeft="30dp"
            android:elevation="5dp"
            android:background="@drawable/circle_shape"
            android:padding="10dp"
            android:src="@drawable/main_icon_location" />
    
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_marginTop="32dp"
            android:layout_width="match_parent"
            android:layout_height="52dp"
            android:background="#fff"
            android:elevation="4dp"
            app:titleMargin="5dp"
            app:theme="@style/ToolbarTheme"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            app:popupTheme="@style/AppTheme.PopupOverlay">
        </android.support.v7.widget.Toolbar>
    
    </RelativeLayout>
    
    【文件:activty_main.xml】
    
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:openDrawer="start">
    
        <include layout="@layout/content_main"/>
    
        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            app:headerLayout="@layout/nav_header_main"
            app:menu="@menu/activity_main_drawer"/>
    
    </android.support.v4.widget.DrawerLayout>
    
    

    之所以都贴上来是为了让大家看看有一些属性的配置,比如说android:fitsSystemWindows可能影响透明化状态栏的显示
    那么我们的透明化状态栏还需要的操作就是在activity里面配置

     setContentView(R.layout.activity_main);
    
         //在 setContentView(R.layout.activity_main);后面开始写
    在SDK21上的效果,这里我们只兼容Android5.0以上
    
            if (Build.VERSION.SDK_INT >= 21) {
                View decorView = getWindow().getDecorView();
                int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
                decorView.setSystemUiVisibility(option);
          //经测试发现 半透明 #50000000 颜色比较符合规范
            getWindow().setStatusBarColor(Color.parseColor("#50000000"));
            }
    

    接下来我们调整一下toolbar的标题颜色

     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
           //给一个浅一点的灰色
     toolbar.setTitleTextColor(getResources().getColor(R.color.text));
    

    好了,上面已经差不多了,我们看看下面,可以看到我们这里有一个按钮,相信大家也已经猜到了,这个就是用来手动定位的,我们在他的监听器里写定位逻辑就OK

    具体的方法写在下面了,这个方法在我们的onCreate()里调用即可
    好现在我们分析一下代码。

    • 行1:findviewbyid,没啥好说的
    • 行2:设置监听,也是很常见的写法
    • switch:在onclick方法里面有这么一个switch语句,这个主要是给我们的按钮增加了一个多用的功能,点击切换定位模式,首先我们的mCurrentMode默认值为Location.NORMAL,然后点击事件根据当前的mCurrentMode切换
    • case :我们分析其中一个case选项,首先是定位赋值,这个必须的,因为我们要改变定位模式,赋值之后开始真正设置定位模式
      然后我们调整地图角度,这个也是模仿百度地图等做的,根据当前方向调整角度,使用了rotate方法和overlook方法,然后加一个zoom缩放,使用animateMapStatus更新地图view
      这里还有一个设置图标的方法,主要是为了区分两种定位模式,我做了一个蓝色一个灰色的图标,来互相切换
    private void MyLocationButton() {
            location_icon = (ImageView) findViewById(R.id.my_location);
            location_icon.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    switch (mCurrentMode) {
                        case NORMAL:
                            //定位模式赋值
                            mCurrentMode = LocationMode.COMPASS;
                            //配置定位模式
                            baidumap.setMyLocationConfiguration(new MyLocationConfiguration(
                                    mCurrentMode, true, mCurrentMarker));
                            //调整地图角度,设置图标
                            location_icon.setImageResource(R.drawable.main_icon_location_compass);
                            MapStatus.Builder builder = new MapStatus.Builder();
                            builder.rotate(myCurrentX).overlook(0).zoom(18f);
                            baidumap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
                            break;
                        case COMPASS:
                            mCurrentMode = LocationMode.NORMAL;
                            baidumap.setMyLocationConfiguration(new MyLocationConfiguration(
                                    mCurrentMode, true, mCurrentMarker));
                            MapStatus.Builder builder1 = new MapStatus.Builder();
                            location_icon.setImageResource(R.drawable.main_icon_location);
                            builder1.rotate(0).overlook(0).zoom(18f);
                            baidumap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder1.build()));
                            break;
                        default:
                            break;
                    }
                }
            });
        }
    

    这里我再贴一下moveto的代码,梳理一下逻辑

      //地图控制方法
        private void moveTo(final BDLocation bdLocation) {
            lati_ = bdLocation.getLatitude();
            long_ = bdLocation.getLongitude();
            mCurrentAccracy = bdLocation.getRadius();
            //默认配置
            baidumap.setMyLocationConfiguration(new MyLocationConfiguration(
                    mCurrentMode, true, mCurrentMarker));
    
            if(isFirstLocate){
                isFirstLocate = false;
                baidumap.setMyLocationConfiguration(new MyLocationConfiguration(
                        mCurrentMode, true, null));
                LatLng ll = new LatLng(lati_,long_);
                update = MapStatusUpdateFactory.newLatLng(ll);
                baidumap.animateMapStatus(update);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //延时动画缩放
                            Thread.sleep(300);
                            update = MapStatusUpdateFactory.zoomTo(18f);
                            baidumap.animateMapStatus(update);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
    
        }
    

    这些代码大家都已经见到过,没啥新奇的东西,
    最后我们的监听器也修改了一下,主要去掉了第一篇文章的文字显示,加上一些防空判断,和settitle设置一些toolbar上显示的文字

    //接受位置信息
            @Override
            public void onReceiveLocation(final BDLocation bdLocation) {
                // map view 销毁后不在处理新接收的位置
                if (bdLocation == null || mmap == null) {
                    return;
                }
                baidumap.clear();
                //调用设置方法
                moveTo(bdLocation);
    
               runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if(!bdLocation.getLocationDescribe().equals("")){
                            setTitle(bdLocation.getLocationDescribe());
                        }else{
                            setTitle(R.string.app_name);
                        }
                    }
                });
            }
    

    到此为止我们已经实现了自动定位,手动定位。那么接下来我们继续回到SDK。

    地图子控件处理

    我们可以看到在我们的地图上有很多其他的东西,比如说标尺,百度LOGO,缩放控件,罗盘,等等等等,其实这些东西都可以拿来修改处理,于是乎我单独写了这么一个方法来处理这些元素,让界面变得更加整洁。

    在初始化地图的时候,调用这个方法,可以看到我们隐藏了比例尺,缩放控件,百度logo,调整了罗盘位置

     private void changeDefaultBaiduMapView(MapView mmap) {
            // 隐藏地图上比例尺
            mmap.showScaleControl(false);
    
            // 隐藏地图缩放控件
            mmap.showZoomControls(false);
    
            //罗盘位置
            baidumap.setCompassPosition(new Point(70,250));
    
            // 隐藏百度的LOGO
            View child = this.mmap.getChildAt(1);
            //原理也就是在view里面找到一个iamgeview(也就是logo),然后隐藏
            if (child != null && (child instanceof ImageView)) {
                child.setVisibility(View.INVISIBLE);
            }
        }
    

    我们打算实现一个自定义的缩放控件,首先看一下我们修改的布局文件,我们这里省略了一些代码(注意toolbar一定要放在最后面,因为他是z轴上离我们最近的,防止被mapview覆盖)

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        tools:context="com.surine.onemap.MainActivity"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
         ····   省略的代码
    
        <RelativeLayout
            android:id="@+id/ZoomControlView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#fff"
            android:elevation="2dp"
            android:layout_marginRight="5.0dip"
            android:layout_centerVertical="true"
            android:layout_alignEnd="@+id/toolbar">
    
            <Button
                android:id="@+id/zoomin"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_width="40dip"
                android:layout_height="45dip"
                android:textSize="18sp"
                android:text="+"/>
    
            <Button
                android:id="@+id/zoomout"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_width="40dip"
                android:layout_height="45dip"
                android:textSize="18sp"
                android:layout_below="@+id/zoomin"
                android:text="-"/>
        </RelativeLayout>
    
    
       ···省略代码
    
    </RelativeLayout>
    

    可以看到这里是两个按钮组成的一个缩放控件,那么他们的监听器实现如下

     private void MyZoomControl() {
            zoomInBtn = (Button) findViewById(R.id.zoomin);
            zoomOutBtn = (Button) findViewById(R.id.zoomout);
            zoomInBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    float zoomLevel = baidumap.getMapStatus().zoom;
    
                    if(zoomLevel<=18){
                        baidumap.animateMapStatus(MapStatusUpdateFactory.zoomIn());
                        zoomOutBtn.setEnabled(true);
                    }else{
                        Toast.makeText(MainActivity.this, "已经放至最大!", Toast.LENGTH_SHORT).show();
                        zoomInBtn.setEnabled(false);
                    }
                }
            });
            zoomOutBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    float zoomLevel = baidumap.getMapStatus().zoom;
                    if(zoomLevel>4){
                        baidumap.animateMapStatus(MapStatusUpdateFactory.zoomOut());
                        zoomInBtn.setEnabled(true);
                    }else{
                        zoomOutBtn.setEnabled(false);
                        Toast.makeText(MainActivity.this, "已经缩至最小!", Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    

    控制缩放的代码大家也见到过,在第二篇文章,这里的代码和 那里的没什么两样,主要是增加了一些button的UI控制,禁止点击啊之类的。
    好,那我们的界面最终成了什么样子呢?


    美化后

    可以说,不比百度地图差吧。

    总结

    那么这篇文章到这里也就算结束了吧,这篇文章讲的东西比较少,主要是在界面上做一些优化,那么下一篇文章我们会讨论点啥呢?
    期待吗?

    参考

    hyman csdn博客
    hyman mooc视频
    简书:zhh_happig
    baidumapdemo

    相关文章

      网友评论

          本文标题:百度地图 SDK(3)---- 界面美化,图层处理

          本文链接:https://www.haomeiwen.com/subject/blxdlxtx.html