พอดีเจ้าของบล็อกต้องเขียนแอปพลิเคชันที่รองรับการหมุนจอ แต่ทีนี้บนแอนดรอยด์นั้นมีปัญหาเรื่อง Fragment อย่างหนึ่ง คือเรื่องความต่างระหว่าง Phone, Tablet 7" และ Tablet 10" ก็เลยเขียนคลาสสำหรับจัดการกับเรื่องการหมุนหน้าจอเล่นๆ
ก่อนอื่นคงต้องขอพูดเรื่อง Orientation ของอุปกรณ์แอนดรอยด์กันก่อน
Orientation หรือทิศทางของตัวเครื่อง เพราะว่า Smart Device เหล่านี้ สามารถใช้งานโดยหมุนทิศทางของจอได้ตามการใช้งาน ดังนั้นจึงต้องมีการรู้ว่า Orientation ของเครื่อง ณ ตอนนั้นอยู่ทิศทางใด
package app.akexorcist.orientationmanager;
import java.lang.reflect.Method;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.WindowManager;
public class OrientationManager {
public final static int PORTRAIT_NORMAL = 0;
public final static int PORTRAIT_REVERSE = 1;
public final static int LANDSCAPE_NORMAL = 2;
public final static int LANDSCAPE_REVERSE = 3;
Context context;
Activity activity;
String device_orientation = "";
private OrientationEventListener mOrientationEventListener;
private OrientationManagerListener mOrientationManagerListener;
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
public OrientationManager(Activity activity) {
this.activity = activity;
this.context = activity.getApplicationContext();
int xres = 0, yres = 0;
Method mGetRawH;
Display display = activity.getWindowManager().getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
try {
mGetRawH = Display.class.getMethod("getRawHeight");
Method mGetRawW = Display.class.getMethod("getRawWidth");
xres = (Integer) mGetRawW.invoke(display);
yres = (Integer) mGetRawH.invoke(display);
} catch (Exception e) {
xres = display.getWidth();
yres = display.getHeight();
}
} else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getRealMetrics(outMetrics);
xres = outMetrics.widthPixels;
yres = outMetrics.heightPixels;
}
int hdp = (int)(yres * (1f / dm.density));
int wdp = (int)(xres * (1f / dm.density));
int sw = (hdp < wdp) ? hdp : wdp;
device_orientation = (sw >= 720) ? "landscape" : "portrait";
mOrientationEventListener = new OrientationEventListener(context
, SensorManager.SENSOR_DELAY_NORMAL){
int orientation = -1;
public void onOrientationChanged(int arg0) {
if(orientation == -1) {
orientation = getOrientation();
} else {
if(orientation != getOrientation()) {
if((orientation == PORTRAIT_NORMAL
&& getOrientation() == PORTRAIT_REVERSE)
|| (orientation == PORTRAIT_REVERSE
&& getOrientation() == PORTRAIT_NORMAL)
|| (orientation == LANDSCAPE_NORMAL
&& getOrientation() == LANDSCAPE_REVERSE)
|| (orientation == LANDSCAPE_REVERSE
&& getOrientation() == LANDSCAPE_NORMAL)) {
mOrientationManagerListener.onMirrorRotatation(
getOrientation());
mOrientationManagerListener.onOrientationChanged(
getOrientation(), true);
} else {
mOrientationManagerListener.onOrientationChanged(
getOrientation(), false);
}
orientation = getOrientation();
}
}
}
};
}
public void setOnOrientationListener (OrientationManagerListener listener) {
mOrientationManagerListener = listener;
}
public void enable() {
mOrientationEventListener.enable();
}
public void disable() {
mOrientationEventListener.disable();
}
@SuppressWarnings("static-access")
public int getOrientation() {
WindowManager wm = (WindowManager)context.getSystemService(
context.WINDOW_SERVICE);
int rotation = wm.getDefaultDisplay().getRotation();
if(device_orientation.equals("portrait")) {
if(rotation == Surface.ROTATION_0) {
return PORTRAIT_NORMAL;
} else if(rotation == Surface.ROTATION_90) {
return LANDSCAPE_NORMAL;
} else if(rotation == Surface.ROTATION_180) {
return PORTRAIT_REVERSE;
} else if(rotation == Surface.ROTATION_270) {
return LANDSCAPE_REVERSE;
}
} else if(device_orientation.equals("landscape")) {
if(rotation == Surface.ROTATION_0) {
return LANDSCAPE_NORMAL;
} else if(rotation == Surface.ROTATION_90) {
return PORTRAIT_REVERSE;
} else if(rotation == Surface.ROTATION_180) {
return LANDSCAPE_REVERSE;
} else if(rotation == Surface.ROTATION_270) {
return PORTRAIT_NORMAL;
}
}
return -1;
}
public void disableRotation() {
disable();
int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.FROYO){
SCREEN_ORIENTATION_REVERSE_LANDSCAPE =
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
SCREEN_ORIENTATION_REVERSE_PORTRAIT =
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
}
int rotation = getOrientation();
switch(rotation) {
case OrientationManager.PORTRAIT_NORMAL:
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
break;
case OrientationManager.PORTRAIT_REVERSE:
activity.setRequestedOrientation(
SCREEN_ORIENTATION_REVERSE_PORTRAIT);
break;
case OrientationManager.LANDSCAPE_NORMAL:
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
case OrientationManager.LANDSCAPE_REVERSE:
activity.setRequestedOrientation(
SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
break;
}
}
public void enableRotation() {
enable();
activity.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
public interface OrientationManagerListener{
public void onOrientationChanged(int orientation, boolean isMirror);
public void onMirrorRotatation(int orientation);
}
}
ไม่ต้องไปซีเรียสมากกับโค๊ดอันนี้นะ เจ้าของบล็อกเขียนขึ้นมาให้ใช้งานเฉยๆ
สำหรับการเรียกใช้งานคลาส OrientationManager จะเป็นดังนี้
Orientation om = new OrientationManager(ActiviyName.this);
สมมติว่าเจ้าของบล็อกตั้งชื่อออบเจ็คว่า om ละกันนะ ให้ดูตรงที่ ActivityName ให้ใส่ชื่อ Activity ที่เรียกใช้คลาสนี้ เช่น เจ้าของบล็อกเรียกใช้คลาสนี้ใน Main.java ก็จะเป็น
Orientation om = new OrientationManager(Main.this);
โดยจะมีคำสั่งทั้งหมดดังนี้
om.getOrientation();
om.enable();
om.disable();
om.enableRotation();
om.disableRotation();
om.setOnOrientationListener(Listener);
เป็นคำสั่งสำหรับรับค่าทิศทางของตัวเครื่องนั้น โดยค่าที่ Return กลับมาจะเป็นค่า Integer แทนทิศทางดังนี้
public final static int PORTRAIT_NORMAL = 0;
public final static int PORTRAIT_REVERSE = 1;
public final static int LANDSCAPE_NORMAL = 2;
public final static int LANDSCAPE_REVERSE = 3;
เริ่มทำการเช็คทิศทางของเครื่องเพื่อตรวจจับการเปลี่ยนแปลงใช้ในกรณีที่เรียกใช้ OrientationManagerListener เท่านั้น โดยเรียกใช้คำสั่งนี้ใน onResume
หยุดทำการเช็คทิศทางของเครื่องเพื่อตรวจจับการเปลี่ยนแปลงใช้ในกรณีที่เรียกใช้ OrientationManagerListener เท่านั้น โดยเรียกใช้คำสั่งนี้ใน onPause
กำหนดให้ Activity นั้นๆสามารถหมุนทิศทางจอได้
กำหนดให้ Activity นั้นๆไม่สามารถหมุนทิศทางจอได้
สร้าง Listener สำหรับตรวจจับเมื่อเครื่องมีการเปลี่ยนแปลงทิศทางจอ โดยมีฟังก์ชันสองฟังก์ชันคือ onOrientationChanged กับ onMirrorRotation
om.setOnOrientationListener(new OrientationManagerListener() {
public void onOrientationChanged(int orientation, boolean isMirror) {
// เมื่อมีการหมุนหน้าจอเปลี่ยนไปในทิศทางใดๆ
}
public void onMirrorRotatation(int orientation) {
// เมื่อมีการหมุนหน้าจอในทิศทางตรงกันข้าม
}
});
public void onOrientationChanged(int orientation, boolean isMirror) {
// orientation : ทิศทางของหน้าจอ (ค่าเดียวกับคำสั่ง getOrientation)
// isMirror : เป็นการหมุนแบบทิศทางตรงกันข้ามหรือไม่ (true ใช่, false ไม่ใช่)
}
ส่วน onMirrorRotation เอาไว้สำหรับกรณีที่หมุนในทิศทางตรงกันข้ามโดยเฉพาะ
public void onMirrorRotatation(int orientation) {
// orientation : ทิศทางของหน้าจอ (ค่าเดียวกับคำสั่ง getOrientation)
}
จะรู้ได้ไงว่าค่าทิศทางจากตัวแปร orientation เป็นทิศทางไหน ก็ใช้ IF หรือ Switch-Case ในการเช็คว่าค่าตรงกับทิศทางไหน
เท่านี้ก็รู้ได้แล้วว่าเป็นทิศทางใดอยู่
ทีนี้มาดูตัวอย่างการใช้งานกันเลยดีกว่า โดยเจ้าของบล็อกจะให้แสดงบนหน้าจอว่า ตอนนี้เครื่องกำลังอยู่ในทิศทางแบบใดอยู่ โดยแสดงบอกว่า Text View ที่อยู่กลางหน้าจอและมุมขวาบนจะมีปุ่ม Toggle Button เอาไว้เปิด-ปิดการล็อคหน้าจอ ถ้า On จะเป็นการล็อค
3. ประกาศและกำหนดค่าให้กับ Toggle Button และเรียกใช้ Listener แบบ onCheckChangedListener ที่จะทำงานเมื่อ Toggle Button เปลี่ยนจากสถานะ On เป็น Off หรือจาก Off เป็น On ถ้าสถานะเป็น On ให้ล็อคการหมุนจอด้วยคำสั่ง disableRotation และถ้าเป็น Off ให้ปลดล็อคการหมุนจอด้วยคำสั่ง enableRotation
4. กำหนดค่าให้กับ OrientationManager และเรียกใช้ Listener แบบ onOrientationManager ที่จะทำงานเมื่อมีการหมุนหน้าจอ
5. เมื่อมีการหมุนหน้าจอจะทำการเช็คก่อนว่าหมุนแบบ 180 องศาหรือไม่ โดยเช็คจากตัวแปร isMirror ว่าเป็น True หรือ False ถ้าเป็น False ก็คือหมุนแค่ 90 องศาปกติ จะให้แสดงข้อความบน Log และเรียกฟังก์ชัน checkOrientation เพื่อแสดงบน Text View ว่าหมุนทิศทางใด
6. เมื่อมีการหมุนหน้าจอแบบ 180 องศา จะให้แสดงข้อความบน Log และเรียกฟังก์ชัน checkOrientation เพื่อแสดงบน Text View ว่าหมุนทิศทางใด
7. เรียกฟังก์ชัน checkOrientation เพื่อแสดงบน Text View ว่าหมุนทิศทางใด โดยเรียกตั้งแต่แรกเริ่มใน onCreate เพื่อให้เปิดแอปขึ้นมาแล้วแสดงค่าทันที
8. ฟังก์ชัน onResume เรียกคำสั่ง enable เพื่อเช็คทิศทางการหมุนจอ สำหรับ OrientationManagerListener
9. ฟังก์ชัน onPause เรียกคำสั่ง disable เพื่อหยุดเช็คทิศทางการหมุนจอ สำหรับ OrientationManagerListener
10. ฟังก์ชันที่สร้างขึ้นมาเพื่อเช็คค่าจากตัวแปร orientation ว่าตอนนั้นๆหน้าจอกำลังหมุนอยู่ในทิศทางใด โดยเปรียบเทียบกับค่าในคลาส OrientationManager แล้วแสดงทิศทางที่ได้บน Text View
** ในกรณีที่ไม่ใช้ OrientationManagerListener ก็เอาคำสั่ง enable กับ disable ออกได้เลย **
เมื่อลองทดสอบแอปก็ให้หมุนหน้าจอดู ก็จะเห็นข้อความกลางจอเปลี่ยนข้อความไปตามทิศทางของหน้าจอ ถ้าหมุนหน้าจอไม่ได้ให้เช็คว่าเครื่องได้เปิด Auto Rotate Screen ใน Settings แล้วหรือยัง และเมื่อลองกดปุ่ม Toggle Button เพื่อล็อคทิศทางหน้าจอ เวลาหมุนหน้าจอไปในทิศทางอื่นก็จะไม่มีผล
สำหรับผู้ที่หลงเข้ามาอ่านคนใดต้องการไฟล์ตัวอย่างสามารถดาวน์โหลดได้ที่ Orientation Manager [Google Drive]
if(orientation == OrientationManager.PORTRAIT_NORMAL) {
// หน้าจอแสดงทิศทาง Normal Portrait
} else if(orientation == OrientationManager.PORTRAIT_REVERSE) {
// หน้าจอแสดงทิศทาง Reverse Portrait
} else if(orientation == OrientationManager.LANDSCAPE_NORMAL) {
// หน้าจอแสดงทิศทาง Normal Landscape
} else if(orientation == OrientationManager.LANDSCAPE_REVERSE) {
// หน้าจอแสดงทิศทาง Reverse Landscape
}
main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
<ToggleButton
android:id="@+id/toggleButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="ToggleButton" />
</RelativeLayout>
สำหรับโค๊ดก็ประมาณนี้
Main.java
package app.akexorcist.orientationmanager;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
import android.widget.ToggleButton;
import app.akexorcist.orientationmanager.OrientationManager.OrientationManagerListener;
public class Main extends Activity {
OrientationManager om;
TextView textView1;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView1 = (TextView)findViewById(R.id.textView1);
ToggleButton toggleButton1 =
(ToggleButton)findViewById(R.id.toggleButton1);
toggleButton1.setOnCheckedChangeListener(
new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView
, boolean isChecked) {
if(isChecked)
om.disableRotation();
else
om.enableRotation();
}
});
om = new OrientationManager(Main.this);
om.setOnOrientationListener(new OrientationManagerListener() {
public void onOrientationChanged(int orientation
, boolean isMirror) {
if(!isMirror) {
Log.i("OrientationManager", "Orientation Changed");
checkOrientation(orientation);
}
}
public void onMirrorRotatation(int orientation) {
Log.i("OrientationManager", "Mirror Rotatation");
checkOrientation(orientation);
}
});
checkOrientation(om.getOrientation());
}
public void onResume() {
super.onResume();
om.enable();
}
public void onPause() {
super.onPause();
om.disable();
}
public void checkOrientation(int orientation) {
if(orientation == OrientationManager.PORTRAIT_NORMAL)
textView1.setText("Normal Portrait");
else if(orientation == OrientationManager.PORTRAIT_REVERSE)
textView1.setText("Reverse Portrait");
else if(orientation == OrientationManager.LANDSCAPE_NORMAL)
textView1.setText("Normal Landscape");
else if(orientation == OrientationManager.LANDSCAPE_REVERSE)
textView1.setText("Reverse Landscape");
}
}
1. ประกาศเรียกใช้คลาส OrientationManager และ Text View
2. กำหนดค่าให้กับ Text View เพื่อใช้แสดงทิศทางเครื่อง
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.akexorcist.orientationmanager"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="app.akexorcist.orientationmanager.Main"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
เมื่อลองทดสอบแอปก็ให้หมุนหน้าจอดู ก็จะเห็นข้อความกลางจอเปลี่ยนข้อความไปตามทิศทางของหน้าจอ ถ้าหมุนหน้าจอไม่ได้ให้เช็คว่าเครื่องได้เปิด Auto Rotate Screen ใน Settings แล้วหรือยัง และเมื่อลองกดปุ่ม Toggle Button เพื่อล็อคทิศทางหน้าจอ เวลาหมุนหน้าจอไปในทิศทางอื่นก็จะไม่มีผล
สำหรับผู้ที่หลงเข้ามาอ่านคนใดต้องการไฟล์ตัวอย่างสามารถดาวน์โหลดได้ที่ Orientation Manager [Google Drive]