5秒让你的View变3D,ThreeDLayout使用和实现

在好久好久之前,写了一篇自定义3d view的博客。可是只是讲了如何实现,实现起来仍是比较耗时,因此本着平易近人的心态,把他封装成了一个ViewGroup,只须要在你的view或者布局外面包裹一层ThreeDLayout 便可实现3D效果(毕竟:没有什么比拿来就能用更爽的事情了!!)。本文同步自博主的私人博客wing的地方酒馆javascript

ThreeDLayout的项目地址:github.com/githubwing/…php


效果预览

3D触摸效果,旋转效果,和用旋转效果实现的特效java

这里写图片描述



如何导入ThreeDLayout

方式一

修改你的gradle文件android

allprojects {
  repositories {
    jcenter()
    maven { url "https://jitpack.io" }
  }
}

dependencies {
            compile 'com.github.githubwing:ThreeDLayout:1.0.0'
    }复制代码

方式二

将项目地址依赖库 :threedlayout文件夹下ThreeDLayout.java拷贝至你的项目中,便可使用。git

如何使用

以Demo中天气Activity为例。github

在xml中加入一个TextView,来显示大温度,下面一个RecyclerView,来显示天天的温度。canvas

<LinearLayout

    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/threeDLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.wingsofts.myapplication.WeatherActivity"
    >
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView" android:text="30℃" android:textColor="#fff" android:gravity="center" android:textSize="80sp" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.wingsofts.myapplication.MyRecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout>复制代码

这就是一个最基本的界面实现。如何让大温度显示旋转起来呢?只须要用ThreeDlayout将其包裹。api

<com.wingsofts.threedlayout.ThreeDLayout
      android:background="@color/colorPrimary"
      android:id="@+id/td_header"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      >

   <TextView android:id="@+id/textView" android:text="30℃" android:textColor="#fff" android:gravity="center" android:textSize="80sp" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.wingsofts.threedlayout.ThreeDLayout>复制代码

在代码中获取到该layout,而且设置触摸模式,便可实现:app

ThreeDLayout layout = (ThreeDLayout) findViewById(R.id.td_header);
    //开启触摸模式
    layout.setTouchable(true);
    //设置模式为X,Y轴旋转
    layout.setTouchMode(ThreeDLayout.MODE_BOTH_X_Y);复制代码

接下来说解item动画实现,能够看到其实item是一个接一个延迟旋转,在ThreeDLayout中提供了翻转动画的方法:maven

//开启水平翻转动画
startHorizontalAnimate(long duration)

//延迟开启水平翻转动画

startHorizontalAnimate(long duration,long delayed)复制代码

因此Item动画实际上是一个for循环,让他们依次执行动画便可~~(固然item要使用ThreeDLayout包裹):

for(int i = 0;i<list.size();i++){
      ((ThreeDLayout)recyclerView.getChildAt(i)).startHorizontalAnimateDelayed(100*i,1000);
    }复制代码

到这里,ThreeDLayout的使用已经介绍完了,是否是很简单呢。若是你感兴趣,能够继续往下阅读,会介绍ThreeDLayout是如何实现的。


ThreeDLayout如何实现

在好久的一篇博客里,我介绍了如何实现一个3Dview,这里就不重复讲解。手把手带你撸一个3D view

这里主要讲解如何把每次都要写的代码封装起来。 个人初始思路就是直接包裹成一个ViewGroup,重写onDraw()方法便可。= = 没错就是这么简单。

因此我把以前3D view的代码搬运过来了,而后重写了一下onDraw()。

@Override protected void onDraw(Canvas canvas) {
    mMatrix.reset();
    mCamera.save();
    mCamera.getMatrix(mMatrix);
    mCamera.restore();
    mMatrix.preTranslate(-mCenterX, -mCenterY);
    mMatrix.postTranslate(mCenterX, mCenterY);
    canvas.concat(mMatrix);
    super.onDraw(canvas);
  }复制代码

大概是这样就能完成3D的效果了,可是运行起来没卵用。由于viewgroup的onDraw()通常是不会调用的。怎么解决呢?其实让viewgroup参与draw的过程就好啦,因而我在构造器里给他添加了个背景色。有了背景色他就会调用onDraw了。

public ThreeDLayout(Context context, AttributeSet attrs, int defStyleAttr) {

    super(context, attrs, defStyleAttr);

    //set a default background to make sure onDraw() dispatch
    if (getBackground() == null) {
      setBackgroundColor(Color.parseColor("#ffffff"));
    }
    mCamera = new Camera();
    mMatrix = new Matrix();
  }复制代码

不过事情没有这么简单,还要解决测量问题,因而这里我就取巧,让ThreeDLayout只有一个子view,这样就能够把大小设置成子view的大小,免去测量的过程,因此onMeasure()是这样的:

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (getChildCount() != 1) {
      throw new IllegalStateException("ThreeDLayout can only have one child");
    }
    View child = getChildAt(0);
    measureChild(child, widthMeasureSpec, heightMeasureSpec);

    //only one child view,so give the same size
    setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
  }复制代码

为了提供不一样的需求,因此扩展一下,用户能够本身设置是否开启触摸模式,而且能够设置X,Y,因此在onDraw()里进行一些判断:

if (mMode == MODE_Y || mMode == MODE_BOTH_X_Y) {
      mCamera.rotateX(mCanvasRotateX);
    }
    if (mMode == MODE_X || mMode == MODE_BOTH_X_Y) {
      mCamera.rotateY(mCanvasRotateY);
    }复制代码

如今一个ThreeDLayout就完成了。但是为了让他更好用呢,要添加一个动画效果,就是水平翻转动画,这样实用性更高,就能够实现天气Activity相似效果。因此在onDraw()里要多加一层旋转角度控制.

@Override protected void onDraw(Canvas canvas) {
    mMatrix.reset();
    mCamera.save();
       if (mMode == MODE_Y || mMode == MODE_BOTH_X_Y) {
      mCamera.rotateX(mCanvasRotateX);
    }
    if (mMode == MODE_X || mMode == MODE_BOTH_X_Y) {
      mCamera.rotateY(mCanvasRotateY);
    }


    mCamera.rotateY(mDegreeY);
    mCamera.rotateX(mDegreeX);
    }复制代码

而后提供一个动画开始的方法,顺便当动画完成的时候,使degree变为0,这样就会处于不翻转状态:

public void startHorizontalAnimate(long duration){
    ValueAnimator animator = ValueAnimator.ofFloat(-180f,0f);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override public void onAnimationUpdate(ValueAnimator animation) {
        mDegreeY = (float) animation.getAnimatedValue();
        invalidate();
      }
    });
    animator.addListener(new Animator.AnimatorListener() {
      @Override public void onAnimationStart(Animator animation) {

      }

      @Override public void onAnimationEnd(Animator animation) {
        mDegreeY = 0;
        animator.removeAllUpdateListeners();
      }

      @Override public void onAnimationCancel(Animator animation) {

      }

      @Override public void onAnimationRepeat(Animator animation) {

      }
    });
    animator.setDuration(duration);
    animator.start();

  }复制代码

而后再提供一个延迟动画的方法,内部开一个线程计时,而后去执行动画方法便可:

public void startHorizontalAnimateDelayed(final long delayed, final long duration){

    new Thread(new Runnable() {
      @Override public void run() {
        try {
          Thread.sleep(delayed);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        post(new Runnable() {
          @Override public void run() {
           startHorizontalAnimate(duration);
          }
        });

      }
    }).start();

  }复制代码

好啦,大功告成,以上就是ThreeDLayout的实现啦,若是你以为效果比较cool,或者该控件比较实用,欢迎star一下~ 若是你喜欢个人博客,欢迎评论以及关注我~
ThreeDLayout的项目地址:github.com/githubwing/…

若是你是Android开发者,那么你还能够来 wing的酒馆:425983695来分享你的开发经验哦

相关文章
相关标签/搜索