Android 裸眼3D效果View

Posted 玖流之辈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 裸眼3D效果View相关的知识,希望对你有一定的参考价值。

android 裸眼3D效果View

描述:这是一个裸眼3D效果的控件View。
Tips:本项目代码部分逻辑参考于其他文章(自如的3D裸眼实现),众人拾柴火焰高,希望大家能多多补充。

项目代码在最后面!!!!跳转到最后

控件效果如下:

实现功能:

  • 实现三层图片叠加效果(裸眼3D效果)
  • 可设置每层图片移动速率
  • 可设置每层图片移动的限制度数
  • 可直接设置图片或引入图片

设计核心:

主要的设计核心是依赖于传感器对手机晃动的监听(重力感应监听器),对每层图片进行不同的移动,实现仿3D效果。

核心代码:

SensorLayout 用以监听传感器


import android.content.Context;
import android.content.res.TypedArray;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import android.widget.Scroller;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.ui.design.R;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 传感器监听
* author tangxianfeng
* created  2021.8.15
**/
public class SensorLayout extends FrameLayout implements SensorEventListener {
    private final SensorManager mSensorManager;
    private float[] mAccelerateValues;
    private float[] mMagneticValues;
    private final Scroller mScroller;
    private double mDegreeYMin = -50;//最小偏移度数  Y
    private double mDegreeYMax = 50;//最大偏移度数  Y
    private double mDegreeXMin = -50;//最小偏移度数  X
    private double mDegreeXMax = 50;//最大偏移度数  X
    private static final double MOVE_DISTANCE_X = 50;//X轴移动偏移量 实际偏移为MOVE_DISTANCE_X*acclerateratio
    private static final double MOVE_DISTANCE_Y = 50;//Y轴移动偏移量 实际偏移为MOVE_DISTANCE_Y*acclerateratio
    private float acclerateratio = 1;//偏移加速的倍率 可以通过设置此倍率改变偏移速度
    private final float[] values = new float[3];//包含 x,y,z的偏移量
    private final float[] Sensororientation = new float[9];//旋转矩阵

    public SensorLayout(@NonNull Context context) {
        this(context, null);
    }

    public SensorLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SensorLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mScroller = new Scroller(context);
        if (attrs != null) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SensorLayoutStyle);
            acclerateratio = typedArray.getFloat(R.styleable.SensorLayoutStyle_AccelerateRatio, 1);
        }
        mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
        if (mSensorManager != null) {
            Sensor accelerateSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            // 地磁场传感器
            Sensor magneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
            mSensorManager.registerListener(this, accelerateSensor, SensorManager.SENSOR_DELAY_GAME);
            mSensorManager.registerListener(this, magneticSensor, SensorManager.SENSOR_DELAY_GAME);
        }
    }


    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            mAccelerateValues = event.values;
        }
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            mMagneticValues = event.values;
        }

        if (mMagneticValues != null && mAccelerateValues != null)
            SensorManager.getRotationMatrix(Sensororientation, null, mAccelerateValues, mMagneticValues);
        SensorManager.getOrientation(Sensororientation, values);
        // x轴的偏转角度
        double degreeX = (float) Math.toDegrees(values[1]);
        // y轴的偏转角度
        double degreeY = (float) Math.toDegrees(values[2]);
        int scrollX = mScroller.getFinalX();
        int scrollY = mScroller.getFinalY();
        if (degreeY <= 0 && degreeY > mDegreeYMin) {
            scrollX = (int) (degreeY / Math.abs(mDegreeYMin) * MOVE_DISTANCE_X * acclerateratio);
        } else if (degreeY > 0 && degreeY < mDegreeYMax) {
            scrollX = (int) (degreeY / Math.abs(mDegreeYMax) * MOVE_DISTANCE_X * acclerateratio);
        }
        if (degreeX <= 0 && degreeX > mDegreeXMin) {
            scrollY = (int) (degreeX / Math.abs(mDegreeXMin) * MOVE_DISTANCE_Y * acclerateratio);
        } else if (degreeX > 0 && degreeX < mDegreeXMax) {
            scrollY = (int) (degreeX / Math.abs(mDegreeXMax) * MOVE_DISTANCE_Y * acclerateratio);
        }
        smoothScroll(scrollX, scrollY);
    }


    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    //移动
    public void smoothScroll(int destX, int destY) {
        int scrollY = getScrollY();
        int delta = destY - scrollY;
        mScroller.startScroll(destX, scrollY, 0, delta, 200);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    //解绑监听
    public void unregister() {
        mSensorManager.unregisterListener(this);
    }

    public void setDegree(double degreeYMin,double degreeYMax,double degreeXMin,double degreeXMax) {
        mDegreeYMin = degreeYMin;
        mDegreeYMax=degreeYMax;
        degreeXMax=degreeYMax;
        degreeXMin=degreeXMin;
    }

    public void setAcclerateratio(float acclerateratio) {
        this.acclerateratio = acclerateratio;
    }

    @IntDef({DIRECTION_LEFT, DIRECTION_RIGHT})
    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.PARAMETER)
    public @interface ADirection {

    }

    public static final int DIRECTION_LEFT = 1;
    public static final int DIRECTION_RIGHT = -1;
}

Sensor3DView 三层视图封装


import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

import com.bumptech.glide.Glide;
import com.ui.design.R;
/**
* author tangxianfeng
* created  2021.8.15
**/
public class Sensor3DView extends LinearLayout {

    private SensorLayout sensorforeground;//最上层传感器View
    private SensorLayout sensorbackground;//最底层传感器View
    private SensorLayout sensormid;//中间层传感器View

    private ImageView foregroundimg;//最上层图片
    private ImageView backgroundimg;//底层图片
    private ImageView midimg;//中间层图片

    private Context mContext;

    public Sensor3DView(Context context) {
        super(context);
        this.mContext = context;
    }

    public Sensor3DView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        View inflate = LayoutInflater.from(getContext()).inflate(R.layout.sensor3d_item, this);
        this.mContext = context;
        initView(inflate);

        if (attrs != null) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Sensor3DViewStyle);
            float forgroundacclerateratio = typedArray.getFloat(R.styleable.Sensor3DViewStyle_foregroundAccelerateRatio, 1);
            float backgroundacclerateratio = typedArray.getFloat(R.styleable.Sensor3DViewStyle_backgroundAccelerateRatio, 1);
            float midacclerateratio = typedArray.getFloat(R.styleable.Sensor3DViewStyle_midAccelerateRatio, 1);
            setAllImg(typedArray.getResourceId(R.styleable.Sensor3DViewStyle_backgrounddrawable,1),typedArray.getResourceId(R.styleable.Sensor3DViewStyle_middrawable,1),typedArray.getResourceId(R.styleable.Sensor3DViewStyle_foregrounddrawable,1));
            setAllratio(backgroundacclerateratio, midacclerateratio, forgroundacclerateratio);
        }
    }

    private void initView(View inflate) {
        sensorforeground = inflate.findViewById(R.id.sensorforeground);
        sensorbackground = inflate.findViewById(R.id.sensorbackground);
        sensormid = inflate.findViewById(R.id.sensormid);
        midimg = inflate.findViewById(R.id.midimg);
        backgroundimg = inflate.findViewById(R.id.backgroundimg);
        foregroundimg = inflate.findViewById(R.id.foregroundimg);
    }

    //加载三张图片
    public void setAllImg(Object backgroundurl, Object midurl, Object foregroundurl) {
        Glide.with(mContext).load(backgroundurl).into(backgroundimg);
        Glide.with(mContext).load(midurl).into(midimg);
        Glide.with(mContext).load(foregroundurl).into(foregroundimg);
    }

    //设置移动速度
    public void setAllratio(float backgroundratio, float midratio, float foregroundratio) {
        sensorbackground.setAcclerateratio(backgroundratio);
        sensormid.setAcclerateratio(midratio);
        sensorforeground.setAcclerateratio(foregroundratio);
    }

    //设置限制角度
    public void setDegree(float MinX,float MinY,float MaxX,float MaxY,View3DLayer layer){
        if (MinX>=MaxX||MinY>=MaxY){
            return;
        }
        switch (layer){
            case all:
                setDegree(MinY,MaxY,MinX,MaxX,sensorforeground);
                setDegree(MinY,MaxY,MinX,MaxX,sensormid);
                setDegree(MinY,MaxY,MinX,MaxX,sensorbackground);
                break;
            case mid:
                setDegree(MinY,MaxY,MinX,MaxX,sensormid);
                break;
            case background:
                setDegree(MinY,MaxY,MinX,MaxX,sensorbackground);
                break;
            case foreground:
                setDegree(MinY,MaxY,MinX,MaxX,sensorforeground);
                break;
        }
    }

    //sensorLayout 设置限制角度
    private void setDegree(float MinY,float MaxY,float MinX,float MaxX,SensorLayout sensorLayout){
        sensorLayout.setDegree(MinY,MaxY,MinX,MaxX);
    }
    @Override
    public void destroyDrawingCache() {
        super.destroyDrawingCache();
        sensorbackground.unregister();
        sensormid.unregister();
        sensorforeground.unregister();
    }
    public enum View3DLayer{
        foreground,
        background,
        mid,
        all
    }
}

sensor3d_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.ui.design.view.sensor3D.view.SensorLayout
        android:id="@+id/sensorbackground"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_gravity="center">

        <ImageView
            以上是关于Android 裸眼3D效果View的主要内容,如果未能解决你的问题,请参考以下文章

Android OpenGL 仿自如 APP 裸眼 3D 效果

Android OpenGL 仿自如 APP 裸眼 3D 效果

Android OpenGL 仿自如 APP 裸眼 3D 效果

Android OpenGL 仿自如 APP 裸眼 3D 效果

Android OpenGL 仿自如 APP 裸眼 3D 效果

Android 手机裸眼 3D 技术原理和编程实现