藝術來源於生活,對我來講,編程也是一門藝術。今天發佈這篇技術博客,就是我跟朋友在一次登山過程當中的爭論,他跟我說那座山至少45度,我說沒有,最多30度。咱們彼此爭論不休,因而我就想,爲啥不寫個手機程序來實際測量一下?因而,個人工做就開始了。android
由於我暫時沒有筆記本,因此就用了個人ZTE-U807N做爲編寫平臺,它雖然配置較低,但有加速度傳感器就夠了。算法
我選擇移動端開發工具,使用AIDE做爲集成開發環境(表示它真的很強大)。編程
加速度傳感器能夠測量三座標份量,即X, Y, Z座標。XY平面是手機平面,Z座標垂直手機平面,手機觸屏朝上Z值爲正。經過簡單的數學推導能夠得出坡度的餘弦值等於|Z|比上X、Y、Z的矢量和的模。寫成公式就是以下:
g = √(X²+Y²+Z²)
cosθ = |z|/g
因而,我就可以經過Z和g得出坡度θ的值。
因爲加速度傳感器採樣速率很快,並且測量值不許,老是會變,因此我採樣滑動平均算法,將各測量份量進行積累,而後求取平均值來消除偏差,同時也能夠消除手機靜止時坡度數值跳變的問題。數據結構
使用隊列存儲各份量數據,等數據積累完畢之後每來一個數據就出一個老數據。框架
我須要2個界面,一個主界面,就是現地坡度;另外一個是關於界面,就是軟件說明、做者等等。具體如圖所示:
ide
首先在MainActivity放置一個TextView用於顯示坡度。而後我給它加上一個SeekBar用於靈敏度調節,代碼以下:函數
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:orientation="vertical" android:padding="20dp"> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/mainTextView1" android:textSize="80sp"/> <LinearLayout android:layout_height="wrap_content" android:layout_width="match_parent" android:orientation="horizontal"> <TextView android:layout_height="match_parent" android:text="靈敏度調節" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:paddingRight="5dp" android:gravity="center_vertical"/> <SeekBar android:layout_height="wrap_content" android:layout_width="match_parent" android:id="@+id/mainSeekBar1"/> </LinearLayout> </LinearLayout>
其次就是關於About界面,就是做者說明、版本啊等等,而後有個按鈕,按下可以返回MainActivity界面。代碼以下:工具
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:gravity="center"> <TextView android:layout_height="wrap_content" android:text="@string/version" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_width="wrap_content"/> <TextView android:layout_height="wrap_content" android:text="@string/copy_right" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content"/> <TextView android:layout_height="wrap_content" android:text="@string/description" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content"/> <Button android:layout_height="wrap_content" android:text="肯定" android:layout_width="wrap_content" android:id="@+id/aboutButton1"/> </LinearLayout>
而後我要有一個設置菜單,在MainActivity類裏面加入onCreateOptionsMenu
函數,代碼以下:開發工具
@Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.FIRST, Menu.FIRST, Menu.FIRST, "關於").setIcon(android.R.drawable.ic_dialog_info); menu.add(Menu.FIRST+1, Menu.FIRST+1, Menu.FIRST+1, "退出").setIcon(android.R.drawable.ic_lock_power_off); return true; }
固然要給菜單添加單擊處理:this
@Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == Menu.FIRST) { /*關於界面*/ Intent intent = new Intent(); intent.setClass(MainActivity.this, About.class); startActivity(intent); } else if(item.getItemId() == Menu.FIRST + 1) { /*退出*/ android.os.Process.killProcess(android.os.Process.myPid()); finish(); } return super.onOptionsItemSelected(item); }
還有大多數軟件有的連續按兩次返回鍵退出程序:
/*long curent = 0;*/ @Override public void onBackPressed() { if (System.currentTimeMillis() - current > 2000) { //若是兩次按鍵時間大於2秒 current = System.currentTimeMillis(); Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show(); } else { android.os.Process.killProcess(android.os.Process.myPid()); finish(); } }
最後就是在關於界面按鍵可以返回到主界面的功能:
@Override public void onClick(View p1) { super.onBackPressed(); //實現按鍵返回上一活動 }
我偷了懶,調用父類的onBackPressed函數,就至關於按下返回鍵。
首先是數據定義與初始化:
private long current; private TextView tv = null; private SeekBar sb = null; private SeekListener seekl = null; //自定義類 private SensorManager sm = null; private Sensor as = null; private SenListener sl = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); current = 0; tv = (TextView)findViewById(R.id.mainTextView1); sb = (SeekBar)findViewById(R.id.mainSeekBar1); sb.setMax(3); //能夠控制4個級別 sb.setOnSeekBarChangeListener(seekl = new SeekListener()); sm = (SensorManager)getSystemService(Service.SENSOR_SERVICE); //獲取傳感器管理器 as = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); //獲取加速度傳感器 sm.registerListener(sl = new SenListener(), as, SensorManager.SENSOR_DELAY_FASTEST); //註冊傳感器 Toast.makeText(MainActivity.this, "請使手機平行於地面", Toast.LENGTH_LONG).show(); }
而後是傳感器監聽器的實現:
/**核心算法以及傳感器監聽器**/ private class SenListener implements SensorEventListener { private int sizeLimit; //緩衝區大小 private Queue<Float> queX, queY, queZ; //各份量緩衝區 private float sumX, sumY, sumZ; //和 private float aveX, aveY, aveZ; //平均 private float g; //矢量和 private float gradient; //坡度 SenListener() { sizeLimit = 100; queX = new LinkedList<>(); queY = new LinkedList<>(); queZ = new LinkedList<>(); sumX = sumY = sumZ = 0; } void setLimit(int grade) { sizeLimit = 100 * (grade + 1); //SeekBar選擇來控制緩衝區大小,據此能夠調節靈敏度 } private double grad2Deg (double grad) { return grad * 180 / Math.PI; } @Override public void onSensorChanged(SensorEvent p1) { float x, y, z; x = p1.values[0]; y = p1.values[1]; z = p1.values[2]; /*滑動平均算法核心部分*/ sumX += x; sumY += y; sumZ += z; queX.offer(x); queY.offer(y); queZ.offer(z); while (queX.size() > sizeLimit) { sumX -= queX.poll(); sumY -= queY.poll(); sumZ -= queZ.poll(); } aveX = sumX / queX.size(); aveY = sumY / queX.size(); aveZ = sumZ / queX.size(); g = (float)Math.sqrt(aveX * aveX + aveY * aveY + aveZ * aveZ); gradient = (float)Math.acos(Math.abs(aveZ) / g); tv.setText(Math.round(grad2Deg(gradient)) + "º"); //弧度轉爲度 } @Override public void onAccuracyChanged(Sensor p1, int p2) { // TODO: Implement this method } @Override protected void finalize() throws Throwable { // TODO: Implement this method queX = queY = queZ = null; super.finalize(); } }
我最終編寫出了坡度計,解決了我跟朋友的問題。我將我所想的與作的分享出來,寫的也有些亂,若是有不明白的地方歡迎提問。另外,我將整個工程放到了個人百度網盤裏,有興趣的話歡迎下載,雖然是手機上建的,但它兼容Eclipse。http://pan.baidu.com/share/link?shareid=3657265547&uk=2315273780