经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
详解Android 裸眼3D效果View控件
来源:jb51  时间:2021/8/16 16:34:40  对本文有异议

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

项目代码:https://gitee.com/jiugeishere/uidesign

控件效果如下:

请添加图片描述

实现功能:

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

设计核心:

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

核心代码:

SensorLayout 用以监听传感器

  1. import android.content.Context;
  2. import android.content.res.TypedArray;
  3. import android.hardware.Sensor;
  4. import android.hardware.SensorEvent;
  5. import android.hardware.SensorEventListener;
  6. import android.hardware.SensorManager;
  7. import android.util.AttributeSet;
  8. import android.widget.FrameLayout;
  9. import android.widget.Scroller;
  10.  
  11. import androidx.annotation.IntDef;
  12. import androidx.annotation.NonNull;
  13. import androidx.annotation.Nullable;
  14.  
  15. import com.ui.design.R;
  16.  
  17. import java.lang.annotation.ElementType;
  18. import java.lang.annotation.Retention;
  19. import java.lang.annotation.RetentionPolicy;
  20. import java.lang.annotation.Target;
  21.  
  22. /**
  23. * 传感器监听
  24. * author tangxianfeng
  25. * created 2021.8.15
  26. **/
  27. public class SensorLayout extends FrameLayout implements SensorEventListener {
  28. private final SensorManager mSensorManager;
  29. private float[] mAccelerateValues;
  30. private float[] mMagneticValues;
  31. private final Scroller mScroller;
  32. private double mDegreeYMin = -50;//最小偏移度数 Y
  33. private double mDegreeYMax = 50;//最大偏移度数 Y
  34. private double mDegreeXMin = -50;//最小偏移度数 X
  35. private double mDegreeXMax = 50;//最大偏移度数 X
  36. private static final double MOVE_DISTANCE_X = 50;//X轴移动偏移量 实际偏移为MOVE_DISTANCE_X*acclerateratio
  37. private static final double MOVE_DISTANCE_Y = 50;//Y轴移动偏移量 实际偏移为MOVE_DISTANCE_Y*acclerateratio
  38. private float acclerateratio = 1;//偏移加速的倍率 可以通过设置此倍率改变偏移速度
  39. private final float[] values = new float[3];//包含 x,y,z的偏移量
  40. private final float[] Sensororientation = new float[9];//旋转矩阵
  41.  
  42. public SensorLayout(@NonNull Context context) {
  43. this(context, null);
  44. }
  45.  
  46. public SensorLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
  47. this(context, attrs, 0);
  48. }
  49.  
  50. public SensorLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  51. super(context, attrs, defStyleAttr);
  52. mScroller = new Scroller(context);
  53. if (attrs != null) {
  54. TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SensorLayoutStyle);
  55. acclerateratio = typedArray.getFloat(R.styleable.SensorLayoutStyle_AccelerateRatio, 1);
  56. }
  57. mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
  58. if (mSensorManager != null) {
  59. Sensor accelerateSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  60. // 地磁场传感器
  61. Sensor magneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
  62. mSensorManager.registerListener(this, accelerateSensor, SensorManager.SENSOR_DELAY_GAME);
  63. mSensorManager.registerListener(this, magneticSensor, SensorManager.SENSOR_DELAY_GAME);
  64. }
  65. }
  66.  
  67.  
  68. @Override
  69. public void onSensorChanged(SensorEvent event) {
  70. if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
  71. mAccelerateValues = event.values;
  72. }
  73. if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
  74. mMagneticValues = event.values;
  75. }
  76.  
  77. if (mMagneticValues != null && mAccelerateValues != null)
  78. SensorManager.getRotationMatrix(Sensororientation, null, mAccelerateValues, mMagneticValues);
  79. SensorManager.getOrientation(Sensororientation, values);
  80. // x轴的偏转角度
  81. double degreeX = (float) Math.toDegrees(values[1]);
  82. // y轴的偏转角度
  83. double degreeY = (float) Math.toDegrees(values[2]);
  84. int scrollX = mScroller.getFinalX();
  85. int scrollY = mScroller.getFinalY();
  86. if (degreeY <= 0 && degreeY > mDegreeYMin) {
  87. scrollX = (int) (degreeY / Math.abs(mDegreeYMin) * MOVE_DISTANCE_X * acclerateratio);
  88. } else if (degreeY > 0 && degreeY < mDegreeYMax) {
  89. scrollX = (int) (degreeY / Math.abs(mDegreeYMax) * MOVE_DISTANCE_X * acclerateratio);
  90. }
  91. if (degreeX <= 0 && degreeX > mDegreeXMin) {
  92. scrollY = (int) (degreeX / Math.abs(mDegreeXMin) * MOVE_DISTANCE_Y * acclerateratio);
  93. } else if (degreeX > 0 && degreeX < mDegreeXMax) {
  94. scrollY = (int) (degreeX / Math.abs(mDegreeXMax) * MOVE_DISTANCE_Y * acclerateratio);
  95. }
  96. smoothScroll(scrollX, scrollY);
  97. }
  98.  
  99.  
  100. @Override
  101. public void onAccuracyChanged(Sensor sensor, int accuracy) {
  102.  
  103. }
  104.  
  105. //移动
  106. public void smoothScroll(int destX, int destY) {
  107. int scrollY = getScrollY();
  108. int delta = destY - scrollY;
  109. mScroller.startScroll(destX, scrollY, 0, delta, 200);
  110. invalidate();
  111. }
  112.  
  113. @Override
  114. public void computeScroll() {
  115. if (mScroller.computeScrollOffset()) {
  116. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  117. postInvalidate();
  118. }
  119. }
  120.  
  121. //解绑监听
  122. public void unregister() {
  123. mSensorManager.unregisterListener(this);
  124. }
  125.  
  126. public void setDegree(double degreeYMin,double degreeYMax,double degreeXMin,double degreeXMax) {
  127. mDegreeYMin = degreeYMin;
  128. mDegreeYMax=degreeYMax;
  129. degreeXMax=degreeYMax;
  130. degreeXMin=degreeXMin;
  131. }
  132.  
  133. public void setAcclerateratio(float acclerateratio) {
  134. this.acclerateratio = acclerateratio;
  135. }
  136.  
  137. @IntDef({DIRECTION_LEFT, DIRECTION_RIGHT})
  138. @Retention(RetentionPolicy.SOURCE)
  139. @Target(ElementType.PARAMETER)
  140. public @interface ADirection {
  141.  
  142. }
  143.  
  144. public static final int DIRECTION_LEFT = 1;
  145. public static final int DIRECTION_RIGHT = -1;
  146. }

Sensor3DView 三层视图封装

  1. import android.content.Context;
  2. import android.content.res.TypedArray;
  3. import android.util.AttributeSet;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.widget.ImageView;
  7. import android.widget.LinearLayout;
  8.  
  9. import androidx.annotation.Nullable;
  10.  
  11. import com.bumptech.glide.Glide;
  12. import com.ui.design.R;
  13. /**
  14. * author tangxianfeng
  15. * created 2021.8.15
  16. **/
  17. public class Sensor3DView extends LinearLayout {
  18.  
  19. private SensorLayout sensorforeground;//最上层传感器View
  20. private SensorLayout sensorbackground;//最底层传感器View
  21. private SensorLayout sensormid;//中间层传感器View
  22.  
  23. private ImageView foregroundimg;//最上层图片
  24. private ImageView backgroundimg;//底层图片
  25. private ImageView midimg;//中间层图片
  26.  
  27. private Context mContext;
  28.  
  29. public Sensor3DView(Context context) {
  30. super(context);
  31. this.mContext = context;
  32. }
  33.  
  34. public Sensor3DView(Context context, @Nullable AttributeSet attrs) {
  35. super(context, attrs);
  36. View inflate = LayoutInflater.from(getContext()).inflate(R.layout.sensor3d_item, this);
  37. this.mContext = context;
  38. initView(inflate);
  39.  
  40. if (attrs != null) {
  41. TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Sensor3DViewStyle);
  42. float forgroundacclerateratio = typedArray.getFloat(R.styleable.Sensor3DViewStyle_foregroundAccelerateRatio, 1);
  43. float backgroundacclerateratio = typedArray.getFloat(R.styleable.Sensor3DViewStyle_backgroundAccelerateRatio, 1);
  44. float midacclerateratio = typedArray.getFloat(R.styleable.Sensor3DViewStyle_midAccelerateRatio, 1);
  45. setAllImg(typedArray.getResourceId(R.styleable.Sensor3DViewStyle_backgrounddrawable,1),typedArray.getResourceId(R.styleable.Sensor3DViewStyle_middrawable,1),typedArray.getResourceId(R.styleable.Sensor3DViewStyle_foregrounddrawable,1));
  46. setAllratio(backgroundacclerateratio, midacclerateratio, forgroundacclerateratio);
  47. }
  48. }
  49.  
  50. private void initView(View inflate) {
  51. sensorforeground = inflate.findViewById(R.id.sensorforeground);
  52. sensorbackground = inflate.findViewById(R.id.sensorbackground);
  53. sensormid = inflate.findViewById(R.id.sensormid);
  54. midimg = inflate.findViewById(R.id.midimg);
  55. backgroundimg = inflate.findViewById(R.id.backgroundimg);
  56. foregroundimg = inflate.findViewById(R.id.foregroundimg);
  57. }
  58.  
  59. //加载三张图片
  60. public void setAllImg(Object backgroundurl, Object midurl, Object foregroundurl) {
  61. Glide.with(mContext).load(backgroundurl).into(backgroundimg);
  62. Glide.with(mContext).load(midurl).into(midimg);
  63. Glide.with(mContext).load(foregroundurl).into(foregroundimg);
  64. }
  65.  
  66. //设置移动速度
  67. public void setAllratio(float backgroundratio, float midratio, float foregroundratio) {
  68. sensorbackground.setAcclerateratio(backgroundratio);
  69. sensormid.setAcclerateratio(midratio);
  70. sensorforeground.setAcclerateratio(foregroundratio);
  71. }
  72.  
  73. //设置限制角度
  74. public void setDegree(float MinX,float MinY,float MaxX,float MaxY,View3DLayer layer){
  75. if (MinX>=MaxX||MinY>=MaxY){
  76. return;
  77. }
  78. switch (layer){
  79. case all:
  80. setDegree(MinY,MaxY,MinX,MaxX,sensorforeground);
  81. setDegree(MinY,MaxY,MinX,MaxX,sensormid);
  82. setDegree(MinY,MaxY,MinX,MaxX,sensorbackground);
  83. break;
  84. case mid:
  85. setDegree(MinY,MaxY,MinX,MaxX,sensormid);
  86. break;
  87. case background:
  88. setDegree(MinY,MaxY,MinX,MaxX,sensorbackground);
  89. break;
  90. case foreground:
  91. setDegree(MinY,MaxY,MinX,MaxX,sensorforeground);
  92. break;
  93. }
  94. }
  95.  
  96. //sensorLayout 设置限制角度
  97. private void setDegree(float MinY,float MaxY,float MinX,float MaxX,SensorLayout sensorLayout){
  98. sensorLayout.setDegree(MinY,MaxY,MinX,MaxX);
  99. }
  100. @Override
  101. public void destroyDrawingCache() {
  102. super.destroyDrawingCache();
  103. sensorbackground.unregister();
  104. sensormid.unregister();
  105. sensorforeground.unregister();
  106. }
  107. public enum View3DLayer{
  108. foreground,
  109. background,
  110. mid,
  111. all
  112. }
  113. }

styles.xml

  1. <!--3D裸眼效果-->
  2. <declare-styleable name="SensorLayoutStyle">
  3. <attr name="AccelerateRatio" format="float" />
  4. </declare-styleable>
  5.  
  6.  
  7. <!--3D裸眼效果集合View-->
  8. <declare-styleable name="Sensor3DViewStyle">
  9. <attr name="foregroundAccelerateRatio" format="float" />
  10. <attr name="backgroundAccelerateRatio" format="float" />
  11. <attr name="midAccelerateRatio" format="float" />
  12. <attr name="foregrounddrawable" format="reference" />
  13. <attr name="backgrounddrawable" format="reference" />
  14. <attr name="middrawable" format="reference" />
  15. </declare-styleable>

使用示例:

直接引用到layout文件中便可,或者可通过代码设置其他属性。

  1. <com.ui.design.view.sensor3D.view.Sensor3DView
  2. android:id="@+id/sensor3Dview"
  3. android:layout_width="match_parent"
  4. android:layout_height="200dp"
  5. android:layout_centerInParent="true"
  6. app:foregrounddrawable="@drawable/forground3d"
  7. app:backgrounddrawable="@drawable/background3d"
  8. app:middrawable="@drawable/mid3d"
  9. app:foregroundAccelerateRatio="4.0"
  10. app:backgroundAccelerateRatio="-2.0"
  11. app:midAccelerateRatio="1.0"/>

项目代码仓库 UIDesign 开源项目

到此这篇关于详解Android 裸眼3D效果View控件的文章就介绍到这了,更多相关Android 裸眼3D效果内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号