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

实现功能:
- 实现三层图片叠加效果(裸眼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
- }
- }
styles.xml
- <!--3D裸眼效果-->
- <declare-styleable name="SensorLayoutStyle">
- <attr name="AccelerateRatio" format="float" />
- </declare-styleable>
-
-
- <!--3D裸眼效果集合View-->
- <declare-styleable name="Sensor3DViewStyle">
- <attr name="foregroundAccelerateRatio" format="float" />
- <attr name="backgroundAccelerateRatio" format="float" />
- <attr name="midAccelerateRatio" format="float" />
- <attr name="foregrounddrawable" format="reference" />
- <attr name="backgrounddrawable" format="reference" />
- <attr name="middrawable" format="reference" />
- </declare-styleable>
使用示例:
直接引用到layout文件中便可,或者可通过代码设置其他属性。
- <com.ui.design.view.sensor3D.view.Sensor3DView
- android:id="@+id/sensor3Dview"
- android:layout_width="match_parent"
- android:layout_height="200dp"
- android:layout_centerInParent="true"
- app:foregrounddrawable="@drawable/forground3d"
- app:backgrounddrawable="@drawable/background3d"
- app:middrawable="@drawable/mid3d"
- app:foregroundAccelerateRatio="4.0"
- app:backgroundAccelerateRatio="-2.0"
- app:midAccelerateRatio="1.0"/>
项目代码仓库 UIDesign 开源项目
到此这篇关于详解Android 裸眼3D效果View控件的文章就介绍到这了,更多相关Android 裸眼3D效果内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!