在好久好久之前,寫了一篇自定義3d view的博客。可是隻是講了如何實現,實現起來仍是比較耗時,因此本着平易近人的心態,把他封裝成了一個ViewGroup,只須要在你的view或者佈局外面包裹一層ThreeDLayout 便可實現3D效果(畢竟:沒有什麼比拿來就能用更爽的事情了!!)。本文同步自博主的私人博客wing的地方酒館javascript
ThreeDLayout的項目地址:github.com/githubwing/…php
3D觸摸效果,旋轉效果,和用旋轉效果實現的特效java
修改你的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);
}複製代碼
在好久的一篇博客裏,我介紹瞭如何實現一個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來分享你的開發經驗哦