寫在前面的幾句話
<p>
其實(shí)大家在開發(fā)過程中般堆,很少會(huì)接觸到從豎屏到全屏的過程,只有在關(guān)于視頻播放軟件相關(guān)才會(huì)設(shè)計(jì)到這個(gè)方面的知識(shí)炸宵,剛好這次的開發(fā)中就遇到這樣的問題了俭正,那么就把這次遇到的針對(duì)關(guān)于全屏相關(guān)的東西記錄下來。
一.簡(jiǎn)單實(shí)現(xiàn)橫豎屏切換效果
<p>
其實(shí)這個(gè)很簡(jiǎn)單竿开,只要打開手機(jī)的屏幕旋轉(zhuǎn)功能就可以了谱仪,那么當(dāng)手機(jī)旋轉(zhuǎn)過來的時(shí)候,界面也會(huì)自動(dòng)旋轉(zhuǎn)過來的德迹。
當(dāng)然一般我們的開發(fā)都會(huì)屏蔽掉旋轉(zhuǎn)芽卿,設(shè)置為單一的豎屏方向,設(shè)置如下
<activity
android:name=".TestActivity"
android:screenOrientation="portrait" />
這樣的橫豎屏其實(shí)可以滿足部分的需求了胳搞,但是其實(shí)橫豎屏相互旋轉(zhuǎn)都是Activity重新的創(chuàng)建卸例,這樣在某些應(yīng)用場(chǎng)景下就不滿足需求了称杨,比如當(dāng)視頻播放軟件豎屏播放切換到橫屏?xí)r,如果重新創(chuàng)建的話筷转,那么視頻自然就不能夠持續(xù)播放了姑原,這樣自然就不能滿足需求了,
如何實(shí)現(xiàn)旋轉(zhuǎn)過程中不重新創(chuàng)建Activity呢呜舒?
configChanges
在AndroidManifest.xml中設(shè)置這個(gè)Activity的configChanges參數(shù)就可以了
如下
<activity
android:name=".TestActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
/>
添加這句的作用是锭汛,當(dāng)Activity發(fā)生keyboardHidden(虛擬鍵盤隱藏),orientation(屏幕方向變化),screenSize(屏幕大小改變)這些變化的時(shí)候不會(huì)重新創(chuàng)建Activity,但是會(huì)在onConfigurationChanged方法中檢測(cè)到響應(yīng)的變化,通過這個(gè)方法才實(shí)現(xiàn)我們響應(yīng)需要實(shí)現(xiàn)的邏輯
方法如下
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
那么我們結(jié)合下一個(gè)小的事例來講解實(shí)現(xiàn)
如圖所示袭蝗,豎屏下上半部分為視頻播放區(qū)域唤殴,下半部分則是相關(guān)的控制或者其他顯示區(qū)域,而橫屏下則全屏為視頻播放區(qū)域到腥,那么我們?nèi)绾瓮ㄟ^onConfigurationChanged來實(shí)現(xiàn)這樣的功能呢朵逝?
首先通過onConfigurationChanged來判斷橫豎屏的轉(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)操作
}
}
接下來就是處理橫豎屏幕響應(yīng)的方法了
如果不做處理呢?我們可以看到旋轉(zhuǎn)屏幕后會(huì)是這樣的效果
但是這樣明顯就和我們最開始給出的橫屏界面不一致
所以為了實(shí)現(xiàn)這種效果我們分析一下乡范,橫屏的時(shí)候視頻播放區(qū)域覆蓋了整個(gè)屏幕配名,而控制顯示區(qū)域則消失了,所以其實(shí)很好理解的是當(dāng)橫屏的時(shí)候?qū)⒖刂茀^(qū)域設(shè)置為GONE晋辆,而將視頻播放區(qū)域設(shè)置為match_parent即可以渠脉,這樣其實(shí)視頻播放區(qū)域則可以自動(dòng)拉升到充滿整個(gè)屏幕,當(dāng)豎屏的時(shí)候則將控制區(qū)域設(shè)置為VISIBLE,并將視頻播放區(qū)域動(dòng)態(tài)設(shè)置為之前的大小瓶佳。
所以現(xiàn)在來看下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);
}
}
通過這樣就實(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)過來器仗,而且在onConfigurationChanged中也會(huì)監(jiān)聽到屏幕的橫豎方向轉(zhuǎn)變,一切看起來如此完美
等等童番,怎么強(qiáng)制設(shè)置屏幕后精钮,重力感應(yīng)就沒有了,wtf剃斧?這個(gè)是什么鬼轨香?為什么會(huì)這樣?
沒辦法只能想想通過別的方式來實(shí)現(xiàn)重力感應(yīng)的效果了
二 .利用傳感器服務(wù)實(shí)現(xiàn)橫豎屏切換效果
<p>
首先我們需要知道下傳感器幼东,在Android設(shè)備中一般內(nèi)置了很多的傳感器,其中就包含重力感應(yīng)傳感器瓦胎,雖然橫豎屏切換與重力感應(yīng)貌似有關(guān)矾利,其實(shí)他們是兩回事。橫豎屏在重力感應(yīng)中只是最粗略的說法了糟秘,你把手機(jī)平放著不動(dòng),重力感應(yīng)都是時(shí)時(shí)刻刻發(fā)生著的球散,因?yàn)榘沧吭O(shè)備能感應(yīng)到很細(xì)微的震動(dòng)尿赚。
Android中檢測(cè)重力感應(yīng)變化大致需要下面幾個(gè)步驟:
- 得到傳感器服務(wù) getSystemService(SENSOR_SERVICE);
得到一個(gè)SensorManager,用來管理分配調(diào)度處理Sensor的工作蕉堰,注意它并不服務(wù)運(yùn)行于后臺(tái)凌净,真正屬于Sensor的系統(tǒng)服務(wù)是SensorService,終端下#service list可以看到sensorservice: [android.gui.SensorServer]屋讶。
- 得到傳感器類型 getDefaultSensor(Sensor.TYPE_GRAVITY);
當(dāng)然還有各種千奇百怪的傳感器泻蚊,可以查閱Android官網(wǎng)API或者源碼Sensor.java。
- 注冊(cè)監(jiān)聽器 SensorEventListener
應(yīng)用程序打開一個(gè)監(jiān)聽接口丑婿,專門處理傳感器的數(shù)據(jù)性雄,這個(gè)監(jiān)聽機(jī)制比較重要,被系統(tǒng)廣泛使用羹奉。
- 實(shí)現(xiàn)監(jiān)聽器的回調(diào)函數(shù) onSensorChanged, onAccuracyChanged
很多移動(dòng)設(shè)備都內(nèi)置了感應(yīng)器秒旋,android通過Sensor和SensorManager類抽象了這些感應(yīng)器,通過這些類可以使用android設(shè)備的傳感器
所以我們通過onSensorChanged中的位置來判斷手機(jī)的方向诀拭,通過這個(gè)方向來設(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)聽者
*/
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è)不相同才開始監(jiān)聽行為
{
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)傳感器的變化,通過這個(gè)變化來設(shè)置屏幕的方向就可以了
看起來很完美的樣子耕挨,實(shí)際運(yùn)行就會(huì)發(fā)現(xiàn)問題了细卧,當(dāng)我們點(diǎn)擊手動(dòng)控制橫豎屏幕的按鈕后,發(fā)現(xiàn)又不起作用了筒占,但是其實(shí)是有一個(gè)閃屏的效果贪庙,為什么會(huì)這樣呢?
其實(shí)想想也可以理解翰苫,雖然我們強(qiáng)制設(shè)了橫屏屏幕的方向止邮,但是這個(gè)時(shí)候重力感應(yīng)傳感器是一直在監(jiān)聽手機(jī)的變化的啊,所以這時(shí)候變化就再次取強(qiáng)制設(shè)置屏幕方向奏窑,方向又被設(shè)為了豎屏导披,所以看起來就沒有任何變化,但實(shí)際上這中間有這個(gè)設(shè)置的過程埃唯,既然知道是什么原因造成的了撩匕,那么就應(yīng)該去解決這樣的問題了
既然我們注冊(cè)的重力感應(yīng)監(jiān)聽的會(huì)對(duì)我們操作有影響,那么在按下的時(shí)候把重力感應(yīng)監(jiān)聽的注冊(cè)取消掉就好了墨叛,等后面在注冊(cè)上就可以即對(duì)操作不影響止毕,也在后面有了重力感應(yīng)了啊模蜡,完美!W壹肌A埂!
等等令漂,什么時(shí)候加上呢膝昆?問題來了,omg叠必,無解荚孵,救命
沒有什么事情是一個(gè)重力感應(yīng)監(jiān)聽做不了,如果有纬朝,那么就用兩個(gè)重力感應(yīng)的監(jiān)聽
所以我們注冊(cè)兩個(gè)重力感應(yīng)的監(jiān)聽收叶,一個(gè)重力感應(yīng)負(fù)責(zé)旋轉(zhuǎn)屏幕方向,另一個(gè)負(fù)責(zé)動(dòng)態(tài)去注冊(cè)上面的那個(gè)監(jiān)聽共苛,這樣就解決了什么時(shí)候重力感應(yīng)監(jiān)聽注冊(cè)的問題了判没,
二話不說,直接上代碼
//注冊(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)聽者
*/
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è)不相同才開始監(jiān)聽行為
{
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ù)說是 android源碼里面拿出來的計(jì)算 屏幕旋轉(zhuǎ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)萬變了俏竞,
最后記得在pause的時(shí)候取消兩個(gè)sensor的監(jiān)聽,因?yàn)檎娴暮芎碾姷?/p>
寫在后面的幾句話
<p>
我的愿望是世界和平!!!!!!!