前陣子有個需求,項目中要實現一個縱向抽屜,抽屜的高度會影響父佈局的高度。聽着感受很簡單的一個佈局是否是?剛開始我也不想重複造輪子,因此跑到github上搜了一下,也許是由於太簡單,也許是由於這種需求很少吧,竟然沒有知足需求的組件。不過無論什麼緣由,本身簡單實現了一個這樣的佈局,發出來給你們提供一下參考。java
首先照慣例,演示一下效果。以下所示(這個gif最後有點掉幀,因此感受有點卡頓)git
功能其實很簡單,就是一個可改變高度的佈局。接下來就簡單講一下實現中須要注意的東西。github
首先是文件目錄,很簡單,就三個文件ide
最主要的就是 VerticalDrawerView
這個類。繼承一個基本佈局便可,這裏我繼承的是 RelativeLayout
,而後在構造函數裏將須要初始化的內部變量所有初始化。函數
public class VerticalDrawerView extends RelativeLayout {
// ... ...
public VerticalDrawerView(@NonNull Context context) {
super(context);
init();
}
public VerticalDrawerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public VerticalDrawerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 展開收起的動畫封裝類
mDrawerAnimator = new VerticalDrawerAnimator(this);
// 展開收起的動畫響應回調
mDrawerAnimator.setListener(new VerticalDrawerAnimator.OnAnimationListener() {
@Override
public void onStart(Animator animation) {
beforeAnimation();
}
@Override
public void onEnd(Animator animation) {
afterAnimation();
}
});
// 三角指示箭頭的容器
mIndicatorContainer = new RelativeLayout(getContext());
// 三角指示箭頭的點擊回調
mIndicatorContainer.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onTrigger();
}
});
}
// ... ...
}
複製代碼
初始化的時候,咱們並無把內容放進來。咱們須要在外部經過 set
方法傳入內容佈局,以下所示佈局
/** * 設置指示器圖標 */
public void setIndicator(Drawable drawable, int width, int height) {
// ...
}
/** * 設置收起時對外展現的View * * @param collapsedView 收起時對外展現的View * @param collapsedHeight 外部設置的固定高度 */
public void setCollapsedView(View collapsedView, int collapsedHeight) {
// ...
}
/** * 設置展開時對外展現的View * * @param expandedView 收起時對外展現的View * @param expandedHeight 外部設置的固定高度 */
public void setExpandedView(View expandedView, int expandedHeight) {
// ...
}
複製代碼
經過這幾個方法,咱們能夠將指示器,展開和收起的佈局(能夠是同一個,本身控制高度)傳入備用。那麼在何時去設置佈局呢?這裏重寫了 onAttachedToWindow()
方法,當整個佈局被附加到窗口的時候才進行設置動畫
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// 初始化佈局參數
initializeExpanded();
initializeCollapsed();
// 設置初始狀態
if (mIsExpanded) {
// ...
} else {
// ...
}
// 設置指示器
ImageView indicatorView = new ImageView(getContext());
// ...
mDrawerAnimator.setIndicatorView(indicatorView);
// 加入到整個佈局中
addView(mIndicatorContainer, containerParams);
}
複製代碼
爲了簡潔,我把其中一些代碼省略了。這樣就算是把基本的工做都作好了,接下來就是實現展開收起的效果,以及效果動畫。this
咱們知道,要實現這樣一個動畫效果,最簡單的就是使用系統提供的屬性動畫。若是一個組件,擁有某個屬性 height
並提供了對應的 set
方法,那麼咱們就能夠經過這樣的代碼spa
ObjectAnimator.ofInt(view, "height", startHeight, endHeight);
複製代碼
以動畫的形式改變 height
的值,達到改變高度的效果。可是問題來了,因爲咱們繼承的是 RelativeLayout
,而 RelativeLayout
自己並無 setHeight()
方法,它的高度是經過 layoutParam
控制的,這時候要經過動畫改變高度怎麼辦呢?code
很簡單,咱們寫個包裝類,持有佈局,並提供一個 height
屬性,在這個屬性的 set
方法去修改高度就能夠了。代碼以下
public class ViewAnimFactory {
private View view;
public void setView(View view) {
this.view = view;
}
public void setHeight(int height) {
view.getLayoutParams().height = height;
view.requestLayout();
}
}
複製代碼
設置屬性動畫的時候,只要這樣作便可
ObjectAnimator.ofInt(mViewAnimFactory, "height", startHeight, endHeight)
複製代碼
同理,設置指示器上下倒轉方向的動畫也相似
ObjectAnimator.ofFloat(mIndicatorView, "rotation", startAngle, endAngle)
複製代碼
動畫設置好了,按理來講咱們直接使用便可。可是我想實現這樣的效果,就是每次當佈局展開收起結束後,指示器方向才發生變化,那麼這裏就要用到 AnimatorSet
來管理動畫,經過調用 AnimatorSet
提供的 playSequentially()
方法就能實現咱們想要的效果了
if (mAnimatorSet != null) {
mAnimatorSet.cancel();
mAnimatorSet.playSequentially(mAnimators);
mAnimatorSet.start();
}
複製代碼
剩下的就是一些細節的處理。好比動畫完成先後內容視圖的變化,以及展開收起默認狀態的處理等等,就不一一細說了。
好了,大概就這麼多,源代碼我已經提交到Github了,而且也打包到jcenter了,直接引用便可。詳細代碼能夠去Github上查看。寫得很差的地方也歡迎你們拍磚。
若是這篇文章幫到了你,麻煩點個贊或者 Github 上給個 star 。您的支持是我最大的動力!謝謝!