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來分享你的開發經驗哦

相關文章
相關標籤/搜索