package com.example.compass;
import java.util.Locale;
import android.graphics.drawable.AnimationDrawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
public class CompassActivity extends Activity {
private static final int EXIT_TIME = 2000;// 兩次按返回鍵的間隔判斷
private final float MAX_ROATE_DEGREE = 1.0f;// 最多旋轉(zhuǎn)一周,即360°
private SensorManager mSensorManager;// 傳感器管理對(duì)象
private Sensor mOrientationSensor;// 傳感器對(duì)象
private float mDirection;// 當(dāng)前浮點(diǎn)方向
private float mTargetDirection;// 目標(biāo)浮點(diǎn)方向
private AccelerateInterpolator mInterpolator;// 動(dòng)畫從開始到結(jié)束,變化率是一個(gè)加速的過程,就是一個(gè)動(dòng)畫速率
protected final Handler mHandler = new Handler();
private boolean mStopDrawing;// 是否停止指南針旋轉(zhuǎn)的標(biāo)志位
private boolean mChinease;// 系統(tǒng)當(dāng)前是否使用中文
private long firstExitTime = 0L;// 用來保存第一次按返回鍵的時(shí)間
View mCompassView;
CompassView mPointer;// 指南針view
LinearLayout mDirectionLayout;// 顯示方向(東南西北)的view
LinearLayout mAngleLayout;// 顯示方向度數(shù)的view
View mViewGuide;
ImageView mGuideAnimation;
@SuppressLint("HandlerLeak")
protected Handler invisiableHandler = new Handler() {
public void handleMessage(Message msg) {
mViewGuide.setVisibility(View.GONE);
}
};
public void onWindowFocusChanged(boolean hasFocus) {
AnimationDrawable anim = (AnimationDrawable) mGuideAnimation
.getDrawable();
anim.start();
}
// 這個(gè)是更新指南針旋轉(zhuǎn)的線程,handler的靈活使用,每20毫秒檢測(cè)方向變化值,對(duì)應(yīng)更新指南針旋轉(zhuǎn)
protected Runnable mCompassViewUpdater = new Runnable() {
@Override
public void run() {
if (mPointer != null && !mStopDrawing) {
if (mDirection != mTargetDirection) {
// calculate the short routine
float to = mTargetDirection;
if (to - mDirection > 180) {
to -= 360;
} else if (to - mDirection < -180) {
to += 360;
}
// limit the max speed to MAX_ROTATE_DEGREE
float 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 short
mDirection = normalizeDegree(mDirection
+ ((to - mDirection) * mInterpolator
.getInterpolation(Math.abs(distance) > MAX_ROATE_DEGREE ? 0.4f
: 0.3f)));// 用了一個(gè)加速動(dòng)畫去旋轉(zhuǎn)圖片,很細(xì)致
mPointer.updateDirection(mDirection);// 更新指南針旋轉(zhuǎn)
}
updateDirection();// 更新方向值
mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫米后重新執(zhí)行自己,比定時(shí)器好
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initResources();// 初始化view
initServices();// 初始化傳感器和位置服務(wù)
}
@Override
public void onBackPressed() {// 覆蓋返回鍵
long curTime = System.currentTimeMillis();
if (curTime - firstExitTime < EXIT_TIME) {// 兩次按返回鍵的時(shí)間小于2秒就退出應(yīng)用
finish();
} else {
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
firstExitTime = curTime;
}
}
@Override
protected void onResume() {// 在恢復(fù)的生命周期里判斷、啟動(dòng)位置更新服務(wù)和傳感器服務(wù)
super.onResume();
if (mOrientationSensor != null) {
mSensorManager.registerListener(mOrientationSensorEventListener,
mOrientationSensor, SensorManager.SENSOR_DELAY_GAME);
} else {
Toast.makeText(this, "不能找到傳感器!",
Toast.LENGTH_SHORT)
.show();
}
mStopDrawing = false;
mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫秒執(zhí)行一次更新指南針圖片旋轉(zhuǎn)
}
@Override
protected void onPause() {// 在暫停的生命周期里注銷傳感器服務(wù)和位置更新服務(wù)
super.onPause();
mStopDrawing = true;
if (mOrientationSensor != null) {
mSensorManager.unregisterListener(mOrientationSensorEventListener);
}
}
// 方向傳感器變化監(jiān)聽
private SensorEventListener mOrientationSensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
float direction = event.values[mSensorManager.DATA_X] * -1.0f;
mTargetDirection = normalizeDegree(direction);// 賦值給全局變量,讓指南針旋轉(zhuǎn)
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
// 初始化view
private void initResources() {
mViewGuide = findViewById(R.id.view_guide);
mViewGuide.setVisibility(View.VISIBLE);
invisiableHandler.sendMessageDelayed(new Message(), 3000);
mGuideAnimation = (ImageView) findViewById(R.id.guide_animation);
mDirection = 0.0f;// 初始化起始方向
mTargetDirection = 0.0f;// 初始化目標(biāo)方向
mInterpolator = new AccelerateInterpolator();// 實(shí)例化加速動(dòng)畫對(duì)象
mStopDrawing = true;
mChinease = TextUtils.equals(Locale.getDefault().getLanguage(), "zh");// 判斷系統(tǒng)當(dāng)前使用的語言是否為中文
mCompassView = findViewById(R.id.view_compass);// 實(shí)際上是一個(gè)LinearLayout,裝指南針I(yè)mageView和位置TextView
mPointer = (CompassView) findViewById(R.id.compass_pointer);// 自定義的指南針view
mDirectionLayout = (LinearLayout) findViewById(R.id.layout_direction);// 頂部顯示方向名稱(東南西北)的LinearLayout
mAngleLayout = (LinearLayout) findViewById(R.id.layout_angle);// 頂部顯示方向具體度數(shù)的LinearLayout
}
// 初始化傳感器和位置服務(wù)
private void initServices() {
// sensor manager
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mOrientationSensor = mSensorManager.getSensorList(
Sensor.TYPE_ORIENTATION).get(0);
}
// 調(diào)整方向傳感器獲取的值
private float normalizeDegree(float degree) {
return (degree + 720) % 360;
}
// 更新頂部方向顯示的方法
private void updateDirection() {
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
// 先移除layout中所有的view
mDirectionLayout.removeAllViews();
mAngleLayout.removeAllViews();
// 下面是根據(jù)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) {
// east
east = new ImageView(this);
east.setImageResource(mChinease ? R.drawable.e_cn : R.drawable.e);
east.setLayoutParams(lp);
} else if (direction > 202.5f && direction < 337.5f) {
// west
west = new ImageView(this);
west.setImageResource(mChinease ? R.drawable.w_cn : R.drawable.w);
west.setLayoutParams(lp);
}
if (direction > 112.5f && direction < 247.5f) {
// south
south = new ImageView(this);
south.setImageResource(mChinease ? R.drawable.s_cn : R.drawable.s);
south.setLayoutParams(lp);
} else if (direction < 67.5 || direction > 292.5f) {
// north
north = new ImageView(this);
north.setImageResource(mChinease ? R.drawable.n_cn : R.drawable.n);
north.setLayoutParams(lp);
}
// 下面是根據(jù)系統(tǒng)使用語言,更換對(duì)應(yīng)的語言圖片資源
if (mChinease) {
if (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/west
if (south != null) {
mDirectionLayout.addView(south);
}
if (north != null) {
mDirectionLayout.addView(north);
}
if (east != null) {
mDirectionLayout.addView(east);
}
if (west != null) {
mDirectionLayout.addView(west);
}
}
// 下面是根據(jù)方向度數(shù)顯示度數(shù)圖片數(shù)字
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));
// 下面是增加一個(gè)°的圖片
ImageView degreeImageView = new ImageView(this);
degreeImageView.setImageResource(R.drawable.degree);
degreeImageView.setLayoutParams(lp);
mAngleLayout.addView(degreeImageView);
}
// 獲取方向度數(shù)對(duì)應(yīng)的圖片,返回ImageView
private ImageView getNumberImage(int number) {
ImageView p_w_picpath = new ImageView(this);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
switch (number) {
case 0:
p_w_picpath.setImageResource(R.drawable.number_0);
break;
case 1:
p_w_picpath.setImageResource(R.drawable.number_1);
break;
case 2:
p_w_picpath.setImageResource(R.drawable.number_2);
break;
case 3:
p_w_picpath.setImageResource(R.drawable.number_3);
break;
case 4:
p_w_picpath.setImageResource(R.drawable.number_4);
break;
case 5:
p_w_picpath.setImageResource(R.drawable.number_5);
break;
case 6:
p_w_picpath.setImageResource(R.drawable.number_6);
break;
case 7:
p_w_picpath.setImageResource(R.drawable.number_7);
break;
case 8:
p_w_picpath.setImageResource(R.drawable.number_8);
break;
case 9:
p_w_picpath.setImageResource(R.drawable.number_9);
break;
}
p_w_picpath.setLayoutParams(lp);
return p_w_picpath;
}
}
package com.example.compass;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class CompassView extends ImageView {
private float mDirection;// 方向旋轉(zhuǎn)浮點(diǎn)數(shù)
private Drawable compass;// 圖片資源
// 三個(gè)構(gòu)造器
public CompassView(Context context) {
super(context);
mDirection = 0.0f;// 默認(rèn)不旋轉(zhuǎn)
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;
}
@Override
protected void onDraw(Canvas canvas) {
if (compass == null) {
compass = getDrawable();// 獲取當(dāng)前view的圖片資源
compass.setBounds(0, 0, getWidth(), getHeight());// 圖片資源在view的位置,此處相當(dāng)于充滿view
}
canvas.save();
canvas.rotate(mDirection, getWidth() / 2, getHeight() / 2);// 繞圖片中心點(diǎn)旋轉(zhuǎn),
compass.draw(canvas);// 把旋轉(zhuǎn)后的圖片畫在view上,即保持旋轉(zhuǎn)后的樣子
canvas.restore();// 保存一下
}
/**
* 自定義更新方向的方法
*
* @param direction
* 傳入的方向
*/
public void updateDirection(float direction) {
mDirection = direction;
invalidate();// 重新刷新一下,更新方向
}
}
當(dāng)前名稱:android指南針
文章起源:
http://weahome.cn/article/pdcchi.html