Android图片根据陀螺仪进行左右移动

1,505 阅读2分钟

前言

先上一张效果图,从这个效果图中可以看出,虽然质量低,但是不影响看到图片可以随着手机左右倾斜而移动。

4ba98e05d5371b67992a929d143a591a.gif

虽然不常见,但是我觉得这种很酷,所以学习了一下,特此记录,这个原理主要是监听陀螺仪的变换,从而计算出图片左右要移动多少,所以,图片不能将他全部显示出来,一般图片要比他所在容器大一点才行。

传感器的使用

首先要获取 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)
    }
}