Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android 水准儀的實現(方向傳感器的使用)

android 水准儀的實現(方向傳感器的使用)

編輯:關於android開發

android 水准儀的實現(方向傳感器的使用)


好久之前就已經研究了方向傳感器Sensor.TYPE_ORIENTATION。根據自已實踐,改寫了網上的兩個水准儀的例子,又重新封裝使用了一下,最後也用在了項目中。

1、前言介紹

下面這段話是出自Android 傳感器之方向傳感器

 

一般情況下,在android系統中獲取手機的方位信息在api中有TYPE_ORIENTATION常量,可以像得到加速度傳感器那樣得到方向傳感器sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);然而我們這樣做的話在最新版的SDK中就會看到這麼一句話:“TYPE_ORIENTATIONThis constant is deprecated. use SensorManager.getOrientation() instead. ”即這種方式也過期,不建議使用!Google建議我們在應用程序中使用SensorManager.getOrientation()來獲得原始數據。

 

public static float[] getOrientation (float[] R, float[] values)

第一個參數是R[] 是一個旋轉矩陣,用來保存磁場和加速度的數據,可以理解為這個函數的傳入值,通過它這個函數給你求出方位角。

第二個參數就是這個函數的輸出了,他有函數自動為我們填充,這就是我們想要的。

 

values[0]:方向角,但用(磁場+加速度)得到的數據范圍是(-180~180),也就是說,0表示正北,90表示正東,180/-180表示正南,-90表示正西。而直接通過方向感應器數據范圍是(0~359)360/0表示正北,90表示正東,180表示正南,270表示正西。

values[1]pitch 傾斜角即由靜止狀態開始,前後翻轉,手機頂部往上抬起(0~-90),手機尾部往上抬起(0~90)

values[2] roll旋轉角即由靜止狀態開始,左右翻轉,手機左側抬起(0~90),手機右側抬起(0~-90)

2、示例代碼一

 

package com.level.level1;

import com.level.level1.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

public class LevelView extends View
{
	// 定義水平儀大圓盤圖片
	Bitmap compass;
	// 定義水平儀中的氣泡圖標
	Bitmap ball;
	// 定義水平儀中氣泡 的X、Y座標
	int ballX, ballY;
	
	// 定義氣泡位於中間時(水平儀完全水平),氣泡的X、Y座標
	int cx, cy;
	// 定義水平儀大圓盤中心座標X、Y
	int backCx;
	int backCy;
	// 定義靈敏度,即水平儀能處理的最大傾斜角,超過該角度,氣泡將直接在位於邊界。
	int SENSITIVITY = 30;

	public LevelView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		// 加載水平儀大圓盤圖片和氣泡圖片
		compass = BitmapFactory.decodeResource(getResources()
			, R.drawable.back);
		ball = BitmapFactory
			.decodeResource(getResources(), R.drawable.small);
		
		
		// 計算出 水平儀完全水平時 氣泡位置  左上角為原點
		cx = (compass.getWidth() - ball.getWidth()) / 2;
		cy = (compass.getHeight() - ball.getHeight()) / 2;

		// 計算出水平儀大圓盤中心座標X、Y  左上角為原點
		backCx = compass.getWidth() / 2;
		backCy = compass.getWidth() / 2;
	}

	@Override
	protected void onDraw(Canvas canvas)
	{
		super.onDraw(canvas);
		// 繪制水平儀大圓盤圖片
		canvas.drawBitmap(compass, 0, 0, null);
		// 根據氣泡坐標繪制氣泡
		canvas.drawBitmap(ball, ballX, ballY, null);
	}
	
	public void onChangeXY(int zAngle,int yAngle,int xAngle){
		// 定義氣泡當前位置X Y坐標值
		int x, y;
		x = cx;
		y = cy;
		
		// 如果沿x軸的傾斜角  在最大角度之內則計算出其相應坐標值
		if (Math.abs(xAngle) <= SENSITIVITY) {
			// 根據與x軸的傾斜角度計算X座標的變化值(傾斜角度越大,X座標變化越大)
			int deltaX = (int) (cx * xAngle / SENSITIVITY);
			x += deltaX;
		}
		// 如果沿x軸的傾斜角已經大於MAX_ANGLE,氣泡應到最左邊
		else if (xAngle > SENSITIVITY) {
			x = 0;
		}
		// 如果與x軸的傾斜角已經小於負的MAX_ANGLE,氣泡應到最右邊
		else {
			x = cx * 2;
		}
		// 如果沿Y軸的傾斜角還在最大角度之內
		if (Math.abs(yAngle) <= SENSITIVITY) {
			// 根據沿Y軸的傾斜角度計算Y座標的變化值(傾斜角度越大,Y座標變化越大)
			int deltaY = (int) (cy * yAngle / SENSITIVITY);
			y += deltaY;
		}
		// 如果沿Y軸的傾斜角已經大於MAX_ANGLE,氣泡應到最下邊
		else if (yAngle > SENSITIVITY) {
			y = cy * 2;
		}
		// 如果沿Y軸的傾斜角已經小於負的MAX_ANGLE,氣泡應到最右邊
		else {
			y = 0;
		}
		// 如果計算出來的X、Y座標還位於水平儀的儀表盤內,更新水平儀的氣泡座標
		if (isContain(x, y)) {
			ballX = x;
			ballY = y;
		} else {
			// 有待後續繼續完成
		}
		//重繪界面
		invalidate();
	}
	
	// 計算x、y點的氣泡是否處於水平儀的大圓盤內
	private boolean isContain(int x, int y) {
		// 計算氣泡的圓心座標X、Y
		int ballCx = x + ball.getWidth() / 2;
		int ballCy = y + ball.getWidth() / 2;

		// 計算氣泡的圓心與水平儀大圓盤中中心之間的距離。
		double distance = Math.sqrt((ballCx - backCx) * (ballCx - backCx)
				+ (ballCy - backCy) * (ballCy - backCy));
		// 若兩個圓心的距離小於它們的半徑差,即可認為處於該點的氣泡依然位於儀表盤內
		if (distance < cx) {
			return true;
		} else {
			return false;
		}
	}
}

package com.level.level1;

import com.level.level1.R;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class MyGradienter extends Activity implements SensorEventListener {
	// 定義水平儀的儀大圓盤
	private LevelView myview;
	// 定義Sensor管理器
	SensorManager mySM;
	// 定義顯示欄 顯示X Y Z軸方向轉過角度與當前方位
	private TextView tx, ty, tz, td;
	private Sensor acc_sensor;
	private Sensor mag_sensor;
	// 加速度傳感器數據
	float accValues[] = new float[3];
	// 地磁傳感器數據
	float magValues[] = new float[3];
	// 旋轉矩陣,用來保存磁場和加速度的數據
	float r[] = new float[9];
	// 模擬方向傳感器的數據(原始數據為弧度)
	float values[] = new float[3];

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_my_gradienter);
		myview = (LevelView) findViewById(R.id.myview);
		tx = (TextView) findViewById(R.id.testviewx);
		ty = (TextView) findViewById(R.id.testviewy);
		tz = (TextView) findViewById(R.id.testviewz);
		td = (TextView) findViewById(R.id.testviewd);
		// 獲取手機傳感器管理服務
		mySM = (SensorManager) getSystemService(SENSOR_SERVICE);
	}

	@Override
	public void onResume() {
		super.onResume();

		acc_sensor = mySM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		mag_sensor = mySM.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
		// 給傳感器注冊監聽:
		mySM.registerListener(this, acc_sensor, SensorManager.SENSOR_DELAY_GAME);
		mySM.registerListener(this, mag_sensor, SensorManager.SENSOR_DELAY_GAME);
	}

	@Override
	protected void onPause() {
		// 取消方向傳感器的監聽
		mySM.unregisterListener(this);
		super.onPause();
	}

	@Override
	protected void onStop() {
		// 取消方向傳感器的監聽
		mySM.unregisterListener(this);
		super.onStop();
	}

	@Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		// 獲取手機觸發event的傳感器的類型
		int sensorType = event.sensor.getType();
		switch (sensorType) {
		case Sensor.TYPE_ACCELEROMETER:
			accValues = event.values.clone();
			break;
		case Sensor.TYPE_MAGNETIC_FIELD:
			magValues = event.values.clone();
			break;

		}

		SensorManager.getRotationMatrix(r, null, accValues, magValues);
		SensorManager.getOrientation(r, values);

		// 獲取 沿著Z軸轉過的角度
		int zAngle = (int) Math.toDegrees(values[0]);
		tz.setText("Z軸方向轉過的角度:" + zAngle);
		// 顯示當前的方位
		displayCompass(zAngle);

		// 獲取 沿著X軸傾斜時 與Y軸的夾角
		int yAngle = (int) Math.toDegrees(values[1]);
		ty.setText("Y軸方向翹起的角度:" + yAngle);

		// 獲取 沿著Y軸的滾動時 與X軸的角度
		int xAngle = (int) Math.toDegrees(values[2]);
		tx.setText("x軸方向翹起的角度:" + xAngle);
		
		myview.onChangeXY(zAngle,yAngle,xAngle);
	}

	private void displayCompass(int angle) {
		if ((angle < 22.5) || (angle > 337.5))
			td.setText("手機頂部當前方位: 北");
		if ((angle > 22.5) && (angle < 67.5))
			td.setText("手機頂部當前方位: 西北");
		if ((angle > 67.5) && (angle < 112.5))
			td.setText("手機頂部當前方位: 西");
		if ((angle > 112.5) && (angle < 157.5))
			td.setText("手機頂部當前方位: 西北");
		if ((angle > 157.5) && (angle < 202.5))
			td.setText("手機頂部當前方位: 南");
		if ((angle > 202.5) && (angle < 247.5))
			td.setText("手機頂部當前方位: 東南");
		if ((angle > 247.5) && (angle < 292.5))
			td.setText("手機頂部當前方位: 東");
		if ((angle > 292.5) && (angle < 337.5))
			td.setText("手機頂部當前方位: 東北");
	}
}

 

3、示例代碼二

 

 

package com.tcjt.level2;

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.RectF;
import android.util.AttributeSet;
import android.view.View;

public class MainView extends View {

	Paint paint = new Paint(); //畫筆
	Bitmap shangBitmap1; //上面的大矩形圖
	Bitmap shangBitmap2; //上面的氣泡
	Bitmap zuoBitmap1; //左面的大矩形圖
	Bitmap zuoBitmap2; //左面圖的氣泡
	Bitmap zhongBitmap1; //中間的大圓圖
	Bitmap zhongBitmap2; //中間的小氣泡
	Bitmap xiaBitmap1; //右下的矩形圖
	Bitmap xiaBitmap2; //右下的氣泡
	
	//背景矩形的位置聲明
	int shang1_X = 60; //上面的大矩形圖
	int shang1_Y = 12;
	int zuo1_X = 12; //左面的大矩形圖
	int zuo1_Y = 60;
	int zhong1_X = 65; //中間的大圓圖
	int zhong1_Y = 65;
	int xia1_X = 145; //右下的矩形圖
	int xia1_Y = 145;//水泡的位置聲明
	int shang2_X; //上面的氣泡XY 坐標
	int shang2_Y;
	int zuo2_X; //左面圖的氣泡XY 坐標
	int zuo2_Y;
	int zhong2_X; //中間的小氣泡XY 坐標
	int zhong2_Y;
	int xia2_X; //右下的氣泡XY 坐標
	int xia2_Y;

	public MainView(Context context, AttributeSet attrs){
		super(context, attrs);
		initBitmap(); //初始化圖片資源
		initLocation(); //初始化氣泡的位置
	}

	private void initBitmap(){ //初始化圖片的方法
		//該處省略了部分代碼,將在後面進行介紹
		shangBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_shang1);
		shangBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_shang2);
		zuoBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zuo1);
		zuoBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zuo2);
		zhongBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zhong1);
		zhongBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zhong2);
		xiaBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_xia1);
		xiaBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_xia2);
	}

	private void initLocation(){ //初始化氣泡位置的方法
		//該處省略了部分代碼,將在後面進行介紹
		shang2_X = shang1_X + shangBitmap1.getWidth()/2- shangBitmap2.getWidth()/2;
		shang2_Y = shang1_Y + shangBitmap1.getHeight()/2- shangBitmap2.getHeight()/2;
		zuo2_X = zuo1_X + zuoBitmap1.getWidth()/2- zuoBitmap2.getWidth()/2;
		zuo2_Y = zuo1_Y + zuoBitmap1.getHeight()/2- zuoBitmap2.getHeight()/2;
		zhong2_X = zhong1_X + zhongBitmap1.getWidth()/2- zhongBitmap2.getWidth()/2;
		zhong2_Y = zhong1_Y + zhongBitmap1.getHeight()/2- zhongBitmap2.getHeight()/2;
		xia2_X = xia1_X + xiaBitmap1.getWidth()/2- xiaBitmap2.getWidth()/2;
		xia2_Y = xia1_Y + xiaBitmap1.getHeight()/2- xiaBitmap2.getHeight()/2;
	}

	@Override
	protected void onDraw(Canvas canvas){//重寫的繪制方法
		super.onDraw(canvas);
		//該處省略了部分代碼,將在後面進行介紹
		canvas.drawColor(Color.WHITE); //設置背景色為白色

		paint.setColor(Color.BLUE); //設置畫筆顏色
		paint.setStyle(Style.STROKE); //設置畫筆為不填充
		canvas.drawRect(5, 5, 315, 315, paint);//繪制外邊框矩形

		//畫背景矩形
		canvas.drawBitmap(shangBitmap1, shang1_X,shang1_Y, paint); //上
		canvas.drawBitmap(zuoBitmap1, zuo1_X,zuo1_Y, paint); //左
		canvas.drawBitmap(zhongBitmap1, zhong1_X,zhong1_Y, paint); //中
		canvas.drawBitmap(xiaBitmap1, xia1_X,xia1_Y, paint); //下

		//開始繪制氣泡
		canvas.drawBitmap(shangBitmap2, shang2_X,shang2_Y, paint); //上
		canvas.drawBitmap(zuoBitmap2, zuo2_X,zuo2_Y, paint); //左
		canvas.drawBitmap(zhongBitmap2, zhong2_X,zhong2_Y, paint); //中
		canvas.drawBitmap(xiaBitmap2, xia2_X, xia2_Y, paint);//下

		paint.setColor(Color.GRAY);//設置畫筆顏色用來繪制刻度

		//繪制上面方框中的刻度
		canvas.drawLine (shang1_X+shangBitmap1.getWidth()/2-7,shang1_Y, shang1_X+shangBitmap1.getWidth()/2-7,shang1_Y+shangBitmap1.getHeight()-2, paint);
		canvas.drawLine (shang1_X+shangBitmap1.getWidth()/2+7,shang1_Y, shang1_X+shangBitmap1.getWidth()/2+7,shang1_Y+shangBitmap1.getHeight()-2, paint);

		//繪制左面方框中的刻度
		canvas.drawLine(zuo1_X,zuo1_Y+zuoBitmap1.getHeight()/2-7,zuo1_X+zuoBitmap1.getWidth()-2,zuo1_Y+zuoBitmap1.getHeight()/2-7, paint);canvas.drawLine(zuo1_X,zuo1_Y+zuoBitmap1.getHeight()/2+7,zuo1_X+zuoBitmap1.getWidth()-2,zuo1_Y+zuoBitmap1.getHeight()/2+7, paint);
		
		//繪制下面方框中的刻度

		canvas.drawLine(xia1_X+xiaBitmap1.getWidth()/2-10,xia1_Y+xiaBitmap1.getHeight()/2-20,xia1_X+xiaBitmap1.getWidth()/2+20,xia1_Y+xiaBitmap1.getHeight()/2+10, paint);
		canvas.drawLine(xia1_X+xiaBitmap1.getWidth()/2-20,xia1_Y+xiaBitmap1.getHeight()/2-10,xia1_X+xiaBitmap1.getWidth()/2+10,xia1_Y+xiaBitmap1.getHeight()/2+20, paint);

		//中間圓圈中的刻度(小圓)
		RectF oval = new RectF(zhong1_X+zhongBitmap1.getWidth()/2-10,zhong1_Y+zhongBitmap1.getHeight()/2-10,zhong1_X+zhongBitmap1.getWidth()/2+10,zhong1_Y+zhongBitmap1.getHeight()/2+10);
		
		canvas.drawOval(oval, paint);//繪制基准線(圓)
	}
}

package com.tcjt.level2;

import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

@SuppressWarnings("deprecation")
public class LevelActivity extends Activity {

	int k = 45; //靈敏度
	MainView mv;
	//真機
	SensorManager mySensorManager;

	//測試時
//	SensorManagerSimulator mySensorManager; 

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_level);
		
		mv = (MainView) findViewById(R.id.mainView);
		
		mySensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);//真機
	}
	
	private final SensorListener mSensorLisener =new SensorListener(){

		@Override
		public void onAccuracyChanged(int sensor, int accuracy) { }
		
		public boolean isContain(int x, int y){//判斷點是否在圓內

			int tempx =(int) (x + mv.zhongBitmap2.getWidth()/2.0);
			int tempy =(int) (y + mv.zhongBitmap2.getWidth()/2.0);
			int ox = (int) (mv.zhong1_X+ mv.zhongBitmap1.getWidth()/2.0);
			int oy = (int) (mv.zhong1_X+ mv.zhongBitmap1.getWidth()/2.0);
			if(Math.sqrt((tempx-ox)*(tempx-ox)+(tempy-oy)*(tempy-oy))>(mv.zhongBitmap1.getWidth()/2.0-mv.zhongBitmap2.getWidth()/2.0)){
				//不在圓內
				return false;
			}else{
				//在圓內時
				return true;
			}
		}

		@Override
		public void onSensorChanged(int sensor, float[] values) {
			if(sensor == SensorManager.SENSOR_ORIENTATION){
				double pitch = values[SensorManager.DATA_Y];
				double roll = values[SensorManager.DATA_Z];
				int x=0; int y=0;//臨時變量,算中間水泡坐標時用
				int tempX=0; int tempY=0;//下面氣泡的臨時變量
				
				//開始調整x 的值
				if(Math.abs(roll)<=k){
					mv.shang2_X = mv.shang1_X //上面的
						+ (int)(((mv.shangBitmap1.getWidth()
						-mv.shangBitmap2.getWidth())/2.0)
						-(((mv.shangBitmap1.getWidth()
						-mv.shangBitmap2.getWidth())/2.0)*roll)/k);
					
					x = mv.zhong1_X //中間的
						+ (int)(((mv.zhongBitmap1.getWidth()
						-mv.zhongBitmap2.getWidth())/2.0)
						-(((mv.zhongBitmap1.getWidth()
						-mv.zhongBitmap2.getWidth())/2.0)*roll)/k);
				}else if(roll>k){
					mv.shang2_X=mv.shang1_X; x = mv.zhong1_X;
				}else{
					mv.shang2_X=mv.shang1_X+
						mv.shangBitmap1.getWidth()
						- mv.shangBitmap2.getWidth();
					
					x = mv.zhong1_X+ mv.zhongBitmap1.getWidth()
						- mv.zhongBitmap2.getWidth();
				}
				
				//開始調整y 的值

				if(Math.abs(pitch)<=k){
					mv.zuo2_Y=mv.zuo1_Y //左面的
						+ (int)(((mv.zuoBitmap1.getHeight()
						-mv.zuoBitmap2.getHeight())/2.0)
						+(((mv.zuoBitmap1.getHeight()
						-mv.zuoBitmap2.getHeight())/2.0)*pitch)/k);
	
					y =mv.zhong1_Y+ //中間的
						(int)(((mv.zhongBitmap1.getHeight()
						-mv.zhongBitmap2.getHeight())/2.0)
						+(((mv.zhongBitmap1.getHeight()
						-mv.zhongBitmap2.getHeight())/2.0)*pitch)/k);
				}else if(pitch>k){
					mv.zuo2_Y=mv.zuo1_Y
						+mv.zuoBitmap1.getHeight()
						-mv.zuoBitmap2.getHeight();
					
					y=mv.zhong1_Y+mv.zhongBitmap1.getHeight()
						-mv.zhongBitmap2.getHeight();
				}else{
					mv.zuo2_Y = mv.zuo1_Y; y = mv.zhong1_Y;
				}

				//下面的
				tempX = -(int) (((mv.xiaBitmap1.getWidth()/2-28)*roll
						+(mv.xiaBitmap1.getWidth()/2-28)*pitch)/k);
				
				tempY = -(int) ((-(mv.xiaBitmap1.getWidth()/2-28)*roll
						-(mv.xiaBitmap1.getWidth()/2-28)*pitch)/k);

				//限制下面的氣泡范圍
				if(tempY>mv.xiaBitmap1.getHeight()/2-28){
					tempY = mv.xiaBitmap1.getHeight()/2-28;
				}
				if(tempY < -mv.xiaBitmap1.getHeight()/2+28){
					tempY = -mv.xiaBitmap1.getHeight()/2+28;
				}

				if(tempX > mv.xiaBitmap1.getWidth()/2-28){
					tempX = mv.xiaBitmap1.getWidth()/2-28;
				}
				
				if(tempX < -mv.xiaBitmap1.getWidth()/2+28){
					tempX = -mv.xiaBitmap1.getWidth()/2+28;
				}
				
				mv.xia2_X = tempX + mv.xia1_X + mv.xiaBitmap1.getWidth()/2 -mv.xiaBitmap2.getWidth()/2;
				mv.xia2_Y = tempY + mv.xia1_Y + mv.xiaBitmap1.getHeight()/2 - mv.xiaBitmap2.getWidth()/2;
				
				if(isContain(x, y)){//中間的水泡在圓內才改變坐標
					mv.zhong2_X = x; mv.zhong2_Y = y;
				}
				mv.postInvalidate();//重繪MainView
			}
		} //傳感器監聽器類
		//該處省略了部分代碼,將在後面進行介紹
	};

	@Override
	protected void onResume(){ //添加監聽
		mySensorManager.registerListener(mSensorLisener,SensorManager.SENSOR_ORIENTATION);
		super.onResume();
	}

	@Override
	protected void onPause() { //取消監聽
		mySensorManager.unregisterListener (mSensorLisener);
		super.onPause();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_level, menu);
		return true;
	}

}
兩個例子都能實現水准儀的效果。圖片我就不貼,可以參考裡面的代碼。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved