Android之指南针学习

来源:互联网 发布:海岛奇兵医生升级数据 编辑:程序博客网 时间:2024/04/28 14:19
  点我下载源码

5月12日更新到V5版:http://download.csdn.net/detail/weidi1989/5364243

       今天,在小米的开源项目中下载了一个指南针源码学习了一下,感觉不仅界面做得很漂亮,代码也是很精致,所以我在研究了之后,加了比较多的注释,果断跟大家分享一下,很精简的几段代码,仔细品味可以学到很多东西,我稍微总结一下:

①.handler的灵活运用,每20秒后执行一次自己,用来检测方向变化值,更新指南针旋转。

②.传感器和谷歌位置服务的使用。

③.自定义View,这里面是自定义一个ImageView,自己增加一个旋转图片的方法。

④.Android动画Interpolator插入器:AccelerateInterpolator加速插入器的运用。顺便说一下另外几个插入器:

                                     ——AccelerateInterpolator:动画从开始到结束,变化率是一个加速的过程。

                                     ——DecelerateInterpolator:动画从开始到结束,变化率是一个减速的过程。

                                     ——CycleInterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线。

                                    ——AccelerateDecelerateInterpolator:动画从开始到结束,变化率是先加速后减速的过程。

                                     ——LinearInterpolator:动画从开始到结束,变化率是线性变化。
                                    AccelerateInterpolator有一个方法:getInterpolation(float input);

⑤.巧妙的数字替换成对应的数字图片和根据本地语言使用对应的图片资源(图片资源国际化,哈哈)。还有一些其他的小知识,朋友们,自己下载去研究吧!

 

下面看一下效果图(我的是模拟器,木有传感器也木有定位的):

 

下面我们来看一下这个界面的布局文件(main.xml):

[html] view plaincopyprint?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent" >  
  5.   
  6.     <FrameLayout  
  7.         android:layout_width="fill_parent"  
  8.         android:layout_height="fill_parent"  
  9.         android:background="@drawable/background" >  
  10.   
  11.         <LinearLayout  
  12.             android:id="@+id/view_compass"  
  13.             android:layout_width="fill_parent"  
  14.             android:layout_height="fill_parent"  
  15.             android:background="@drawable/background_light"  
  16.             android:orientation="vertical" >  
  17.   
  18.             <LinearLayout  
  19.                 android:layout_width="fill_parent"  
  20.                 android:layout_height="0dip"  
  21.                 android:layout_weight="1"  
  22.                 android:orientation="vertical" >  
  23.   
  24.                 <FrameLayout  
  25.                     android:layout_width="fill_parent"  
  26.                     android:layout_height="wrap_content"  
  27.                     android:background="@drawable/prompt" >  
  28.   
  29.                     <LinearLayout  
  30.                         android:layout_width="fill_parent"  
  31.                         android:layout_height="wrap_content"  
  32.                         android:layout_gravity="center_horizontal"  
  33.                         android:layout_marginTop="70dip"  
  34.                         android:orientation="horizontal" >  
  35.   
  36.                         <LinearLayout  
  37.                             android:id="@+id/layout_direction"  
  38.                             android:layout_width="0dip"  
  39.                             android:layout_height="wrap_content"  
  40.                             android:layout_weight="1"  
  41.                             android:gravity="right"  
  42.                             android:orientation="horizontal" >  
  43.                         </LinearLayout>  
  44.   
  45.                         <ImageView  
  46.                             android:layout_width="20dip"  
  47.                             android:layout_height="fill_parent" >  
  48.                         </ImageView>  
  49.   
  50.                         <LinearLayout  
  51.                             android:id="@+id/layout_angle"  
  52.                             android:layout_width="0dip"  
  53.                             android:layout_height="wrap_content"  
  54.                             android:layout_weight="1"  
  55.                             android:gravity="left"  
  56.                             android:orientation="horizontal" >  
  57.                         </LinearLayout>  
  58.                     </LinearLayout>  
  59.                 </FrameLayout>  
  60.   
  61.                 <LinearLayout  
  62.                     android:layout_width="fill_parent"  
  63.                     android:layout_height="0dip"  
  64.                     android:layout_weight="1"  
  65.                     android:orientation="vertical" >  
  66.   
  67.                     <FrameLayout  
  68.                         android:layout_width="fill_parent"  
  69.                         android:layout_height="wrap_content"  
  70.                         android:layout_gravity="center" >  
  71.   
  72.                         <ImageView  
  73.                             android:layout_width="wrap_content"  
  74.                             android:layout_height="wrap_content"  
  75.                             android:layout_gravity="center"  
  76.                             android:src="@drawable/background_compass" />  
  77.   
  78.                         <net.micode.compass.CompassView  
  79.                             android:id="@+id/compass_pointer"  
  80.                             android:layout_width="wrap_content"  
  81.                             android:layout_height="wrap_content"  
  82.                             android:layout_gravity="center"  
  83.                             android:src="@drawable/compass" />  
  84.   
  85.                         <ImageView  
  86.                             android:layout_width="wrap_content"  
  87.                             android:layout_height="wrap_content"  
  88.                             android:layout_gravity="center"  
  89.                             android:src="@drawable/miui_cover" />  
  90.                     </FrameLayout>  
  91.                 </LinearLayout>  
  92.             </LinearLayout>  
  93.   
  94.             <FrameLayout  
  95.                 android:id="@+id/location_layout"  
  96.                 android:layout_width="fill_parent"  
  97.                 android:layout_height="wrap_content" >  
  98.   
  99.                 <LinearLayout  
  100.                     android:layout_width="fill_parent"  
  101.                     android:layout_height="wrap_content"  
  102.                     android:background="@drawable/background_bottom"  
  103.                     android:orientation="vertical" >  
  104.                 </LinearLayout>  
  105.   
  106.                 <TextView  
  107.                     android:id="@+id/textview_location"  
  108.                     android:layout_width="wrap_content"  
  109.                     android:layout_height="wrap_content"  
  110.                     android:layout_gravity="center"  
  111.                     android:text="@string/getting_location"  
  112.                     android:textAppearance="?android:attr/textAppearanceMedium"  
  113.                     android:textColor="#7FFFFFFF" />  
  114.             </FrameLayout>  
  115.         </LinearLayout>  
  116.     </FrameLayout>  
  117.   
  118. </FrameLayout>  
<?xml version="1.0" encoding="UTF-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent" >    <FrameLayout        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:background="@drawable/background" >        <LinearLayout            android:id="@+id/view_compass"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:background="@drawable/background_light"            android:orientation="vertical" >            <LinearLayout                android:layout_width="fill_parent"                android:layout_height="0dip"                android:layout_weight="1"                android:orientation="vertical" >                <FrameLayout                    android:layout_width="fill_parent"                    android:layout_height="wrap_content"                    android:background="@drawable/prompt" >                    <LinearLayout                        android:layout_width="fill_parent"                        android:layout_height="wrap_content"                        android:layout_gravity="center_horizontal"                        android:layout_marginTop="70dip"                        android:orientation="horizontal" >                        <LinearLayout                            android:id="@+id/layout_direction"                            android:layout_width="0dip"                            android:layout_height="wrap_content"                            android:layout_weight="1"                            android:gravity="right"                            android:orientation="horizontal" >                        </LinearLayout>                        <ImageView                            android:layout_width="20dip"                            android:layout_height="fill_parent" >                        </ImageView>                        <LinearLayout                            android:id="@+id/layout_angle"                            android:layout_width="0dip"                            android:layout_height="wrap_content"                            android:layout_weight="1"                            android:gravity="left"                            android:orientation="horizontal" >                        </LinearLayout>                    </LinearLayout>                </FrameLayout>                <LinearLayout                    android:layout_width="fill_parent"                    android:layout_height="0dip"                    android:layout_weight="1"                    android:orientation="vertical" >                    <FrameLayout                        android:layout_width="fill_parent"                        android:layout_height="wrap_content"                        android:layout_gravity="center" >                        <ImageView                            android:layout_width="wrap_content"                            android:layout_height="wrap_content"                            android:layout_gravity="center"                            android:src="@drawable/background_compass" />                        <net.micode.compass.CompassView                            android:id="@+id/compass_pointer"                            android:layout_width="wrap_content"                            android:layout_height="wrap_content"                            android:layout_gravity="center"                            android:src="@drawable/compass" />                        <ImageView                            android:layout_width="wrap_content"                            android:layout_height="wrap_content"                            android:layout_gravity="center"                            android:src="@drawable/miui_cover" />                    </FrameLayout>                </LinearLayout>            </LinearLayout>            <FrameLayout                android:id="@+id/location_layout"                android:layout_width="fill_parent"                android:layout_height="wrap_content" >                <LinearLayout                    android:layout_width="fill_parent"                    android:layout_height="wrap_content"                    android:background="@drawable/background_bottom"                    android:orientation="vertical" >                </LinearLayout>                <TextView                    android:id="@+id/textview_location"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_gravity="center"                    android:text="@string/getting_location"                    android:textAppearance="?android:attr/textAppearanceMedium"                    android:textColor="#7FFFFFFF" />            </FrameLayout>        </LinearLayout>    </FrameLayout></FrameLayout>


这其中用到了一个自定义view,其实就是中间那个可以旋转的指南针,我们也来看看它的代码(CompassView.java):

[java] view plaincopyprint?
  1. /** 
  2.  * 自定义一个View继承ImageView,增加一个通用的旋转图片资源的方法 
  3.  *  
  4.  * @author way 
  5.  *  
  6.  */  
  7. public class CompassView extends ImageView {  
  8.     private float mDirection;// 方向旋转浮点数  
  9.     private Drawable compass;// 图片资源  
  10.       
  11.     //三个构造器   
  12.     public CompassView(Context context) {  
  13.         super(context);  
  14.         mDirection = 0.0f;// 默认不旋转  
  15.         compass = null;  
  16.     }  
  17.   
  18.     public CompassView(Context context, AttributeSet attrs) {  
  19.         super(context, attrs);  
  20.         mDirection = 0.0f;  
  21.         compass = null;  
  22.     }  
  23.   
  24.     public CompassView(Context context, AttributeSet attrs, int defStyle) {  
  25.         super(context, attrs, defStyle);  
  26.         mDirection = 0.0f;  
  27.         compass = null;  
  28.     }  
  29.   
  30.     @Override  
  31.     protected void onDraw(Canvas canvas) {  
  32.         if (compass == null) {  
  33.             compass = getDrawable();// 获取当前view的图片资源  
  34.             compass.setBounds(00, getWidth(), getHeight());// 图片资源在view的位置,此处相当于充满view  
  35.         }  
  36.   
  37.         canvas.save();  
  38.         canvas.rotate(mDirection, getWidth() / 2, getHeight() / 2);// 绕图片中心点旋转,  
  39.         compass.draw(canvas);// 把旋转后的图片画在view上,即保持旋转后的样子  
  40.         canvas.restore();// 保存一下   
  41.     }  
  42.   
  43.     /** 
  44.      * 自定义更新方向的方法 
  45.      *  
  46.      * @param direction 
  47.      *            传入的方向 
  48.      */  
  49.     public void updateDirection(float direction) {  
  50.         mDirection = direction;  
  51.         invalidate();// 重新刷新一下,更新方向  
  52.     }  
  53.   
  54. }  
/** * 自定义一个View继承ImageView,增加一个通用的旋转图片资源的方法 *  * @author way *  */public class CompassView extends ImageView {private float mDirection;// 方向旋转浮点数private Drawable compass;// 图片资源//三个构造器public CompassView(Context context) {super(context);mDirection = 0.0f;// 默认不旋转compass = null;}public CompassView(Context context, AttributeSet attrs) {super(context, attrs);mDirection = 0.0f;compass = null;}public CompassView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mDirection = 0.0f;compass = null;}@Overrideprotected void onDraw(Canvas canvas) {if (compass == null) {compass = getDrawable();// 获取当前view的图片资源compass.setBounds(0, 0, getWidth(), getHeight());// 图片资源在view的位置,此处相当于充满view}canvas.save();canvas.rotate(mDirection, getWidth() / 2, getHeight() / 2);// 绕图片中心点旋转,compass.draw(canvas);// 把旋转后的图片画在view上,即保持旋转后的样子canvas.restore();// 保存一下}/** * 自定义更新方向的方法 *  * @param direction *            传入的方向 */public void updateDirection(float direction) {mDirection = direction;invalidate();// 重新刷新一下,更新方向}}


接下来就只剩下一个Activity了,其实总体结构还是很简单的,CompassActivity.java:

[java] view plaincopyprint?
  1. /** 
  2.  * MainActivity 
  3.  *  
  4.  * @author way 
  5.  *  
  6.  */  
  7. public class CompassActivity extends Activity {  
  8.     private static final int EXIT_TIME = 2000;// 两次按返回键的间隔判断  
  9.     private final float MAX_ROATE_DEGREE = 1.0f;// 最多旋转一周,即360°  
  10.     private SensorManager mSensorManager;// 传感器管理对象  
  11.     private Sensor mOrientationSensor;// 传感器对象  
  12.     private LocationManager mLocationManager;// 位置管理对象  
  13.     private String mLocationProvider;// 位置提供者名称,GPS设备还是网络  
  14.     private float mDirection;// 当前浮点方向  
  15.     private float mTargetDirection;// 目标浮点方向  
  16.     private AccelerateInterpolator mInterpolator;// 动画从开始到结束,变化率是一个加速的过程,就是一个动画速率  
  17.     protected final Handler mHandler = new Handler();  
  18.     private boolean mStopDrawing;// 是否停止指南针旋转的标志位  
  19.     private boolean mChinease;// 系统当前是否使用中文  
  20.     private long firstExitTime = 0L;// 用来保存第一次按返回键的时间  
  21.   
  22.     View mCompassView;  
  23.     CompassView mPointer;// 指南针view  
  24.     TextView mLocationTextView;// 显示位置的view  
  25.     LinearLayout mDirectionLayout;// 显示方向(东南西北)的view  
  26.     LinearLayout mAngleLayout;// 显示方向度数的view  
  27.   
  28.     // 这个是更新指南针旋转的线程,handler的灵活使用,每20毫秒检测方向变化值,对应更新指南针旋转  
  29.     protected Runnable mCompassViewUpdater = new Runnable() {  
  30.         @Override  
  31.         public void run() {  
  32.             if (mPointer != null && !mStopDrawing) {  
  33.                 if (mDirection != mTargetDirection) {  
  34.   
  35.                     // calculate the short routine  
  36.                     float to = mTargetDirection;  
  37.                     if (to - mDirection > 180) {  
  38.                         to -= 360;  
  39.                     } else if (to - mDirection < -180) {  
  40.                         to += 360;  
  41.                     }  
  42.   
  43.                     // limit the max speed to MAX_ROTATE_DEGREE  
  44.                     float distance = to - mDirection;  
  45.                     if (Math.abs(distance) > MAX_ROATE_DEGREE) {  
  46.                         distance = distance > 0 ? MAX_ROATE_DEGREE  
  47.                                 : (-1.0f * MAX_ROATE_DEGREE);  
  48.                     }  
  49.   
  50.                     // need to slow down if the distance is short  
  51.                     mDirection = normalizeDegree(mDirection  
  52.                             + ((to - mDirection) * mInterpolator  
  53.                                     .getInterpolation(Math.abs(distance) > MAX_ROATE_DEGREE ? 0.4f  
  54.                                             : 0.3f)));// 用了一个加速动画去旋转图片,很细致  
  55.                     mPointer.updateDirection(mDirection);// 更新指南针旋转  
  56.                 }  
  57.   
  58.                 updateDirection();// 更新方向值  
  59.   
  60.                 mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫米后重新执行自己,比定时器好  
  61.             }  
  62.         }  
  63.     };  
  64.   
  65.     @Override  
  66.     protected void onCreate(Bundle savedInstanceState) {  
  67.         super.onCreate(savedInstanceState);  
  68.         setContentView(R.layout.main);  
  69.         initResources();// 初始化view  
  70.         initServices();// 初始化传感器和位置服务   
  71.     }  
  72.   
  73.     @Override  
  74.     public void onBackPressed() {// 覆盖返回键  
  75.         long curTime = System.currentTimeMillis();  
  76.         if (curTime - firstExitTime < EXIT_TIME) {// 两次按返回键的时间小于2秒就退出应用  
  77.             finish();  
  78.         } else {  
  79.             Toast.makeText(this, R.string.exit_toast, Toast.LENGTH_SHORT)  
  80.                     .show();  
  81.             firstExitTime = curTime;  
  82.         }  
  83.     }  
  84.   
  85.     @Override  
  86.     protected void onResume() {// 在恢复的生命周期里判断、启动位置更新服务和传感器服务  
  87.         super.onResume();  
  88.         if (mLocationProvider != null) {  
  89.             updateLocation(mLocationManager  
  90.                     .getLastKnownLocation(mLocationProvider));  
  91.             mLocationManager.requestLocationUpdates(mLocationProvider, 2000,  
  92.                     10, mLocationListener);// 2秒或者距离变化10米时更新一次地理位置  
  93.         } else {  
  94.             mLocationTextView.setText(R.string.cannot_get_location);  
  95.         }  
  96.         if (mOrientationSensor != null) {  
  97.             mSensorManager.registerListener(mOrientationSensorEventListener,  
  98.                     mOrientationSensor, SensorManager.SENSOR_DELAY_GAME);  
  99.         } else {  
  100.             Toast.makeText(this, R.string.cannot_get_sensor, Toast.LENGTH_SHORT)  
  101.                     .show();  
  102.         }  
  103.         mStopDrawing = false;  
  104.         mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫秒执行一次更新指南针图片旋转  
  105.     }  
  106.   
  107.     @Override  
  108.     protected void onPause() {// 在暂停的生命周期里注销传感器服务和位置更新服务  
  109.         super.onPause();  
  110.         mStopDrawing = true;  
  111.         if (mOrientationSensor != null) {  
  112.             mSensorManager.unregisterListener(mOrientationSensorEventListener);  
  113.         }  
  114.         if (mLocationProvider != null) {  
  115.             mLocationManager.removeUpdates(mLocationListener);  
  116.         }  
  117.     }  
  118.   
  119.     // 初始化view   
  120.     private void initResources() {  
  121.         mDirection = 0.0f;// 初始化起始方向  
  122.         mTargetDirection = 0.0f;// 初始化目标方向  
  123.         mInterpolator = new AccelerateInterpolator();// 实例化加速动画对象  
  124.         mStopDrawing = true;  
  125.         mChinease = TextUtils.equals(Locale.getDefault().getLanguage(), "zh");// 判断系统当前使用的语言是否为中文  
  126.   
  127.         mCompassView = findViewById(R.id.view_compass);// 实际上是一个LinearLayout,装指南针ImageView和位置TextView  
  128.         mPointer = (CompassView) findViewById(R.id.compass_pointer);// 自定义的指南针view  
  129.         mLocationTextView = (TextView) findViewById(R.id.textview_location);// 显示位置信息的TextView  
  130.         mDirectionLayout = (LinearLayout) findViewById(R.id.layout_direction);// 顶部显示方向名称(东南西北)的LinearLayout  
  131.         mAngleLayout = (LinearLayout) findViewById(R.id.layout_angle);// 顶部显示方向具体度数的LinearLayout  
  132.   
  133.         mPointer.setImageResource(mChinease ? R.drawable.compass_cn  
  134.                 : R.drawable.compass);// 如果系统使用中文,就用中文的指南针图片  
  135.     }  
  136.   
  137.     // 初始化传感器和位置服务   
  138.     private void initServices() {  
  139.         // sensor manager   
  140.         mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);  
  141.         mOrientationSensor = mSensorManager  
  142.                 .getDefaultSensor(Sensor.TYPE_ORIENTATION);  
  143.   
  144.         // location manager   
  145.         mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);  
  146.         Criteria criteria = new Criteria();// 条件对象,即指定条件过滤获得LocationProvider  
  147.         criteria.setAccuracy(Criteria.ACCURACY_FINE);// 较高精度  
  148.         criteria.setAltitudeRequired(false);// 是否需要高度信息  
  149.         criteria.setBearingRequired(false);// 是否需要方向信息  
  150.         criteria.setCostAllowed(true);// 是否产生费用  
  151.         criteria.setPowerRequirement(Criteria.POWER_LOW);// 设置低电耗  
  152.         mLocationProvider = mLocationManager.getBestProvider(criteria, true);// 获取条件最好的Provider  
  153.   
  154.     }  
  155.   
  156.     // 更新顶部方向显示的方法   
  157.     private void updateDirection() {  
  158.         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,  
  159.                 LayoutParams.WRAP_CONTENT);  
  160.         // 先移除layout中所有的view   
  161.         mDirectionLayout.removeAllViews();  
  162.         mAngleLayout.removeAllViews();  
  163.   
  164.         // 下面是根据mTargetDirection,作方向名称图片的处理  
  165.         ImageView east = null;  
  166.         ImageView west = null;  
  167.         ImageView south = null;  
  168.         ImageView north = null;  
  169.         float direction = normalizeDegree(mTargetDirection * -1.0f);  
  170.         if (direction > 22.5f && direction < 157.5f) {  
  171.             // east   
  172.             east = new ImageView(this);  
  173.             east.setImageResource(mChinease ? R.drawable.e_cn : R.drawable.e);  
  174.             east.setLayoutParams(lp);  
  175.         } else if (direction > 202.5f && direction < 337.5f) {  
  176.             // west   
  177.             west = new ImageView(this);  
  178.             west.setImageResource(mChinease ? R.drawable.w_cn : R.drawable.w);  
  179.             west.setLayoutParams(lp);  
  180.         }  
  181.   
  182.         if (direction > 112.5f && direction < 247.5f) {  
  183.             // south   
  184.             south = new ImageView(this);  
  185.             south.setImageResource(mChinease ? R.drawable.s_cn : R.drawable.s);  
  186.             south.setLayoutParams(lp);  
  187.         } else if (direction < 67.5 || direction > 292.5f) {  
  188.             // north   
  189.             north = new ImageView(this);  
  190.             north.setImageResource(mChinease ? R.drawable.n_cn : R.drawable.n);  
  191.             north.setLayoutParams(lp);  
  192.         }  
  193.         // 下面是根据系统使用语言,更换对应的语言图片资源  
  194.         if (mChinease) {  
  195.             // east/west should be before north/south  
  196.             if (east != null) {  
  197.                 mDirectionLayout.addView(east);  
  198.             }  
  199.             if (west != null) {  
  200.                 mDirectionLayout.addView(west);  
  201.             }  
  202.             if (south != null) {  
  203.                 mDirectionLayout.addView(south);  
  204.             }  
  205.             if (north != null) {  
  206.                 mDirectionLayout.addView(north);  
  207.             }  
  208.         } else {  
  209.             // north/south should be before east/west  
  210.             if (south != null) {  
  211.                 mDirectionLayout.addView(south);  
  212.             }  
  213.             if (north != null) {  
  214.                 mDirectionLayout.addView(north);  
  215.             }  
  216.             if (east != null) {  
  217.                 mDirectionLayout.addView(east);  
  218.             }  
  219.             if (west != null) {  
  220.                 mDirectionLayout.addView(west);  
  221.             }  
  222.         }  
  223.         // 下面是根据方向度数显示度数图片数字   
  224.         int direction2 = (int) direction;  
  225.         boolean show = false;  
  226.         if (direction2 >= 100) {  
  227.             mAngleLayout.addView(getNumberImage(direction2 / 100));  
  228.             direction2 %= 100;  
  229.             show = true;  
  230.         }  
  231.         if (direction2 >= 10 || show) {  
  232.             mAngleLayout.addView(getNumberImage(direction2 / 10));  
  233.             direction2 %= 10;  
  234.         }  
  235.         mAngleLayout.addView(getNumberImage(direction2));  
  236.         // 下面是增加一个°的图片   
  237.         ImageView degreeImageView = new ImageView(this);  
  238.         degreeImageView.setImageResource(R.drawable.degree);  
  239.         degreeImageView.setLayoutParams(lp);  
  240.         mAngleLayout.addView(degreeImageView);  
  241.     }  
  242.   
  243.     // 获取方向度数对应的图片,返回ImageView  
  244.     private ImageView getNumberImage(int number) {  
  245.         ImageView image = new ImageView(this);  
  246.         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,  
  247.                 LayoutParams.WRAP_CONTENT);  
  248.         switch (number) {  
  249.         case 0:  
  250.             image.setImageResource(R.drawable.number_0);  
  251.             break;  
  252.         case 1:  
  253.             image.setImageResource(R.drawable.number_1);  
  254.             break;  
  255.         case 2:  
  256.             image.setImageResource(R.drawable.number_2);  
  257.             break;  
  258.         case 3:  
  259.             image.setImageResource(R.drawable.number_3);  
  260.             break;  
  261.         case 4:  
  262.             image.setImageResource(R.drawable.number_4);  
  263.             break;  
  264.         case 5:  
  265.             image.setImageResource(R.drawable.number_5);  
  266.             break;  
  267.         case 6:  
  268.             image.setImageResource(R.drawable.number_6);  
  269.             break;  
  270.         case 7:  
  271.             image.setImageResource(R.drawable.number_7);  
  272.             break;  
  273.         case 8:  
  274.             image.setImageResource(R.drawable.number_8);  
  275.             break;  
  276.         case 9:  
  277.             image.setImageResource(R.drawable.number_9);  
  278.             break;  
  279.         }  
  280.         image.setLayoutParams(lp);  
  281.         return image;  
  282.     }  
  283.   
  284.     // 更新位置显示   
  285.     private void updateLocation(Location location) {  
  286.         if (location == null) {  
  287.             mLocationTextView.setText(R.string.getting_location);  
  288.         } else {  
  289.             StringBuilder sb = new StringBuilder();  
  290.             double latitude = location.getLatitude();  
  291.             double longitude = location.getLongitude();  
  292.   
  293.             if (latitude >= 0.0f) {  
  294.                 sb.append(getString(R.string.location_north,  
  295.                         getLocationString(latitude)));  
  296.             } else {  
  297.                 sb.append(getString(R.string.location_south,  
  298.                         getLocationString(-1.0 * latitude)));  
  299.             }  
  300.   
  301.             sb.append("    ");  
  302.   
  303.             if (longitude >= 0.0f) {  
  304.                 sb.append(getString(R.string.location_east,  
  305.                         getLocationString(longitude)));  
  306.             } else {  
  307.                 sb.append(getString(R.string.location_west,  
  308.                         getLocationString(-1.0 * longitude)));  
  309.             }  
  310.             mLocationTextView.setText(sb.toString());// 显示经纬度,其实还可以作反向编译,显示具体地址  
  311.         }  
  312.     }  
  313.   
  314.     // 把经纬度转换成度分秒显示   
  315.     private String getLocationString(double input) {  
  316.         int du = (int) input;  
  317.         int fen = (((int) ((input - du) * 3600))) / 60;  
  318.         int miao = (((int) ((input - du) * 3600))) % 60;  
  319.         return String.valueOf(du) + "°" + String.valueOf(fen) + "′"  
  320.                 + String.valueOf(miao) + "″";  
  321.     }  
  322.   
  323.     // 方向传感器变化监听   
  324.     private SensorEventListener mOrientationSensorEventListener = new SensorEventListener() {  
  325.   
  326.         @Override  
  327.         public void onSensorChanged(SensorEvent event) {  
  328.             float direction = event.values[0] * -1.0f;  
  329.             mTargetDirection = normalizeDegree(direction);// 赋值给全局变量,让指南针旋转  
  330.         }  
  331.   
  332.         @Override  
  333.         public void onAccuracyChanged(Sensor sensor, int accuracy) {  
  334.         }  
  335.     };  
  336.   
  337.     // 调整方向传感器获取的值   
  338.     private float normalizeDegree(float degree) {  
  339.         return (degree + 720) % 360;  
  340.     }  
  341.   
  342.     // 位置信息更新监听   
  343.     LocationListener mLocationListener = new LocationListener() {  
  344.   
  345.         @Override  
  346.         public void onStatusChanged(String provider, int status, Bundle extras) {  
  347.             if (status != LocationProvider.OUT_OF_SERVICE) {  
  348.                 updateLocation(mLocationManager  
  349.                         .getLastKnownLocation(mLocationProvider));  
  350.             } else {  
  351.                 mLocationTextView.setText(R.string.cannot_get_location);  
  352.             }  
  353.         }  
  354.   
  355.         @Override  
  356.         public void onProviderEnabled(String provider) {  
  357.         }  
  358.   
  359.         @Override  
  360.         public void onProviderDisabled(String provider) {  
  361.         }  
  362.   
  363.         @Override  
  364.         public void onLocationChanged(Location location) {  
  365.             updateLocation(location);// 更新位置  
  366.         }  
  367.     };  
  368. }  
/** * MainActivity *  * @author way *  */public class CompassActivity extends Activity {private static final int EXIT_TIME = 2000;// 两次按返回键的间隔判断private final float MAX_ROATE_DEGREE = 1.0f;// 最多旋转一周,即360°private SensorManager mSensorManager;// 传感器管理对象private Sensor mOrientationSensor;// 传感器对象private LocationManager mLocationManager;// 位置管理对象private String mLocationProvider;// 位置提供者名称,GPS设备还是网络private float mDirection;// 当前浮点方向private float mTargetDirection;// 目标浮点方向private AccelerateInterpolator mInterpolator;// 动画从开始到结束,变化率是一个加速的过程,就是一个动画速率protected final Handler mHandler = new Handler();private boolean mStopDrawing;// 是否停止指南针旋转的标志位private boolean mChinease;// 系统当前是否使用中文private long firstExitTime = 0L;// 用来保存第一次按返回键的时间View mCompassView;CompassView mPointer;// 指南针viewTextView mLocationTextView;// 显示位置的viewLinearLayout mDirectionLayout;// 显示方向(东南西北)的viewLinearLayout mAngleLayout;// 显示方向度数的view// 这个是更新指南针旋转的线程,handler的灵活使用,每20毫秒检测方向变化值,对应更新指南针旋转protected Runnable mCompassViewUpdater = new Runnable() {@Overridepublic void run() {if (mPointer != null && !mStopDrawing) {if (mDirection != mTargetDirection) {// calculate the short routinefloat to = mTargetDirection;if (to - mDirection > 180) {to -= 360;} else if (to - mDirection < -180) {to += 360;}// limit the max speed to MAX_ROTATE_DEGREEfloat distance = to - mDirection;if (Math.abs(distance) > MAX_ROATE_DEGREE) {distance = distance > 0 ? MAX_ROATE_DEGREE: (-1.0f * MAX_ROATE_DEGREE);}// need to slow down if the distance is shortmDirection = normalizeDegree(mDirection+ ((to - mDirection) * mInterpolator.getInterpolation(Math.abs(distance) > MAX_ROATE_DEGREE ? 0.4f: 0.3f)));// 用了一个加速动画去旋转图片,很细致mPointer.updateDirection(mDirection);// 更新指南针旋转}updateDirection();// 更新方向值mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫米后重新执行自己,比定时器好}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);initResources();// 初始化viewinitServices();// 初始化传感器和位置服务}@Overridepublic void onBackPressed() {// 覆盖返回键long curTime = System.currentTimeMillis();if (curTime - firstExitTime < EXIT_TIME) {// 两次按返回键的时间小于2秒就退出应用finish();} else {Toast.makeText(this, R.string.exit_toast, Toast.LENGTH_SHORT).show();firstExitTime = curTime;}}@Overrideprotected void onResume() {// 在恢复的生命周期里判断、启动位置更新服务和传感器服务super.onResume();if (mLocationProvider != null) {updateLocation(mLocationManager.getLastKnownLocation(mLocationProvider));mLocationManager.requestLocationUpdates(mLocationProvider, 2000,10, mLocationListener);// 2秒或者距离变化10米时更新一次地理位置} else {mLocationTextView.setText(R.string.cannot_get_location);}if (mOrientationSensor != null) {mSensorManager.registerListener(mOrientationSensorEventListener,mOrientationSensor, SensorManager.SENSOR_DELAY_GAME);} else {Toast.makeText(this, R.string.cannot_get_sensor, Toast.LENGTH_SHORT).show();}mStopDrawing = false;mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫秒执行一次更新指南针图片旋转}@Overrideprotected void onPause() {// 在暂停的生命周期里注销传感器服务和位置更新服务super.onPause();mStopDrawing = true;if (mOrientationSensor != null) {mSensorManager.unregisterListener(mOrientationSensorEventListener);}if (mLocationProvider != null) {mLocationManager.removeUpdates(mLocationListener);}}// 初始化viewprivate void initResources() {mDirection = 0.0f;// 初始化起始方向mTargetDirection = 0.0f;// 初始化目标方向mInterpolator = new AccelerateInterpolator();// 实例化加速动画对象mStopDrawing = true;mChinease = TextUtils.equals(Locale.getDefault().getLanguage(), "zh");// 判断系统当前使用的语言是否为中文mCompassView = findViewById(R.id.view_compass);// 实际上是一个LinearLayout,装指南针ImageView和位置TextViewmPointer = (CompassView) findViewById(R.id.compass_pointer);// 自定义的指南针viewmLocationTextView = (TextView) findViewById(R.id.textview_location);// 显示位置信息的TextViewmDirectionLayout = (LinearLayout) findViewById(R.id.layout_direction);// 顶部显示方向名称(东南西北)的LinearLayoutmAngleLayout = (LinearLayout) findViewById(R.id.layout_angle);// 顶部显示方向具体度数的LinearLayoutmPointer.setImageResource(mChinease ? R.drawable.compass_cn: R.drawable.compass);// 如果系统使用中文,就用中文的指南针图片}// 初始化传感器和位置服务private void initServices() {// sensor managermSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);mOrientationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);// location managermLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);Criteria criteria = new Criteria();// 条件对象,即指定条件过滤获得LocationProvidercriteria.setAccuracy(Criteria.ACCURACY_FINE);// 较高精度criteria.setAltitudeRequired(false);// 是否需要高度信息criteria.setBearingRequired(false);// 是否需要方向信息criteria.setCostAllowed(true);// 是否产生费用criteria.setPowerRequirement(Criteria.POWER_LOW);// 设置低电耗mLocationProvider = mLocationManager.getBestProvider(criteria, true);// 获取条件最好的Provider}// 更新顶部方向显示的方法private void updateDirection() {LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);// 先移除layout中所有的viewmDirectionLayout.removeAllViews();mAngleLayout.removeAllViews();// 下面是根据mTargetDirection,作方向名称图片的处理ImageView east = null;ImageView west = null;ImageView south = null;ImageView north = null;float direction = normalizeDegree(mTargetDirection * -1.0f);if (direction > 22.5f && direction < 157.5f) {// easteast = new ImageView(this);east.setImageResource(mChinease ? R.drawable.e_cn : R.drawable.e);east.setLayoutParams(lp);} else if (direction > 202.5f && direction < 337.5f) {// westwest = new ImageView(this);west.setImageResource(mChinease ? R.drawable.w_cn : R.drawable.w);west.setLayoutParams(lp);}if (direction > 112.5f && direction < 247.5f) {// southsouth = new ImageView(this);south.setImageResource(mChinease ? R.drawable.s_cn : R.drawable.s);south.setLayoutParams(lp);} else if (direction < 67.5 || direction > 292.5f) {// northnorth = new ImageView(this);north.setImageResource(mChinease ? R.drawable.n_cn : R.drawable.n);north.setLayoutParams(lp);}// 下面是根据系统使用语言,更换对应的语言图片资源if (mChinease) {// east/west should be before north/southif (east != null) {mDirectionLayout.addView(east);}if (west != null) {mDirectionLayout.addView(west);}if (south != null) {mDirectionLayout.addView(south);}if (north != null) {mDirectionLayout.addView(north);}} else {// north/south should be before east/westif (south != null) {mDirectionLayout.addView(south);}if (north != null) {mDirectionLayout.addView(north);}if (east != null) {mDirectionLayout.addView(east);}if (west != null) {mDirectionLayout.addView(west);}}// 下面是根据方向度数显示度数图片数字int direction2 = (int) direction;boolean show = false;if (direction2 >= 100) {mAngleLayout.addView(getNumberImage(direction2 / 100));direction2 %= 100;show = true;}if (direction2 >= 10 || show) {mAngleLayout.addView(getNumberImage(direction2 / 10));direction2 %= 10;}mAngleLayout.addView(getNumberImage(direction2));// 下面是增加一个°的图片ImageView degreeImageView = new ImageView(this);degreeImageView.setImageResource(R.drawable.degree);degreeImageView.setLayoutParams(lp);mAngleLayout.addView(degreeImageView);}// 获取方向度数对应的图片,返回ImageViewprivate ImageView getNumberImage(int number) {ImageView image = new ImageView(this);LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);switch (number) {case 0:image.setImageResource(R.drawable.number_0);break;case 1:image.setImageResource(R.drawable.number_1);break;case 2:image.setImageResource(R.drawable.number_2);break;case 3:image.setImageResource(R.drawable.number_3);break;case 4:image.setImageResource(R.drawable.number_4);break;case 5:image.setImageResource(R.drawable.number_5);break;case 6:image.setImageResource(R.drawable.number_6);break;case 7:image.setImageResource(R.drawable.number_7);break;case 8:image.setImageResource(R.drawable.number_8);break;case 9:image.setImageResource(R.drawable.number_9);break;}image.setLayoutParams(lp);return image;}// 更新位置显示private void updateLocation(Location location) {if (location == null) {mLocationTextView.setText(R.string.getting_location);} else {StringBuilder sb = new StringBuilder();double latitude = location.getLatitude();double longitude = location.getLongitude();if (latitude >= 0.0f) {sb.append(getString(R.string.location_north,getLocationString(latitude)));} else {sb.append(getString(R.string.location_south,getLocationString(-1.0 * latitude)));}sb.append("    ");if (longitude >= 0.0f) {sb.append(getString(R.string.location_east,getLocationString(longitude)));} else {sb.append(getString(R.string.location_west,getLocationString(-1.0 * longitude)));}mLocationTextView.setText(sb.toString());// 显示经纬度,其实还可以作反向编译,显示具体地址}}// 把经纬度转换成度分秒显示private String getLocationString(double input) {int du = (int) input;int fen = (((int) ((input - du) * 3600))) / 60;int miao = (((int) ((input - du) * 3600))) % 60;return String.valueOf(du) + "°" + String.valueOf(fen) + "′"+ String.valueOf(miao) + "″";}// 方向传感器变化监听private SensorEventListener mOrientationSensorEventListener = new SensorEventListener() {@Overridepublic void onSensorChanged(SensorEvent event) {float direction = event.values[0] * -1.0f;mTargetDirection = normalizeDegree(direction);// 赋值给全局变量,让指南针旋转}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}};// 调整方向传感器获取的值private float normalizeDegree(float degree) {return (degree + 720) % 360;}// 位置信息更新监听LocationListener mLocationListener = new LocationListener() {@Overridepublic void onStatusChanged(String provider, int status, Bundle extras) {if (status != LocationProvider.OUT_OF_SERVICE) {updateLocation(mLocationManager.getLastKnownLocation(mLocationProvider));} else {mLocationTextView.setText(R.string.cannot_get_location);}}@Overridepublic void onProviderEnabled(String provider) {}@Overridepublic void onProviderDisabled(String provider) {}@Overridepublic void onLocationChanged(Location location) {updateLocation(location);// 更新位置}};}

 

好了,核心代码就这些了,其实思路还是很简单,最后,感谢各位看到文章最后,祝愿各位程序猿们好好学习,天天向上!奋斗

原创粉丝点击