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

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

控件效果如下:

请添加图片描述

实现功能:

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

设计核心:

主要的设计核心是依赖于传感器对手机晃动的监听(重力感应监听器),对每层图片进行不同的移动,实现仿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效果内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

详解Android 裸眼3D效果View控件的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部