日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

获取Android设备的方向 -- gsensor

發(fā)布時(shí)間:2023/12/13 综合教程 42 生活家
生活随笔 收集整理的這篇文章主要介紹了 获取Android设备的方向 -- gsensor 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

http://www.cnblogs.com/bpasser/archive/2011/10/17/2214517.html

帶有g(shù)-sensor的Android設(shè)備上可通過(guò)API獲取到設(shè)備的運(yùn)動(dòng)加速度,應(yīng)用程序通過(guò)一些假設(shè)和運(yùn)算,可以從加速度計(jì)算出設(shè)備的方向

獲取設(shè)備運(yùn)動(dòng)加速度的基本代碼是:

        SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        sm.registerListener(new SensorEventListener() {

            public void onSensorChanged(SensorEvent event) {
                if (Sensor.TYPE_ACCELEROMETER != event.sensor.getType()) {
                    return;
                }

                float[] values = event.values;
                float ax = values[0];
                float ay = values[1];
                float az = values[2];

                // TODO Have fun with the acceleration components...
                
            }

            public void onAccuracyChanged(Sensor sensor, int accuracy) {

            }
        }, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);

SendorEventListener 通過(guò) SendorEvent 回調(diào)參數(shù)獲得當(dāng)前設(shè)備在坐標(biāo)系x、y、z軸上的加速度分量。SensorEvent 的 api doc 中定義了這里使用的坐標(biāo)系為:

我暫且稱之為“設(shè)備坐標(biāo)系”吧,設(shè)備坐標(biāo)系是固定于設(shè)備的,與設(shè)備的方向(在世界坐標(biāo)系中的朝向)無(wú)關(guān)

精確地說(shuō),Sensor Event 所提供的加速度數(shù)值,是設(shè)備以地球?yàn)閰⒄瘴锏募铀俣葴p去重力加速度的疊加后的值。我是這樣理解的:當(dāng)以重力加速度g向地面作自由落體運(yùn)動(dòng)時(shí),手機(jī)處于失重狀態(tài),g-sensor以這種狀態(tài)作為加速度的0;而當(dāng)手機(jī)處于靜止?fàn)顟B(tài)(相對(duì)于地面)時(shí),為了抵御自由落體運(yùn)動(dòng)的趨勢(shì),它有一個(gè)反向(向上)的g的加速度。因此,得出一個(gè)結(jié)論:當(dāng)設(shè)備處于靜止或者勻速運(yùn)動(dòng)狀態(tài)時(shí),它有一個(gè)垂直地面向上的g的加速度,這個(gè)g投影到設(shè)備坐標(biāo)系的x、y、z軸上,就是SensorEvent 提供給我們的3個(gè)分量的數(shù)值。在“設(shè)備處于靜止或者勻速運(yùn)動(dòng)狀態(tài)”的假設(shè)的前提下,可以根據(jù)SensorEvent所提供的3個(gè)加速度分量計(jì)算出設(shè)備相對(duì)于地面的方向

前面所提到的“設(shè)備的方向”是一個(gè)含糊的說(shuō)法。這里我們精確地描述設(shè)備方向?yàn)椋阂源怪庇诘孛娴姆较驗(yàn)檎较颍迷O(shè)備坐標(biāo)系x、y、z軸與正方向軸之間的夾角Ax、Ay、Az來(lái)描述設(shè)備的方向,如下圖所示。可以看出,設(shè)備還有一個(gè)自由度,即:繞著正方向軸旋轉(zhuǎn),Ax、Ay、Az不變。但Ax、Ay、Az的約束條件,對(duì)于描述設(shè)備相對(duì)于正方向軸的相對(duì)位置已經(jīng)足夠了。如果需要完全約束設(shè)備相對(duì)于地面的位置,除了正方向軸外,還需要引入另一個(gè)參照軸,例如連接地球南、北極的地軸(如果設(shè)備上有地磁強(qiáng)度Sensor,則可滿足該約束條件)

Ax、Ay、Az的范圍為[0, 2*PI)。例如,當(dāng)Ay=0時(shí),手機(jī)y軸豎直向上;Ay=PI時(shí),手機(jī)y軸向下;Ay=PI/2時(shí),手機(jī)水平、屏幕向上;Ay=3*PI/2時(shí),手機(jī)水平、屏幕向下

根據(jù)3D矢量代數(shù)的法則,可知:

Gx=g*cos(Ax)Gy=g*cos(Ay)Gz=g*cos(Az)g^2=Gz^2+Gy^2+Gz^2

因此,根據(jù)Gx、Gy、Gz,可以計(jì)算出Ax、Ay、Az

在x-y平面上的2D簡(jiǎn)化

當(dāng)Ax、Ay確定時(shí),Az有兩種可能的值,二者相差PI,確定了設(shè)備屏幕的朝向是向上還是向下。大多數(shù)情況下,我們只關(guān)心Ax、Ay(因?yàn)槌绦騏I位于x-y平面?),而忽略Az,例如,Android的屏幕自動(dòng)旋轉(zhuǎn)功能,不管使用者是低著頭看屏幕(屏幕朝上)、還是躺在床上看(屏幕朝下),UI始終是底邊最接近地心的方向

那么我們?cè)O(shè)Gx與Gy的矢量和為g'(即:g在x-y平面上的投影),將計(jì)算簡(jiǎn)化到x-y 2D平面上。記y軸相對(duì)于g'的偏角為A,以A來(lái)描述設(shè)備的方向。以逆時(shí)針?lè)较驗(yàn)檎珹的范圍為[0, 2*PI)

有:

g'^2=Gx^2+Gy^2Gy=g'*cos(A)Gx=g'*sin(A)

則:

g'=sqrt(Gx^2+Gy^2)A=arccos(Gy/g')

由于arccos函數(shù)值范圍為[0, PI];而A>PI時(shí),Gx=g'*sin(A)<0,因此,根據(jù)Gx的符號(hào)分別求A的值為:

當(dāng)Gx>=0時(shí),A=arccos(Gy/g')當(dāng)Gx<0時(shí),A=2*PI-arccos(Gy/g')

注意:由于cos函數(shù)曲線關(guān)于直線x=n*PI 對(duì)稱,因此arccos函數(shù)的曲線如果在y軸方向[0, 2*PI]范圍內(nèi)補(bǔ)全的話,則關(guān)于直線y=PI對(duì)稱,因此有上面當(dāng)Gx<0時(shí)的算法

考慮應(yīng)用程序的屏幕旋轉(zhuǎn)

前面計(jì)算出了Android設(shè)備的“物理屏幕”相對(duì)于地面的旋轉(zhuǎn)角度,而應(yīng)用程序的UI又相對(duì)于“物理屏幕”存在0、90、180、270度4種可能的旋轉(zhuǎn)角度,要綜合考慮進(jìn)來(lái)。也就是說(shuō):

UI相對(duì)于地面的旋轉(zhuǎn)角度=物理屏幕相對(duì)于地面的旋轉(zhuǎn)角度-UI相對(duì)于物理屏幕的旋轉(zhuǎn)角度

Android應(yīng)用獲取屏幕旋轉(zhuǎn)角度的方法為:

        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int degree= 90 * rotation;
        float rad = (float)Math.PI / 2 * rotation;

Demo

根據(jù)上面的算法,我寫了一個(gè)“不倒翁”的Demo,當(dāng)設(shè)備旋轉(zhuǎn)時(shí),不倒翁始終是站立的。軟件市場(chǎng)上不少“水平尺”一類的應(yīng)用,其實(shí)現(xiàn)原理應(yīng)該是與此相同的

下載Demo源代碼

Activity實(shí)現(xiàn)了SensorEventListener,并且注冊(cè)到SensorManager。同時(shí)設(shè)置屏幕方向固定為L(zhǎng)ANDSCAPE:

    private GSensitiveView gsView;
    private SensorManager sm;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        super.onCreate(savedInstanceState);

        gsView = new GSensitiveView(this);
        setContentView(gsView);

        sm = (SensorManager) getSystemService(SENSOR_SERVICE);
        sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onDestroy() {
        sm.unregisterListener(this);
        super.onDestroy();
    }

當(dāng)g-sensor數(shù)據(jù)變化時(shí)的回調(diào)如下。這里就是根據(jù)我們前面推論的算法計(jì)算出UI旋轉(zhuǎn)的角度,并且調(diào)用GSensitiveView.setRotation()方法通知View更新

    public void onSensorChanged(SensorEvent event) {
        if (Sensor.TYPE_ACCELEROMETER != event.sensor.getType()) {
            return;
        }

        float[] values = event.values;
        float ax = values[0];
        float ay = values[1];

        double g = Math.sqrt(ax * ax + ay * ay);
        double cos = ay / g;
        if (cos > 1) {
            cos = 1;
        } else if (cos < -1) {
            cos = -1;
        }
        double rad = Math.acos(cos);
        if (ax < 0) {
            rad = 2 * Math.PI - rad;
        }

        int uiRot = getWindowManager().getDefaultDisplay().getRotation();
        double uiRad = Math.PI / 2 * uiRot;
        rad -= uiRad;

        gsView.setRotation(rad);
    }

GSensitiveView是擴(kuò)展ImageView的自定義類,主要是根據(jù)旋轉(zhuǎn)角度繪制圖片:

    private static class GSensitiveView extends ImageView {

        private Bitmap image;
        private double rotation;
        private Paint paint;

        public GSensitiveView(Context context) {
            super(context);
            BitmapDrawable drawble = (BitmapDrawable) context.getResources().getDrawable(R.drawable.budaow);
            image = drawble.getBitmap();

            paint = new Paint();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // super.onDraw(canvas);

            double w = image.getWidth();
            double h = image.getHeight();

            Rect rect = new Rect();
            getDrawingRect(rect);

            int degrees = (int) (180 * rotation / Math.PI);
            canvas.rotate(degrees, rect.width() / 2, rect.height() / 2);
            canvas.drawBitmap(image, //
                    (float) ((rect.width() - w) / 2),//  
                    (float) ((rect.height() - h) / 2),//  
                    paint);
        }

        public void setRotation(double rad) {
            rotation = rad;
            invalidate();
        }

    }

總結(jié)

以上是生活随笔為你收集整理的获取Android设备的方向 -- gsensor的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。