Gravity View: แสดงภาพ 360 องศา/Parallax ด้วย Sensor
Gravity View เป็นไลบรารีที่ใช้ Sensor (Accelerometer, Gyroscope) ของอุปกรณ์ Android เพื่อให้ผู้ใช้มองเห็นภาพกว้าง/360 องศาโดยการเอียงอุปกรณ์ เหมาะสำหรับ Virtual Tour, Product Showcase, Panorama Viewer
⚠️ สถานะไลบรารี
ไลบรารี co.gofynd.library:gravity-view:1.0 ดูเหมือนจะไม่ได้รับการอัปเดตนาน (Last update ~2018) และอาจไม่รองรับ Android ใหม่ๆ แนะนำให้พิจารณา:
- Google Scene Viewer / ARCore สำหรับ AR/3D
- PanoramaGL หรือ ViroCore สำหรับ Panorama/360
- เขียนเองด้วย
SensorManager+OpenGL ESเพื่อควบคุมได้เต็มที่
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
Android Studio → File → New Project → Empty Activity
ขั้นตอนที่ 2: build.gradle (Module) - เพิ่ม Dependency
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// ไลบรารี Gravity View (เก่า อาจต้องใช้ JitPack)
implementation 'com.github.gofynd:gravity-view:1.0'
// หรือถ้าไม่หาเจอ ให้ลอง implementation 'co.gofynd.library:gravity-view:1.0'
}
ใน Project-level build.gradle ต้องมี maven { url 'https://jitpack.io' }
ขั้นตอนที่ 3: Layout - activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/panorama_image" />
</HorizontalScrollView>
</RelativeLayout>
เตรียมภาพ: ใส่ภาพ Panorama (กว้างมาก ความสูงเท่าจอ) ใน res/drawable/panorama_image.jpg หรือ res/drawable-nodpi/
ขั้นตอนที่ 4: MainActivity.java
package com.example.gravityviewdemo;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.ImageView;
import co.gofynd.gravityview.GravityView;
public class MainActivity extends AppCompatActivity {
private GravityView gravityView;
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
gravityView = GravityView.getInstance(this);
// ตรวจสอบว่าอุปกรณ์รองรับ Sensor ที่จำเป็นหรือไม่
boolean isSupported = gravityView.deviceSupported();
if (isSupported) {
gravityView
.setImage(imageView, R.drawable.panorama_image)
.center(); // เริ่มที่กึ่งกลาง
} else {
imageView.setImageResource(R.drawable.panorama_image);
// Fallback: ใช้ HorizontalScrollView ธรรมดา
}
}
@Override
protected void onResume() {
super.onResume();
if (gravityView != null) {
gravityView.registerListener(); // เริ่มฟัง Sensor
}
}
@Override
protected void onPause() {
super.onPause();
if (gravityView != null) {
gravityView.unRegisterListener(); // หยุดฟัง Sensor (ประหยัดแบต)
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (gravityView != null) {
gravityView.destroy(); // ทำความสะอาด
}
}
}
การทำงานของ Gravity View
- Sensor Fusion: รวม Accelerometer + Gyroscope (ถ้ามี) เพื่อคำนวณ Orientation
- Image Mapping: แมปภาพกว้างลงบน Viewport ของหน้าจอ
- Parallax Effect: เลื่อนภาพตามมุมเอียงของอุปกรณ์
การเขียนเอง (Custom Implementation) - แนะนำสำหรับ Production
หากไลบรารีเก่าไม่ทำงาน ให้เขียนเองด้วย SensorManager:
public class CustomGravityView {
private SensorManager sensorManager;
private Sensor accelerometer, magnetometer;
private float[] rotationMatrix = new float[9];
private float[] orientation = new float[3];
private ImageView imageView;
private int imageWidth, screenWidth;
private float maxScrollX;
public CustomGravityView(Context context, ImageView imageView) {
this.imageView = imageView;
sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
public void register() {
sensorManager.registerListener(sensorEventListener, accelerometer, SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(sensorEventListener, magnetometer, SensorManager.SENSOR_DELAY_GAME);
}
public void unregister() {
sensorManager.unregisterListener(sensorEventListener);
}
private final SensorEventListener sensorEventListener = new SensorEventListener() {
float[] gravity = new float[3];
float[] geomagnetic = new float[3];
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
gravity = event.values.clone();
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
geomagnetic = event.values.clone();
}
if (SensorManager.getRotationMatrix(rotationMatrix, null, gravity, geomagnetic)) {
SensorManager.getOrientation(rotationMatrix, orientation);
// orientation[0] = Azimuth (Z-axis), orientation[1] = Pitch (X-axis), orientation[2] = Roll (Y-axis)
float azimuth = orientation[0]; // -PI to PI
float scrollX = (azimuth / (float)Math.PI) * maxScrollX; // Map to scroll
imageView.post(() -> imageView.setScrollX((int) scrollX));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
};
}
สรุป
Gravity View ให้เอฟเฟกต์ Parallax/360 View ได้ง่าย แต่ไลบรารีเก่าอาจมีปัญหาเข้ากันไม่ได้กับ Android ใหม่ แนะนำให้เขียนเองด้วย SensorManager หรือใช้ไลบรารีสมัยใหม่เช่น ViroCore/ARCore เพื่อความเสถียรและฟีเจอร์มากกว่า