5秒讓你的View變3D,ThreeDLayout使用和實現

androidwing發表於2016-10-17

在很久很久以前,寫了一篇自定義3d view的部落格。但是隻是講了如何實現,實現起來還是比較耗時,所以本著平易近人的心態,把他封裝成了一個ViewGroup,只需要在你的view或者佈局外面包裹一層ThreeDLayout 即可實現3D效果(畢竟:沒有什麼比拿來就能用更爽的事情了!!)。本文同步自博主的私人部落格wing的地方酒館

ThreeDLayout的專案地址:github.com/githubwing/…


效果預覽

3D觸控效果,旋轉效果,和用旋轉效果實現的特效

5秒讓你的View變3D,ThreeDLayout使用和實現
這裡寫圖片描述

5秒讓你的View變3D,ThreeDLayout使用和實現

5秒讓你的View變3D,ThreeDLayout使用和實現


如何匯入ThreeDLayout

方式一

修改你的gradle檔案


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

dependencies {
            compile 'com.github.githubwing: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的專案地址:github.com/githubwing/…

如果你是Android開發者,那麼你還可以來 wing的酒館:425983695
來分享你的開發經驗哦

相關文章