前言
先上一张效果图,从这个效果图中可以看出,虽然质量低,但是不影响看到图片可以随着手机左右倾斜而移动。
虽然不常见,但是我觉得这种很酷,所以学习了一下,特此记录,这个原理主要是监听陀螺仪的变换,从而计算出图片左右要移动多少,所以,图片不能将他全部显示出来,一般图片要比他所在容器大一点才行。
传感器的使用
首先要获取 SensorManager实例,SensorManager也是通过conntext. getSystemService(Context.SENSOR_SERVICE)来获取的,后续需要通过他注册或者取消传感器事件的监听器,因为在陀螺仪变化时,系统会通过回调机制来告诉我们。
var manager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
第二步获取默认的传感器,他的参数类型是传感器类型,而陀螺仪的类型就是 TYPE_GYROSCOPE
var defaultSensor = manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
第三步注册监听器,SensorManager.SENSOR_DELAY_FASTEST表示尽快获取传感器数据。
manager.registerListener(this, defaultSensor, SensorManager.SENSOR_DELAY_FASTEST)
下面是MainAcitity全部代码。
下面公式中需要了解弧度等知识,所有值都是以弧度/秒为单位的,
package com.hxl.kotlindemo
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.view.View
import android.widget.ImageView
class MainActivity : AppCompatActivity(), SensorEventListener {
private val NS2S:Float = 1.0f / 1000000000.0f
private var mLastTimestamp: Long = 0;
private var mRotateRadianY: Double = 0.0;
private var mMaxRotateRadian=Math.PI/9
private lateinit var imageView: SensorImageView;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imageView=findViewById<View>(R.id.panorama_image_view) as SensorImageView
var manager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
var defaultSensor = manager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
manager.registerListener(this, defaultSensor, SensorManager.SENSOR_DELAY_FASTEST)
}
override fun onSensorChanged(event: SensorEvent) {
if (mLastTimestamp == 0L) {
mLastTimestamp = event.timestamp
return
}
val rotateX = Math.abs(event.values[0])
val rotateY = Math.abs(event.values[1])
val rotateZ = Math.abs(event.values[2])
if (rotateY > rotateX + rotateZ) {
val dT: Float = (event.timestamp - mLastTimestamp) * NS2S
mRotateRadianY += (event.values[1] * dT)
if (mRotateRadianY > mMaxRotateRadian) {
mRotateRadianY = mMaxRotateRadian
} else if (mRotateRadianY < -mMaxRotateRadian) {
mRotateRadianY = -mMaxRotateRadian
} else {
imageView.updateProgress((mRotateRadianY / mMaxRotateRadian).toFloat())
}
}
mLastTimestamp = event.timestamp
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
接着自定义ImageView,对外提供一个设置进度的方法,这里首先第一步要获取最大的缩放值,因为在倾斜的过程中,总要有到头的时候。
import android.content.Context
import kotlin.jvm.JvmOverloads
import android.util.AttributeSet
import android.widget.ImageView
import android.graphics.Canvas
class SensorImageView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ImageView(context, attrs, defStyleAttr) {
private var mDrawableWidth = 0
private var mDrawableHeight = 0
private var mWidth = 0
private var mHeight = 0
private var mMaxOffset = 0f
private var mProgress = 0f
fun updateProgress(progress: Float) {
mProgress = progress;
invalidate();
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
mWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight
mHeight = MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom
if (drawable != null) {
mDrawableWidth = drawable.intrinsicWidth
mDrawableHeight = drawable.intrinsicHeight
val imgScale = mHeight.toFloat() / mDrawableHeight.toFloat()
mMaxOffset = Math.abs((mDrawableWidth * imgScale - mWidth) * 0.5f)
}
}
override fun onDraw(canvas: Canvas) {
if ( drawable == null ) {
super.onDraw(canvas)
return
}
val currentOffsetX = mMaxOffset * mProgress
canvas.translate(currentOffsetX, 0f)
super.onDraw(canvas)
}
init {
super.setScaleType(ScaleType.CENTER_CROP)
}
}