Android自定義View - 簡單縱向抽屜的實現

前陣子有個需求,項目中要實現一個縱向抽屜,抽屜的高度會影響父佈局的高度。聽着感受很簡單的一個佈局是否是?剛開始我也不想重複造輪子,因此跑到github上搜了一下,也許是由於太簡單,也許是由於這種需求很少吧,竟然沒有知足需求的組件。不過無論什麼緣由,本身簡單實現了一個這樣的佈局,發出來給你們提供一下參考。java

首先照慣例,演示一下效果。以下所示(這個gif最後有點掉幀,因此感受有點卡頓)git

demo

功能其實很簡單,就是一個可改變高度的佈局。接下來就簡單講一下實現中須要注意的東西。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地址

若是這篇文章幫到了你,麻煩點個贊或者 Github 上給個 star 。您的支持是我最大的動力!謝謝!

相關文章
相關標籤/搜索