詳解Android 裸眼3D效果View控件
描述:這是一個(gè)裸眼3D效果的控件View。
Tips:本項(xiàng)目代碼部分邏輯參考于其他文章(自如的3D裸眼實(shí)現(xiàn)),眾人拾柴火焰高,希望大家能多多補(bǔ)充。
項(xiàng)目代碼:https://gitee.com/jiugeishere/uidesign
控件效果如下:
實(shí)現(xiàn)功能:
- 實(shí)現(xiàn)三層圖片疊加效果(裸眼3D效果)
- 可設(shè)置每層圖片移動速率
- 可設(shè)置每層圖片移動的限制度數(shù)
- 可直接設(shè)置圖片或引入圖片
設(shè)計(jì)核心:
主要的設(shè)計(jì)核心是依賴于傳感器對手機(jī)晃動的監(jiān)聽(重力感應(yīng)監(jiān)聽器),對每層圖片進(jìn)行不同的移動,實(shí)現(xiàn)仿3D效果。
核心代碼:
SensorLayout 用以監(jiān)聽傳感器
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; /** * 傳感器監(jiān)聽 * 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;//最小偏移度數(shù) Y private double mDegreeYMax = 50;//最大偏移度數(shù) Y private double mDegreeXMin = -50;//最小偏移度數(shù) X private double mDegreeXMax = 50;//最大偏移度數(shù) X private static final double MOVE_DISTANCE_X = 50;//X軸移動偏移量 實(shí)際偏移為MOVE_DISTANCE_X*acclerateratio private static final double MOVE_DISTANCE_Y = 50;//Y軸移動偏移量 實(shí)際偏移為MOVE_DISTANCE_Y*acclerateratio private float acclerateratio = 1;//偏移加速的倍率 可以通過設(shè)置此倍率改變偏移速度 private final float[] values = new float[3];//包含 x,y,z的偏移量 private final float[] Sensororientation = new float[9];//旋轉(zhuǎn)矩陣 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軸的偏轉(zhuǎn)角度 double degreeX = (float) Math.toDegrees(values[1]); // y軸的偏轉(zhuǎn)角度 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(); } } //解綁監(jiān)聽 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); } //設(shè)置移動速度 public void setAllratio(float backgroundratio, float midratio, float foregroundratio) { sensorbackground.setAcclerateratio(backgroundratio); sensormid.setAcclerateratio(midratio); sensorforeground.setAcclerateratio(foregroundratio); } //設(shè)置限制角度 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 設(shè)置限制角度 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文件中便可,或者可通過代碼設(shè)置其他屬性。
<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"/>
項(xiàng)目代碼倉庫 UIDesign 開源項(xiàng)目
到此這篇關(guān)于詳解Android 裸眼3D效果View控件的文章就介紹到這了,更多相關(guān)Android 裸眼3D效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android利用Intent實(shí)現(xiàn)讀取圖片操作
這篇文章主要為大家詳細(xì)介紹了Android利用Intent實(shí)現(xiàn)讀取圖片操作的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-06-06Win10下Android App安裝配置開發(fā)環(huán)境
這篇文章主要為大家詳細(xì)介紹了Win10下Android App安裝配置開發(fā)環(huán)境,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07安卓 獲取手機(jī)IP地址的實(shí)現(xiàn)代碼
本篇文章主要介紹 Android 4.0 獲取手機(jī)IP地址的方法,附有實(shí)現(xiàn)代碼,具有參考價(jià)值,希望對有需要的小伙伴有幫助2016-07-07Android編程處理窗口控件大小,形狀,像素等UI元素工具類
這篇文章主要介紹了Android編程處理窗口控件大小,形狀,像素等UI元素工具類,可實(shí)現(xiàn)像素與dp的轉(zhuǎn)換、窗口寬度設(shè)置、彈出窗口中l(wèi)istview高度設(shè)置等功能,需要的朋友可以參考下2017-12-12Android編程實(shí)現(xiàn)向SD卡寫入數(shù)據(jù)的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)向SD卡寫入數(shù)據(jù)的方法,涉及Android針對SD卡狀態(tài)判斷,文件及權(quán)限操作等相關(guān)技巧,需要的朋友可以參考下2016-04-04