關(guān)于A(yíng)pp全屏的一二事

寫(xiě)在前面的幾句話(huà)

<p>
其實(shí)大家在開(kāi)發(fā)過(guò)程中,很少會(huì)接觸到從豎屏到全屏的過(guò)程,只有在關(guān)于視頻播放軟件相關(guān)才會(huì)設(shè)計(jì)到這個(gè)方面的知識(shí),剛好這次的開(kāi)發(fā)中就遇到這樣的問(wèn)題了,那么就把這次遇到的針對(duì)關(guān)于全屏相關(guān)的東西記錄下來(lái)。

一.簡(jiǎn)單實(shí)現(xiàn)橫豎屏切換效果

<p>
其實(shí)這個(gè)很簡(jiǎn)單,只要打開(kāi)手機(jī)的屏幕旋轉(zhuǎn)功能就可以了,那么當(dāng)手機(jī)旋轉(zhuǎn)過(guò)來(lái)的時(shí)候,界面也會(huì)自動(dòng)旋轉(zhuǎn)過(guò)來(lái)的。

當(dāng)然一般我們的開(kāi)發(fā)都會(huì)屏蔽掉旋轉(zhuǎn),設(shè)置為單一的豎屏方向,設(shè)置如下

  <activity
            android:name=".TestActivity"
            android:screenOrientation="portrait" />

這樣的橫豎屏其實(shí)可以滿(mǎn)足部分的需求了,但是其實(shí)橫豎屏相互旋轉(zhuǎn)都是Activity重新的創(chuàng)建,這樣在某些應(yīng)用場(chǎng)景下就不滿(mǎn)足需求了,比如當(dāng)視頻播放軟件豎屏播放切換到橫屏?xí)r,如果重新創(chuàng)建的話(huà),那么視頻自然就不能夠持續(xù)播放了,這樣自然就不能滿(mǎn)足需求了,

如何實(shí)現(xiàn)旋轉(zhuǎn)過(guò)程中不重新創(chuàng)建Activity呢?

configChanges

在A(yíng)ndroidManifest.xml中設(shè)置這個(gè)Activity的configChanges參數(shù)就可以了

如下

<activity
            android:name=".TestActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            />

添加這句的作用是,當(dāng)Activity發(fā)生keyboardHidden(虛擬鍵盤(pán)隱藏),orientation(屏幕方向變化),screenSize(屏幕大小改變)這些變化的時(shí)候不會(huì)重新創(chuàng)建Activity,但是會(huì)在onConfigurationChanged方法中檢測(cè)到響應(yīng)的變化,通過(guò)這個(gè)方法才實(shí)現(xiàn)我們響應(yīng)需要實(shí)現(xiàn)的邏輯

方法如下

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
 }

那么我們結(jié)合下一個(gè)小的事例來(lái)講解實(shí)現(xiàn)

圖1 豎屏界面
圖2 橫屏界面

如圖所示,豎屏下上半部分為視頻播放區(qū)域,下半部分則是相關(guān)的控制或者其他顯示區(qū)域,而橫屏下則全屏為視頻播放區(qū)域,那么我們?nèi)绾瓮ㄟ^(guò)onConfigurationChanged來(lái)實(shí)現(xiàn)這樣的功能呢?

首先通過(guò)onConfigurationChanged來(lái)判斷橫豎屏的轉(zhuǎn)換

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
       if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
      // TODO: 16/6/14 橫屏相關(guān)操作
       } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
      // TODO: 16/6/14 豎屏相關(guān)操作
      }
 }

接下來(lái)就是處理橫豎屏幕響應(yīng)的方法了

如果不做處理呢?我們可以看到旋轉(zhuǎn)屏幕后會(huì)是這樣的效果

圖3 不做處理的橫屏界面

但是這樣明顯就和我們最開(kāi)始給出的橫屏界面不一致

所以為了實(shí)現(xiàn)這種效果我們分析一下,橫屏的時(shí)候視頻播放區(qū)域覆蓋了整個(gè)屏幕,而控制顯示區(qū)域則消失了,所以其實(shí)很好理解的是當(dāng)橫屏的時(shí)候?qū)⒖刂茀^(qū)域設(shè)置為GONE,而將視頻播放區(qū)域設(shè)置為match_parent即可以,這樣其實(shí)視頻播放區(qū)域則可以自動(dòng)拉升到充滿(mǎn)整個(gè)屏幕,當(dāng)豎屏的時(shí)候則將控制區(qū)域設(shè)置為VISIBLE,并將視頻播放區(qū)域動(dòng)態(tài)設(shè)置為之前的大小。

所以現(xiàn)在來(lái)看下onConfigurationChanged中的方法

public void onConfigurationChanged(Configuration newConfig) {
   super.onConfigurationChanged(newConfig);
   if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        //動(dòng)態(tài)設(shè)置視頻播放區(qū)域的為整個(gè)屏幕區(qū)域
      DisplayMetrics dm = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(dm);
        LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) mViedioLayout.getLayoutParams();
        linearParams.height = dm.heightPixels;
        linearParams.width = dm.widthPixels;
        linearParams.setMargins(0, 0, 0, 0);
        mViedioLayout.setLayoutParams(linearParams);

        mControlLayout.setVisibility(View.GONE);


   } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        //動(dòng)態(tài)設(shè)置視頻播放區(qū)域?yàn)橹暗拇笮?      LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) mViedioLayout.getLayoutParams();
        linearParams.height = (int) layoutheight;
        linearParams.width = (int) layoutwidth;
        mViedioLayout.setLayoutParams(linearParams);

        mControlLayout.setVisibility(View.VISIBLE);
  }
 }

通過(guò)這樣就實(shí)現(xiàn)所要求的結(jié)果,是不是很簡(jiǎn)單?

當(dāng)然等等,還有新的需求,一般情況下視頻播放軟件是存在切換橫豎屏的按鈕的,點(diǎn)擊則去旋轉(zhuǎn)屏幕為橫屏還是豎屏

怎么去做這個(gè)呢?查一下就知道有強(qiáng)制設(shè)置屏幕的方法了,如下

//設(shè)置為豎屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//設(shè)置為橫屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

當(dāng)點(diǎn)擊后使用上面的方法,屏幕則會(huì)旋轉(zhuǎn)過(guò)來(lái),而且在onConfigurationChanged中也會(huì)監(jiān)聽(tīng)到屏幕的橫豎方向轉(zhuǎn)變,一切看起來(lái)如此完美

等等,怎么強(qiáng)制設(shè)置屏幕后,重力感應(yīng)就沒(méi)有了,wtf?這個(gè)是什么鬼?為什么會(huì)這樣?

沒(méi)辦法只能想想通過(guò)別的方式來(lái)實(shí)現(xiàn)重力感應(yīng)的效果了

二 .利用傳感器服務(wù)實(shí)現(xiàn)橫豎屏切換效果

<p>
首先我們需要知道下傳感器,在A(yíng)ndroid設(shè)備中一般內(nèi)置了很多的傳感器,其中就包含重力感應(yīng)傳感器,雖然橫豎屏切換與重力感應(yīng)貌似有關(guān),其實(shí)他們是兩回事。橫豎屏在重力感應(yīng)中只是最粗略的說(shuō)法了,你把手機(jī)平放著不動(dòng),重力感應(yīng)都是時(shí)時(shí)刻刻發(fā)生著的,因?yàn)榘沧吭O(shè)備能感應(yīng)到很細(xì)微的震動(dòng)。

Android中檢測(cè)重力感應(yīng)變化大致需要下面幾個(gè)步驟:

    1. 得到傳感器服務(wù) getSystemService(SENSOR_SERVICE);

得到一個(gè)SensorManager,用來(lái)管理分配調(diào)度處理Sensor的工作,注意它并不服務(wù)運(yùn)行于后臺(tái),真正屬于Sensor的系統(tǒng)服務(wù)是SensorService,終端下#service list可以看到sensorservice: [android.gui.SensorServer]。

    1. 得到傳感器類(lèi)型 getDefaultSensor(Sensor.TYPE_GRAVITY);

當(dāng)然還有各種千奇百怪的傳感器,可以查閱Android官網(wǎng)API或者源碼Sensor.java。

    1. 注冊(cè)監(jiān)聽(tīng)器 SensorEventListener

應(yīng)用程序打開(kāi)一個(gè)監(jiān)聽(tīng)接口,專(zhuān)門(mén)處理傳感器的數(shù)據(jù),這個(gè)監(jiān)聽(tīng)機(jī)制比較重要,被系統(tǒng)廣泛使用。

    1. 實(shí)現(xiàn)監(jiān)聽(tīng)器的回調(diào)函數(shù) onSensorChanged, onAccuracyChanged

很多移動(dòng)設(shè)備都內(nèi)置了感應(yīng)器,android通過(guò)Sensor和SensorManager類(lèi)抽象了這些感應(yīng)器,通過(guò)這些類(lèi)可以使用android設(shè)備的傳感器

所以我們通過(guò)onSensorChanged中的位置來(lái)判斷手機(jī)的方向,通過(guò)這個(gè)方向來(lái)設(shè)置手機(jī)的橫豎屏幕即可

ok,上代碼如下


//注冊(cè)重力感應(yīng)器  屏幕旋轉(zhuǎn)
SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
OrientationSensorListener listener = new OrientationSensorListener(handler);
sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);

private Handler handler = new Handler(){
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 888:
                int orientation = msg.arg1;
                if (orientation>45&&orientation<135) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                  
                }else if (orientation>135&&orientation<225){

                }else if (orientation>225&&orientation<315){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                   
                }else if ((orientation>315&&orientation<360)||(orientation>0&&orientation<45)){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                  
                }
                break;
            default:
                break;
        }

    };
};


/**
 * 重力感應(yīng)監(jiān)聽(tīng)者
 */
public class OrientationSensorListener implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    public static final int ORIENTATION_UNKNOWN = -1;

    private Handler rotateHandler;

    public OrientationSensorListener(Handler handler) {
        rotateHandler = handler;
    }

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

    }

    public void onSensorChanged(SensorEvent event) {

        if(sensor_flag!=stretch_flag)  //只有兩個(gè)不相同才開(kāi)始監(jiān)聽(tīng)行為
        {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X*X + Y*Y;
            // Don't trust the angle if the magnitude is small compared to the y value
            if (magnitude * 4 >= Z*Z) {
                //屏幕旋轉(zhuǎn)時(shí)
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - (int)Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            System.out.println("orientation-->" + orientation);
            if (rotateHandler!=null) {
                rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();
            }

        }
    }
}

所以這里其實(shí)的邏輯就是檢測(cè)重力感應(yīng)傳感器的變化,通過(guò)這個(gè)變化來(lái)設(shè)置屏幕的方向就可以了

看起來(lái)很完美的樣子,實(shí)際運(yùn)行就會(huì)發(fā)現(xiàn)問(wèn)題了,當(dāng)我們點(diǎn)擊手動(dòng)控制橫豎屏幕的按鈕后,發(fā)現(xiàn)又不起作用了,但是其實(shí)是有一個(gè)閃屏的效果,為什么會(huì)這樣呢?

其實(shí)想想也可以理解,雖然我們強(qiáng)制設(shè)了橫屏屏幕的方向,但是這個(gè)時(shí)候重力感應(yīng)傳感器是一直在監(jiān)聽(tīng)手機(jī)的變化的啊,所以這時(shí)候變化就再次取強(qiáng)制設(shè)置屏幕方向,方向又被設(shè)為了豎屏,所以看起來(lái)就沒(méi)有任何變化,但實(shí)際上這中間有這個(gè)設(shè)置的過(guò)程,既然知道是什么原因造成的了,那么就應(yīng)該去解決這樣的問(wèn)題了

既然我們注冊(cè)的重力感應(yīng)監(jiān)聽(tīng)的會(huì)對(duì)我們操作有影響,那么在按下的時(shí)候把重力感應(yīng)監(jiān)聽(tīng)的注冊(cè)取消掉就好了,等后面在注冊(cè)上就可以即對(duì)操作不影響,也在后面有了重力感應(yīng)了啊,完美?。。?!

等等,什么時(shí)候加上呢?問(wèn)題來(lái)了,omg,無(wú)解,救命

沒(méi)有什么事情是一個(gè)重力感應(yīng)監(jiān)聽(tīng)做不了,如果有,那么就用兩個(gè)重力感應(yīng)的監(jiān)聽(tīng)

所以我們注冊(cè)兩個(gè)重力感應(yīng)的監(jiān)聽(tīng),一個(gè)重力感應(yīng)負(fù)責(zé)旋轉(zhuǎn)屏幕方向,另一個(gè)負(fù)責(zé)動(dòng)態(tài)去注冊(cè)上面的那個(gè)監(jiān)聽(tīng),這樣就解決了什么時(shí)候重力感應(yīng)監(jiān)聽(tīng)注冊(cè)的問(wèn)題了,

二話(huà)不說(shuō),直接上代碼

//注冊(cè)重力感應(yīng)器  屏幕旋轉(zhuǎn)
SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
OrientationSensorListener listener = new OrientationSensorListener(handler);
sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);


//根據(jù)  旋轉(zhuǎn)之后 點(diǎn)擊 符合之后 激活sm
SensorManager sm1 = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor sensor1 = sm1.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
listener1 = new OrientationSensorListener2();
sm1.registerListener(listener1, sensor1, SensorManager.SENSOR_DELAY_UI);


private Handler handler = new Handler(){
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 888:
                int orientation = msg.arg1;
                if (orientation>45&&orientation<135) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                    sensor_flag = false;
                    stretch_flag=false;
                }else if (orientation>135&&orientation<225){

                }else if (orientation>225&&orientation<315){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    sensor_flag = false;
                    stretch_flag=false;

                }else if ((orientation>315&&orientation<360)||(orientation>0&&orientation<45)){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    sensor_flag = true;
                    stretch_flag=true;

                }
                break;
            default:
                break;
        }

    };
};


/**
 * 重力感應(yīng)監(jiān)聽(tīng)者
 */
public class OrientationSensorListener implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    public static final int ORIENTATION_UNKNOWN = -1;

    private Handler rotateHandler;

    public OrientationSensorListener(Handler handler) {
        rotateHandler = handler;
    }

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

    }

    public void onSensorChanged(SensorEvent event) {

        if(sensor_flag!=stretch_flag)  //只有兩個(gè)不相同才開(kāi)始監(jiān)聽(tīng)行為
        {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X*X + Y*Y;
            // Don't trust the angle if the magnitude is small compared to the y value
            if (magnitude * 4 >= Z*Z) {
                //屏幕旋轉(zhuǎn)時(shí)
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - (int)Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            System.out.println("orientation-->" + orientation);
            if (rotateHandler!=null) {
                rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();
            }

        }
    }
}


public class OrientationSensorListener2 implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    public static final int ORIENTATION_UNKNOWN = -1;

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

    }

    public void onSensorChanged(SensorEvent event) {

        float[] values = event.values;

        int orientation = ORIENTATION_UNKNOWN;
        float X = -values[_DATA_X];
        float Y = -values[_DATA_Y];
        float Z = -values[_DATA_Z];

        /**
         * 這一段據(jù)說(shuō)是 android源碼里面拿出來(lái)的計(jì)算 屏幕旋轉(zhuǎn)的 不懂 先留著 萬(wàn)一以后懂了呢
         */
        float magnitude = X*X + Y*Y;
        // Don't trust the angle if the magnitude is small compared to the y value
        if (magnitude * 4 >= Z*Z) {
            //屏幕旋轉(zhuǎn)時(shí)
            float OneEightyOverPi = 57.29577957855f;
            float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
            orientation = 90 - (int)Math.round(angle);
            // normalize to 0 - 359 range
            while (orientation >= 360) {
                orientation -= 360;
            }
            while (orientation < 0) {
                orientation += 360;
            }
        }


        if (orientation>45&&orientation<135) { //橫屏
            sensor_flag = false;
        }else if (orientation>225&&orientation<315){  //橫屏
            sensor_flag = false;
        }else if ((orientation>315&&orientation<360)||(orientation>0&&orientation<45)){  //豎屏
            sensor_flag = true;
        }



        if(stretch_flag == sensor_flag){  //點(diǎn)擊變成橫屏  屏幕 也轉(zhuǎn)橫屏 激活
            sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
        }
    }
}   

ok,到這里就基本實(shí)現(xiàn)了所需求的功能了,其實(shí)全屏相關(guān)的內(nèi)容應(yīng)該大部分都已經(jīng)涉及到了,相信后面如果有別的全屏需求,大家也應(yīng)該可以以不變應(yīng)萬(wàn)變了,

最后記得在pause的時(shí)候取消兩個(gè)sensor的監(jiān)聽(tīng),因?yàn)檎娴暮芎碾姷?/p>

寫(xiě)在后面的幾句話(huà)

<p>
我的愿望是世界和平!!!!!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容