Android自定义View - 简单纵向抽屉的实现

栏目: Android · 发布时间: 6年前

内容简介:前阵子有个需求,项目中要实现一个纵向抽屉,抽屉的高度会影响父布局的高度。听着感觉很简单的一个布局是不是?刚开始我也不想重复造轮子,所以跑到github上搜了一下,也许是因为太简单,也许是因为这种需求不多吧,居然没有满足需求的组件。不过不管什么原因,自己简单实现了一个这样的布局,发出来给大家提供一下参考。首先照惯例,演示一下效果。如下所示(这个gif最后有点掉帧,所以感觉有点卡顿)功能其实很简单,就是一个可改变高度的布局。接下来就简单讲一下实现中需要注意的东西。

前阵子有个需求,项目中要实现一个纵向抽屉,抽屉的高度会影响父布局的高度。听着感觉很简单的一个布局是不是?刚开始我也不想重复造轮子,所以跑到github上搜了一下,也许是因为太简单,也许是因为这种需求不多吧,居然没有满足需求的组件。不过不管什么原因,自己简单实现了一个这样的布局,发出来给大家提供一下参考。

首先照惯例,演示一下效果。如下所示(这个gif最后有点掉帧,所以感觉有点卡顿)

Android自定义View - 简单纵向抽屉的实现

功能其实很简单,就是一个可改变高度的布局。接下来就简单讲一下实现中需要注意的东西。

首先是文件目录,很简单,就三个文件

Android自定义View - 简单纵向抽屉的实现

最主要的就是 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);
    }
复制代码

为了简洁,我把其中一些代码省略了。这样就算是把基本的工作都做好了,接下来就是实现展开收起的效果,以及效果动画。

我们知道,要实现这样一个动画效果,最简单的就是使用系统提供的 属性动画 。如果一个组件,拥有某个属性 height 并提供了对应的 set 方法,那么我们就可以通过这样的代码

ObjectAnimator.ofInt(view, "height", startHeight, endHeight);
复制代码

以动画的形式改变 height 的值,达到改变高度的效果。但是问题来了,由于我们继承的是 RelativeLayout ,而 RelativeLayout 本身并没有 setHeight() 方法,它的高度是通过 layoutParam 控制的,这时候要通过动画改变高度怎么办呢?

很简单,我们写个包装类,持有布局,并提供一个 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 。您的支持是我最大的动力!谢谢!


以上所述就是小编给大家介绍的《Android自定义View - 简单纵向抽屉的实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Fluent Python

Fluent Python

Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99

Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具