安卓应用开发学习:获取导航卫星信息

news2024/10/9 1:24:01

一、引言

我昨天写了《安卓应用开发学习:获取经纬度及地理位置描述信息》日志,今天再接再厉,记录一下跟着《Android App 开发进阶与项目实战》一书,实现获取导航卫星信息,并在手机上显示的功能的情况。先上实现后的在手机上搜索卫星信息的最终效果图。

 

二、书上相关内容

书上对全球卫星导航系统做了简单的介绍,说明了获取导航卫星信息的大致方法。

随教材提供了完整的代码,照着做做出来的效果如下:

三、我对代码的改进

做出来后,有一个问题就是在我的手机上定位类型显示的null,而教材上是卫星定位。这一问题经过研究发现是我的手机上返回的Fused定位,而不是作者书籍中返回的Gps定位,关于这个问题的解决办法,我已经在 《安卓应用开发学习:获取经纬度及地理位置描述信息》一文的末尾进行的补充,这里就不复述了。

另外,我在网上搜了一下类似的软件界面,不少界面还会显示各个类型的卫星的数量,我也想显示,就需要对代码进行研究和修改,来实现。

我的做法是,增加几个整数型变量(num_china, num_america, num_russia, num_europe, num_other)用于统计各个类型的卫星数量,在卫星导航系统的状态变更时触发的onSatelliteStatusChanged方法中,获取到卫星信息后,就对卫星类别进行统计,根据卫星类型代码值分别递增上面的整数型变量。类型代码的对照关系如下:

0=UNKNOWN; 1= GPS; 2=SBAS; 3=GLONASS; 4=QZSS; 5=BEIDOU; 6=GALILEO; 7=IRNSS

在完成搜星后,将整数型变量中的值更新到页面中,如此就得到了如下的最终效果图。

四、代码展示

最后上相关的关键代码。

1.搜星的Activity文件

src\main\java\......\SatelliteSphereActivity.java

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GnssStatus;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import com.bahamutjapp.bean.Satellite;
import com.bahamutjapp.util.DateUtil;
import com.bahamutjapp.util.SwitchUtil;
import com.bahamutjapp.widget.CompassView;

import java.util.HashMap;
import java.util.Map;

@SuppressLint("DefaultLocale")
public class SatelliteSphereActivity extends AppCompatActivity {
    private final static String TAG = "SatelliteSphereActivity";
    private Map<String, String> providerMap = new HashMap<>(); // 定位提供者映射
    private TextView tv_satellite; // 声明一个文本视图对象
    private CompassView cv_satellite; // 声明一个罗盘视图对象
    private Map<Integer, Satellite> mapSatellite = new HashMap<>(); // 导航卫星映射
    private LocationManager mLocationMgr; // 声明一个定位管理器对象
    private Criteria mCriteria = new Criteria(); // 声明一个定位准则对象
    private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象
    private boolean isLocationEnable = false; // 定位服务是否可用
    private String mLocationType = ""; // 定位类型。是卫星定位还是网络定位
    private TextView tv_china, tv_america, tv_russia, tv_europe, tv_other;  // 申明文本视图对象
    private int num_china, num_america, num_russia, num_europe, num_other;  // 用于统计指定类型卫星数

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_satellite_sphere);
        providerMap.put("gps", "卫星");
        providerMap.put("network", "网络");
        providerMap.put("fused", "融合");  // 我的手机经测试getBestProvider返回的fused,估添加了此行
        tv_satellite = findViewById(R.id.tv_satellite);  // 显示搜索到的卫星信息和经纬度信息
        cv_satellite = findViewById(R.id.cv_satellite);  // 罗盘视图对象显示卫星分布图
        tv_china = findViewById(R.id.tv_china);  // 显示中国北斗卫星数
        tv_america = findViewById(R.id.tv_america);  // 显示美国GPS卫星数
        tv_russia = findViewById(R.id.tv_russia);  // 显示俄罗斯格洛纳斯卫星数
        tv_europe = findViewById(R.id.tv_europe);  // 显示欧洲伽利略卫星数
        tv_other = findViewById(R.id.tv_other);  // 显示其它卫星数
        num_china = num_america = num_russia = num_europe = num_other = 0;  // 指定类型卫星数量初始化
        SwitchUtil.checkLocationIsOpen(this, "需要打开定位功能才能查看卫星导航信息");
    }

    @Override
    protected void onResume() {
        super.onResume();
        mHandler.removeCallbacks(mRefresh); // 移除定位刷新任务
        initLocation(); // 初始化定位服务
        mHandler.postDelayed(mRefresh, 100); // 延迟100毫秒启动定位刷新任务
    }

    // 初始化定位服务
    private void initLocation() {
        // 从系统服务中获取定位管理器
        mLocationMgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        // 定位条件器Criteria设置
        // 设置定位精确度 Criteria.ACCURACY_COARSE表示粗略,Criteria.ACCURACY_FIN表示精细
        mCriteria.setAccuracy(Criteria.ACCURACY_FINE);
        mCriteria.setAltitudeRequired(true); // 设置是否需要海拔信息
        mCriteria.setBearingRequired(true); // 设置是否需要方位信息
        mCriteria.setCostAllowed(true); // 设置是否允许运营商收费
        mCriteria.setPowerRequirement(Criteria.POWER_LOW); // 设置对电源的需求
        // 获取定位管理器LocationManager的最佳定位提供者,本人手机oppo手机返回值为fused
        String bestProvider = mLocationMgr.getBestProvider(mCriteria, true);
        if (mLocationMgr.isProviderEnabled(bestProvider)) {  // 定位提供者当前可用
            mLocationType = providerMap.get(bestProvider)+"定位";
            beginLocation(bestProvider); // 开始定位
            isLocationEnable = true;
        } else { // 定位提供者暂不可用
            isLocationEnable = false;
        }
    }

    // 设置定位结果文本
    private void showLocation(Location location) {
        if (location != null) {
            String desc = String.format("当前定位类型:%s\n定位时间:%s" +
                            "\n经度:%f, 纬度:%f\n高度:%d米,  精度:%d米",
                    mLocationType, DateUtil.formatDate(location.getTime()),
                    location.getLongitude(), location.getLatitude(),
                    Math.round(location.getAltitude()), Math.round(location.getAccuracy()));
            tv_satellite.setText(desc);
        } else {
            Log.d(TAG, "暂未获取到定位对象");
        }
    }

    // 开始定位
    private void beginLocation(String method) {
        // 检查当前设备是否已经开启了定位功能
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "请授予定位权限并开启定位功能", Toast.LENGTH_SHORT).show();
            return;
        }
        // 设置定位管理器的位置变更监听器
        mLocationMgr.requestLocationUpdates(method, 300, 0, mLocationListener);
        // 获取最后一次成功定位的位置信息
        Location location = mLocationMgr.getLastKnownLocation(method);
        showLocation(location); // 显示定位结果文本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 注册全球导航卫星系统的状态监听器
            mLocationMgr.registerGnssStatusCallback(mGnssStatusListener, null);
        } else {
            // 给定位管理器添加导航状态监听器
            mLocationMgr.addGpsStatusListener(mStatusListener);
        }
    }

    private String[] mSystemArray = new String[] {"UNKNOWN", "GPS", "SBAS",
            "GLONASS", "QZSS", "BEIDOU", "GALILEO", "IRNSS"};  // 卫星类型列表
    @RequiresApi(api = Build.VERSION_CODES.N)
    // 定义一个GNSS状态监听器
    private GnssStatus.Callback mGnssStatusListener = new GnssStatus.Callback() {
        @Override
        public void onStarted() {}

        @Override
        public void onStopped() {}

        @Override
        public void onFirstFix(int ttffMillis) {}

        // 在卫星导航系统的状态变更时触发
        @Override
        public void onSatelliteStatusChanged(GnssStatus status) {
            mapSatellite.clear();  // 卫星列表重置
            num_china = num_america = num_russia = num_europe = num_other = 0;  // 卫星数量重置
            for (int i=0; i<status.getSatelliteCount(); i++) {
                Log.d(TAG, "i="+i+",getSvid="+status.getSvid(i)+",getConstellationType="+status.getConstellationType(i));
                Satellite item = new Satellite(); // 创建一个卫星信息对象
                item.signal = status.getCn0DbHz(i); // 获取卫星的信号
                item.elevation = status.getElevationDegrees(i); // 获取卫星的仰角
                item.azimuth = status.getAzimuthDegrees(i); // 获取卫星的方位角
                item.time = DateUtil.getNowDateTime(); // 获取当前时间
                // systemType与卫星类型对照: 0=UNKNOWN; 1= GPS; 2=SBAS; 3=GLONASS;
                //                         4=QZSS; 5=BEIDOU; 6=GALILEO; 7=IRNSS
                int systemType = status.getConstellationType(i); // 获取卫星的类型
                item.name = mSystemArray[systemType];
                mapSatellite.put(i, item);
                // 统计各类型卫星数量
                if (systemType == 1) {
                    num_america += 1;
                } else if (systemType == 3) {
                    num_russia += 1;
                } else if (systemType == 5) {
                    num_china += 1;
                } else if (systemType == 6) {
                    num_europe += 1;
                } else {
                    num_other += 1;
                }
            }
            cv_satellite.setSatelliteMap(mapSatellite); // 设置卫星浑天仪
            // 显示给类型的卫星数
            tv_china.setText(String.valueOf(num_china));
            tv_america.setText(String.valueOf(num_america));
            tv_russia.setText(String.valueOf(num_russia));
            tv_europe.setText(String.valueOf(num_europe));
            tv_other.setText(String.valueOf(num_other));
        }
    };

    // 定义一个位置变更监听器
    private LocationListener mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            showLocation(location); // 显示定位结果文本
        }

        @Override
        public void onProviderDisabled(String arg0) {
        }

        @Override
        public void onProviderEnabled(String arg0) {
        }

        @Override
        public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
        }
    };

    // 定义一个刷新任务,若无法定位则每隔一秒就尝试定位
    private Runnable mRefresh = new Runnable() {
        @Override
        public void run() {
            if (!isLocationEnable) {
                initLocation(); // 初始化定位服务
                mHandler.postDelayed(this, 1000);
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mLocationMgr != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                // 注销全球导航卫星系统的状态监听器
                mLocationMgr.unregisterGnssStatusCallback(mGnssStatusListener);
            } else {
                // 移除定位管理器的导航状态监听器
                mLocationMgr.removeGpsStatusListener(mStatusListener);
            }
            // 移除定位管理器的位置变更监听器
            mLocationMgr.removeUpdates(mLocationListener);
        }
    }

    // 定义一个导航状态监听器
    private GpsStatus.Listener mStatusListener = new GpsStatus.Listener() {
        // 在卫星导航系统的状态变更时触发
        @Override
        public void onGpsStatusChanged(int event) {
            if (ActivityCompat.checkSelfPermission(SatelliteSphereActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                // 获取卫星定位的状态信息
                GpsStatus gpsStatus = mLocationMgr.getGpsStatus(null);
                switch (event) {
                    case GpsStatus.GPS_EVENT_SATELLITE_STATUS: // 周期性报告卫星状态
                        // 得到所有收到的卫星的信息,包括 卫星的高度角、方位角、信噪比、和伪随机号(及卫星编号)
                        Iterable<GpsSatellite> satellites = gpsStatus.getSatellites();
                        for (GpsSatellite satellite : satellites) {
                            /*
                             * satellite.getElevation(); //卫星的仰角 (卫星的高度)
                             * satellite.getAzimuth(); //卫星的方位角
                             * satellite.getSnr(); //卫星的信噪比
                             * satellite.getPrn(); //卫星的伪随机码,可以认为就是卫星的编号
                             * satellite.hasAlmanac(); //卫星是否有年历表
                             * satellite.hasEphemeris(); //卫星是否有星历表
                             * satellite.usedInFix(); //卫星是否被用于近期的GPS修正计算
                             */
                            Satellite item = new Satellite(); // 创建一个卫星信息对象
                            int prn_id = satellite.getPrn(); // 获取卫星的编号
                            item.signal = Math.round(satellite.getSnr()); // 获取卫星的信号
                            item.elevation = Math.round(satellite.getElevation()); // 获取卫星的仰角
                            item.azimuth = Math.round(satellite.getAzimuth()); // 获取卫星的方位角
                            item.time = DateUtil.getNowDateTime(); // 获取当前时间
                            if (prn_id <= 51) { // 美国的GPS
                                item.name = "GPS";
                            } else if (prn_id >= 201 && prn_id <= 235) { // 中国的北斗
                                item.name = "BEIDOU";
                            } else if (prn_id >= 65 && prn_id <= 96) { // 俄罗斯的格洛纳斯
                                item.name = "GLONASS";
                            } else if (prn_id >= 301 && prn_id <= 336) { // 欧洲的伽利略
                                item.name = "GALILEO";
                            } else {
                                item.name = "未知";
                            }
                            Log.d(TAG, "id="+prn_id+", signal="+item.signal+", elevation="+item.elevation+", azimuth="+item.azimuth);
                            mapSatellite.put(prn_id, item);
                        }
                        cv_satellite.setSatelliteMap(mapSatellite); // 设置卫星浑天仪
                    case GpsStatus.GPS_EVENT_FIRST_FIX: // 首次卫星定位
                    case GpsStatus.GPS_EVENT_STARTED: // 卫星导航服务开始
                    case GpsStatus.GPS_EVENT_STOPPED: // 卫星导航服务停止
                    default:
                        break;
                }
            }
        }
    };

}

2.Activity文件对应的xml文件

src\main\res\layout\activity_satellite_sphere.xml

注:改文件引用的图片文件放在src\main\res\layout\drawable-xhdpi文件夹下,图片文件是原书作者的劳动成果,不便提供。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".SatelliteSphereActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:text="卫星浑天仪"
        android:textSize="24sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tv_satellite"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="10dp"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:text="卫星信息"
        android:textSize="16sp" />

    <com.bahamutjapp.widget.CompassView
        android:id="@+id/cv_satellite"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <GridLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:layout_gravity="center_horizontal"
        android:columnCount="5" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                app:srcCompat="@drawable/satellite_china"/>

            <TextView
                android:id="@+id/tv_china"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="0"
                android:textSize="18sp"
                android:textStyle="bold" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                app:srcCompat="@drawable/satellite_america"/>

            <TextView
                android:id="@+id/tv_america"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="0"
                android:textSize="18sp"
                android:textStyle="bold" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                app:srcCompat="@drawable/satellite_russia"/>

            <TextView
                android:id="@+id/tv_russia"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="0"
                android:textSize="18sp"
                android:textStyle="bold" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                app:srcCompat="@drawable/satellite_europe"/>

            <TextView
                android:id="@+id/tv_europe"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="0"
                android:textSize="18sp"
                android:textStyle="bold" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                app:srcCompat="@drawable/satellite_other"/>

            <TextView
                android:id="@+id/tv_other"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:text="0"
                android:textSize="18sp"
                android:textStyle="bold" />
        </LinearLayout>


    </GridLayout>

</LinearLayout>

3.自定义的罗盘组件,用于显示卫星图

src\main\java\......\widget\CompassView.java

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.bahamutjapp.R;
import com.bahamutjapp.bean.Satellite;
import com.bahamutjapp.util.Utils;

import java.util.HashMap;
import java.util.Map;

public class CompassView extends View {
    private final static String TAG = "CompassView";
    private int mWidth; // 视图宽度
    private Paint mPaintLine; // 弧线的画笔
    private Paint mPaintText; // 文字的画笔
    private Paint mPaintAngle; // 刻度的画笔
    private Bitmap mCompassBg; // 背景罗盘的位图
    private Rect mRectSrc; // 位图的原始边界
    private Rect mRectDest; // 位图的目标边界
    private RectF mRectAngle; // 刻度的矩形边界
    private Bitmap mSatelliteChina; // 中国北斗卫星的图标
    private Bitmap mSatelliteAmerica; // 美国GPS卫星的图标
    private Bitmap mSatelliteRussia; // 俄罗斯格洛纳斯卫星的图标
    private Bitmap mSatelliteEurope; // 欧洲伽利略卫星的图标
    private Bitmap mSatelliteOther; // 其他国家卫星的图标
    private Map<Integer, Satellite> mapSatellite = new HashMap<>(); // 卫星分布映射
    private int mScaleLength = 25; // 刻度线的长度
    private float mBorder = 0.9f; // 边界的倍率,比如只在整个区域的90%内部绘图

    public CompassView(Context context) {
        this(context, null);
    }

    public CompassView(Context context, AttributeSet attr) {
        super(context, attr);
        // 以下初始化弧线的画笔
        mPaintLine = new Paint();
        mPaintLine.setAntiAlias(true); // 设置抗锯齿
        mPaintLine.setColor(Color.GREEN); // 设置画笔的颜色
        mPaintLine.setStrokeWidth(2); // 设置画笔的线宽
        mPaintLine.setStyle(Style.STROKE); // 设置画笔的类型。STROK表示空心,FILL表示实心
        // 以下初始化文字的画笔
        mPaintText = new Paint();
        mPaintText.setAntiAlias(true); // 设置抗锯齿
        mPaintText.setColor(Color.RED); // 设置画笔的颜色
        mPaintText.setStrokeWidth(1); // 设置画笔的线宽
        mPaintText.setTextSize(Utils.dip2px(context, 14));
        // 以下初始化刻度的画笔
        mPaintAngle = new Paint();
        mPaintAngle.setAntiAlias(true); // 设置抗锯齿
        mPaintAngle.setColor(Color.BLACK); // 设置画笔的颜色
        mPaintAngle.setStrokeWidth(1); // 设置画笔的线宽
        mPaintAngle.setTextSize(Utils.dip2px(context, 12));
        // 从资源图片中获取罗盘背景的位图
        mCompassBg = BitmapFactory.decodeResource(getResources(), R.drawable.compass_bg);
        // 根据位图的宽高创建位图的原始边界
        mRectSrc = new Rect(0, 0, mCompassBg.getWidth(), mCompassBg.getHeight());
        // 从资源图片中获取中国北斗卫星的图标
        mSatelliteChina = BitmapFactory.decodeResource(getResources(), R.drawable.satellite_china);
        // 从资源图片中获取美国GPS卫星的图标
        mSatelliteAmerica = BitmapFactory.decodeResource(getResources(), R.drawable.satellite_america);
        // 从资源图片中获取俄罗斯格洛纳斯卫星的图标
        mSatelliteRussia = BitmapFactory.decodeResource(getResources(), R.drawable.satellite_russia);
        // 从资源图片中获取欧洲伽利略卫星的图标
        mSatelliteEurope = BitmapFactory.decodeResource(getResources(), R.drawable.satellite_europe);
        // 从资源图片中获取其他国家卫星的图标
        mSatelliteOther = BitmapFactory.decodeResource(getResources(), R.drawable.satellite_other);
    }

    // 重写onMeasure方法,使得该视图无论竖屏还是横屏都保持正方形状
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = View.MeasureSpec.getSize(widthMeasureSpec);
        int height = View.MeasureSpec.getSize(heightMeasureSpec);
        mWidth = getMeasuredWidth(); // 获取视图的实际宽度
        if (width < height) { // 宽度比高度小,则缩短高度使之与宽度一样长
            super.onMeasure(widthMeasureSpec, widthMeasureSpec);
        } else { // 宽度比高度大,则缩短宽度使之与高度一样长
            super.onMeasure(heightMeasureSpec, heightMeasureSpec);
        }
        // 根据视图的宽高创建位图的目标边界
        mRectDest = new Rect(0, 0, mWidth, mWidth);
        // 创建刻度的矩形边界
        mRectAngle = new RectF(mWidth / 10, mWidth / 10, mWidth * 9 / 10, mWidth * 9 / 10);
        Log.d(TAG, "mWidth=" + mWidth);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        int radius = mWidth / 2;
        int margin = radius / 10;
        // 在画布上绘制罗盘背景
        canvas.drawBitmap(mCompassBg, mRectSrc, mRectDest, new Paint());
        // 以下在画布上绘制各种半径的圆圈
        canvas.drawCircle(radius, radius, radius * 3 / 10, mPaintLine);
        canvas.drawCircle(radius, radius, radius * 5 / 10, mPaintLine);
        canvas.drawCircle(radius, radius, radius * 7 / 10, mPaintLine);
        canvas.drawCircle(radius, radius, radius * 9 / 10, mPaintLine);
        // 在画布上绘制罗盘的中央垂直线
        canvas.drawLine(radius, margin, radius, mWidth - margin, mPaintLine);
        // 在画布上绘制罗盘的中央水平线
        canvas.drawLine(margin, radius, mWidth - margin, radius, mPaintLine);
        // 画罗盘的刻度
        for (int i = 0; i < 360; i += 30) {
            Path path = new Path(); // 创建一个路径对象
            path.addArc(mRectAngle, i - 3, i + 3); // 往路径添加圆弧
            int angle = (i + 90) % 360;
            // 在画布上绘制刻度文字
            canvas.drawTextOnPath("" + angle, path, 0, 0, mPaintAngle);
            // 在画布上绘制刻度线条
            canvas.drawLine(getXpos(radius, angle, radius * mBorder),
                    getYpos(radius, angle, radius * mBorder),
                    getXpos(radius, angle, (radius - mScaleLength) * mBorder),
                    getYpos(radius, angle, (radius - mScaleLength) * mBorder),
                    mPaintAngle);
        }
        // 画卫星分布图
        for (Map.Entry<Integer, Satellite> item_map : mapSatellite.entrySet()) {
            Satellite item = item_map.getValue();
            Bitmap bitmap;
            if (item.name.equals("BEIDOU")) { // 北斗卫星
                bitmap = mSatelliteChina;
            } else if (item.name.equals("GPS") || item.name.equals("SBAS")) { // GPS卫星
                bitmap = mSatelliteAmerica;
            } else if (item.name.equals("GLONASS")) { // 格洛纳斯卫星
                bitmap = mSatelliteRussia;
            } else if (item.name.equals("GALILEO")) { // 伽利略卫星
                bitmap = mSatelliteEurope;
            } else if (!item.name.equals("")) { // 其他卫星
                bitmap = mSatelliteOther;
            } else {
                continue;
            }
            float left = getXpos(radius, item.azimuth, radius * mBorder * getCos(item.elevation));
            float top = getYpos(radius, item.azimuth, radius * mBorder * getCos(item.elevation));
            // 在画布上绘制卫星图标
            canvas.drawBitmap(bitmap, left - bitmap.getWidth() / 2,
                    top - bitmap.getHeight() / 2, new Paint());
        }
        canvas.drawText("北", radius - 15, margin - 15, mPaintText);
    }

    // 根据半径、角度、线长,计算该点的横坐标
    private float getXpos(int radius, float angle, double length) {
        return (float) (radius + getCos(angle) * length);
    }

    // 根据半径、角度、线长,计算该点的纵坐标
    private float getYpos(int radius, float angle, double length) {
        return (float) (radius + getSin(angle) * length);
    }

    // 获得指定角度的正弦值
    private double getSin(float angle) {
        return Math.sin(Math.PI * angle / 180.0);
    }

    // 获得指定角度的余弦值
    private double getCos(float angle) {
        return Math.cos(Math.PI * angle / 180.0);
    }

    // 设置卫星分布映射,用于卫星浑天仪
    public void setSatelliteMap(Map<Integer, Satellite> map) {
        mapSatellite = map;
        postInvalidate(); // 立即刷新视图(线程安全方式)
    }

}

4.卫星信息的对象

src\main\java\......\bean\Satellite.java

public class Satellite {
    public String name; // 卫星导航系统的名称
    public float signal; // 卫星的信噪比(信号)
    public float elevation; // 卫星的仰角
    public float azimuth; // 卫星的方位角
    public String time; // 当前时间

    public Satellite() {
        name = "";
        signal = -1;
        elevation = -1;
        azimuth = -1;
        time = "";
    }
}

5.SwitchUtil.java文件(获取定位功能开关状态)

src\main\java\......\util\SwitchUtil.java

import android.content.Context;
import android.content.Intent;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
 
import java.lang.reflect.Method;
 
public class SwitchUtil {
    private static final String TAG = "SwitchUtil";
 
    // 获取定位功能的开关状态
    public static boolean getLocationStatus(Context ctx) {
        // 从系统服务中获取定位管理器
        LocationManager lm = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
        return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }
 
    // 检查定位功能是否打开,若未打开则跳到系统的定位功能设置页面
    public static void checkLocationIsOpen(Context ctx, String hint) {
        if (!getLocationStatus(ctx)) {
            Toast.makeText(ctx, hint, Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            ctx.startActivity(intent);
        }
    }
     
}

6. DateUtil.java文件(对日期数据进行格式化)

src\main\java\......\util\DateUtil.java

import android.annotation.SuppressLint;
 
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
 
@SuppressLint("SimpleDateFormat")
public class DateUtil {
    // 获取当前的日期时间
    public static String getNowDateTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        return sdf.format(new Date());
    }
 
    
    // 将长整型的时间数值格式化为日期时间字符串
    public static String formatDate(long time) {
        Date date = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }
 
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1863148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go语言day2 配置

使用cmd 中的 go install &#xff1b; go build 命令出现 go cannot find main module 错误怎么解决&#xff1f; go学习-问题记录(开发环境)go: cannot find main module&#xff1b; see ‘go help modules‘_go: no flags specified (see go help mod edit)-CSDN博客 在本…

FPGA学习笔记(6)——硬件调试与网表添加探针

对信号进行分析&#xff0c;除了使用内置的ILA IP核&#xff0c;还可以在网表中添加探针。 本节采用之前配置的LED灯闪烁代码&#xff0c;对原始工程进行修改。 如果是新建工程&#xff0c;需要现将代码进行综合Synthesis&#xff0c;然后再进行接下来的操作。 1、点击Open S…

链表数组遍历输出的辨析(二者都含指针的情况下)----PTA期末复习题

输入输出三位学生的学号和信息 一开始我认为是指针&#xff0c;直接背了指针输出的方式&#xff1b;p;p!NULL;pp->next 这个是错误的 下面这个输出是正确的方式 分析怎么区分这两个 举个例子来 数组遍历&#xff1a; 链表遍历&#xff1a; 输出的结果&#xff1a; 如果将…

浏览器扩展V3开发系列之 chrome.cookies 的用法和案例

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 chrome.cookies API能够让我们在扩展程序中去操作浏览器的cookies。 在使用 chrome.cookies 要先声明…

【Redis】Zset有序集合常用命令以及使用场景

Redis 的有序集合&#xff08;Sorted Set&#xff0c;简称 Zset&#xff09;是一个非常强大的数据结构&#xff0c;它结合了集合&#xff08;Set&#xff09;的唯一性和列表&#xff08;List&#xff09;的有序性。每个元素都关联一个评分&#xff08;score&#xff09;&#x…

减少液氮罐内液氮损耗的方法

监测与管理液氮容器的密封性能 液氮容器的密封性能直接影响液氮的损耗情况。一个常见的损耗源是容器本身的密封不良或老化导致的泄漏。为了有效减少液氮损耗&#xff0c;首先应当定期检查液氮容器的密封性能。这可以通过简单的方法如肉眼检查外观&#xff0c;或者更精确的方法…

SEO与AI的结合:如何用ChatGPT生成符合搜索引擎优化的内容

在当今数字时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;已成为每个网站和内容创作者都必须掌握的一项技能。SEO的主要目标是通过优化内容&#xff0c;使其在搜索引擎结果页面&#xff08;SERP&#xff09;中排名更高&#xff0c;从而吸引更多的流量。然而&#xf…

嵌入式学习——硬件(ARM体系架构)——day51

1. S3C2440基础知识——一条指令四个字节 1.1 定义 S3C2440 是三星&#xff08;Samsung&#xff09;公司设计的一款基于 ARM920T 核心的微处理器&#xff0c;广泛应用于嵌入式系统中&#xff0c;属于三星的 S3C24xx 系列。 1.2 处理器核心 ARM920T&#xff1a;基于 ARM v5T …

[C#][opencvsharp]C#使用opencvsharp进行年龄和性别预测支持视频图片检测

使用 OpenCVSharp 来调用 age_net.caffemodel 和 gender_net.caffemodel 来进行性别和年龄预测涉及几个步骤。以下是一个简化的流程和示例文案&#xff1a; 1. 准备工作 确保你已经安装了 OpenCVSharp 和相关的依赖项。确保你有 age_net.prototxt、age_net.caffemodel、gende…

市面上很轻的 100kW 负载组

FX100S-C 负载组 EAK的 FX100S-C 负载组在轻质外壳中以 415Vac 50Hz 提供 100kW 的连续负载。数字仪表允许您测量功率、电压、电流和频率&#xff0c;同时还允许您在进行测试时记录数据。 EAK是市场上最轻的 100kW 负载组之一&#xff0c;它将使您能够访问其他负载组无法到达…

离线部署OpenIM

目录 1.提取相关安装包和镜像 2.安装docker和docker-compose 3.依次导入镜像 4.解压安装包 5.执行安装命令 6.PC Web 验证 7.开放端口 7.1IM 端口 7.2Chat 端口 7.3 PC Web 及管理后台前端资源端口 “如果您在解决类似问题时也遇到了困难&#xff0c;希望我的经验分享…

服务运营 | MS文章精选:线上点单,当真免排队?餐饮零售与医疗场景中的全渠道运营

编者按&#xff1a; 小A走进了一家奶茶店&#xff0c;准备向店员点单&#xff0c;但却在屏幕上看到还有98杯奶茶待制作&#xff08;因为线上订单突然暴增&#xff09;。因此&#xff0c;小A不满地嘟囔着离开了奶茶店。这个例子展示了线上渠道可能会对线下渠道造成一些负面影响…

并发编程-04synchronized原理

并发编程-04synchronized原理 一 synchronized基础 1.1 并发安全问题 在学习synchronized原理之前&#xff0c;我们先要了解synchronized是干嘛用的&#xff0c;什么场景下需要使用它&#xff0c;以及它的使用方式有哪些&#xff1f;接下来我们去根据一个业务场景去了解下sy…

【排序 队列】1585. 检查字符串是否可以通过排序子字符串得到另一个字符串

本文涉及知识点 排序 队列 LeetCode1585. 检查字符串是否可以通过排序子字符串得到另一个字符串 给你两个字符串 s 和 t &#xff0c;请你通过若干次以下操作将字符串 s 转化成字符串 t &#xff1a; 选择 s 中一个 非空 子字符串并将它包含的字符就地 升序 排序。 比方说&a…

密码学:用随机函数隐藏指纹

英文中e的出现频率高&#xff0c;加密后&#xff0c;频率最高的那个符号代表e。这是历史上的一次真实案例。这些符号的概率&#xff0c;叫做“指纹”。 把e加密成2个符号&#xff0c;用随机函数选择&#xff0c;例如70%概率下选择符号1&#xff0c;30%选择符号2。解密时&#…

Pythonnet能导入clr,但无法引入System模块?

【pythonnet详解】—— Python 和 .NET 互操作的库_pythonnet 详细使用-CSDN博客 Python中动态调用C#的dll动态链接库中方法_python 如何调用c# dll-CSDN博客 需求&#xff1a;Python调用并传List<float>类型参数给.Net 起初&#xff1a;直接 # 创建一个Python浮点数…

数字逻辑--课程设计报告

##智力竞赛抢答器设计 如果对这个有兴趣或者需要电路图或者报告的话请私聊我

Linux 中变量的取用与设定

优质博文&#xff1a;IT-BLOG-CN Linux是一个多人多任务的环境&#xff0c;每个人登录系统都能取得一个bash shell&#xff0c;每个人都能够使用bash下达mail这个指令来接收自己的邮箱等等。问题是&#xff0c;bash如何得知你的邮箱是那个文件&#xff1f;这就需要『变量』的帮…

JavaWeb系列八: WEB 开发通信协议(HTTP协议)

HTTP协议 官方文档什么是HTTP协议快速入门页面请求的一个问题(分析)http请求包分析(get)http请求包分析(post)GET请求 POST请求分别有哪些http响应包分析常用的状态码说明状态码200状态码404状态码500状态码302状态码304 MIME类型MIME介绍常见的 MIME 类型 官方文档 HTTP常见请…

不会吧!KVM竟然不支持磁盘的精简置备!?

正文共&#xff1a;999 字 17 图&#xff0c;预估阅读时间&#xff1a;1 分钟 前面我们安装了KVM主机&#xff08;KVM部署初体验&#xff09;&#xff0c;接下来肯定是要简单操作一下&#xff0c;体验体验如何使用。 上次我们是在主机的桌面环境中打开的“虚拟系统管理器VMM”&…