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


    在很久很久以前,写了一篇自定义3d view的博客。但是只是讲了如何实现,实现起来还是比较耗时,所以本着平易近人的心态,把他封装成了一个ViewGroup,只需要在你的view或者布局外面包裹一层ThreeDLayout 即可实现3D效果(毕竟:没有什么比拿来直接用更爽的时期)。本文同步自博主的私人博客wing的地方酒馆

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


    效果预览

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

    这里写图片描述这里写图片描述这里写图片描述


    如何导入ThreeDLayout

    方式一

    在gralde下加入compile('com.wingsofts.threedlayout:1.0.0')(还没上传到远程仓库,会于近日上传)

    方式二

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

    如何使用

    以Demo中天气Activity为例。

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

    <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将其包裹。

    <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,并且设置触摸模式,即可实现:

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

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

    //开启水平翻转动画
    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的项目地址:https://github.com/githubwing/ThreeDLayout

  • 相关阅读:
    进程和线程的一个简单形象的解释
    java流的性能优化1-文件复制
    JAVA修饰符类型(public,protected,private,friendly)
    Codeforces Round #252 (Div. 2) 441B. Valera and Fruits
    游戏开场镜头拉近(Unity3D开发之四)
    P1282 多米诺骨牌
    P1280 尼克的任务
    求最小生成树(暴力法,prim,prim的堆优化,kruskal)
    暴力求最短路
    用MyEclipse将java文件转换成UML类图
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333508.html
Copyright © 2020-2023  润新知