Android 下拉式抽屉折叠动画

自定义listview工具类1、

public class ViewMeasureUtils {

    /**
     * 根据父 View 规则和子 View 的 LayoutParams,计算子类的宽度(width)测量规则
     *
     * @param view
     */
    public static int getChildWidthMeasureSpec(View view, int parentWidthMeasureSpec) {
        // 获取父 View 的测量模式
        int parentWidthMode = MeasureSpec.getMode(parentWidthMeasureSpec);
        // 获取父 View 的测量尺寸
        int parentWidthSize = MeasureSpec.getSize(parentWidthMeasureSpec);

        // 定义子 View 的测量规则
        int childWidthMeasureSpec = 0;

        // 获取子 View 的 LayoutParams
        ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) view.getLayoutParams();


        if (parentWidthMode == MeasureSpec.EXACTLY || parentWidthMode == MeasureSpec.AT_MOST) {
        /* 这是当父类的模式是 dp 的情况 */
            if (layoutParams.width > 0) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
            } else if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidthSize, MeasureSpec.AT_MOST);
            } else if (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(parentWidthSize, MeasureSpec.EXACTLY);
            }
        } else if (parentWidthMode == MeasureSpec.UNSPECIFIED) {
        /* 这是当父类的模式是 MATCH_PARENT 的情况 */
            if (layoutParams.width > 0) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.width, MeasureSpec.EXACTLY);
            } else if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            } else if (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
        }

        // 返回子 View 的测量规则
        return childWidthMeasureSpec;
    }

    /**
     * 根据父 View 规则和子 View 的 LayoutParams,计算子类的宽度(width)测量规则
     *
     * @param view
     */
    public static int getChildHeightMeasureSpec(View view, int parentHeightMeasureSpec) {
        // 获取父 View 的测量模式
        int parentHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
        // 获取父 View 的测量尺寸
        int parentHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec);

        // 定义子 View 的测量规则
        int childHeightMeasureSpec = 0;

        // 获取子 View 的 LayoutParams
        ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) view.getLayoutParams();


        if (parentHeightMode == MeasureSpec.EXACTLY || parentHeightMode == MeasureSpec.AT_MOST) {
        /* 这是当父类的模式是 dp 的情况 */
            if (layoutParams.height > 0) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
            } else if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeightSize, MeasureSpec.AT_MOST);
            } else if (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(parentHeightSize, MeasureSpec.EXACTLY);
            }
        } else if (parentHeightMode == MeasureSpec.UNSPECIFIED) {
        /* 这是当父类的模式是 MATCH_PARENT 的情况 */
            if (layoutParams.height > 0) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
            } else if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            } else if (layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
        }

        // 返回子 View 的测量规则
        return childHeightMeasureSpec;
    }
}

 

自定义listview、


public class SlidingMenuVertical extends LinearLayout {
    private Scroller mScroller;
    private View view_top;
    private View view_bottom;
    private float downX;
    private float downY;
    private boolean opened = true;//状态是否开闭


    private OnSwitchListener onSwitchListener;


    private int duration_max = 300;//最长过度时间

    private int ambit_scroll = 100;//滑动界限,开闭

    private int y_opened = -1;    // * y_opened:抽屉打开时view_bootom的top y

    public SlidingMenuVertical(Context context) {
        this(context, null);
    }


    public SlidingMenuVertical(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
        setOrientation(VERTICAL);
    }

    @Override
    protected void onFinishInflate() {
        // 当xml解析完成时的回调

        view_top = getChildAt(0);
        view_bottom = getChildAt(1);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);


        view_top.measure(widthMeasureSpec, ViewMeasureUtils.getChildHeightMeasureSpec(view_top, heightMeasureSpec));

//        view_middle.measure(widthMeasureSpec,ViewMeasureUtils.getChildHeightMeasureSpec(view_middle,heightMeasureSpec));
        view_bottom.measure(widthMeasureSpec, heightMeasureSpec);

    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        setY_opened();
        // 拦截
        // 竖直滑动时,去拦截
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();

                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();
                // 竖直滑动

                if (Math.abs(moveY - downY) > Math.abs(moveX - downX)) {
                    //上面隐藏
                    if (opened == false) {

                        return false;
                    }

                    //上面显示并且下滑
                    if (opened == true && (moveY - downY) > 0) {

                        return false;
                    }

                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = event.getX();
                float moveY = event.getY();

                int dy = (int) (downY - moveY + 0.5f);// 四舍五入 20.9 + 0.5-->20


//                Log.e("dy","++++++++++++++++++++++++++++"+dy);

                int scrollY = getScrollY();
                //mDownY - moveY>0上滑

                if (scrollY + dy > 0) {
                    scrollBy(0, dy);
                    if (scrollY + dy > getHeight_top()) {
                        scrollTo(0, getHeight_top());
                    }
                }

                downX = moveX;
                downY = moveY;

                break;
            case MotionEvent.ACTION_UP:
//                Log.e("heigth_top", "+++++++++++++++++" + height_top);
//                Log.e("scrollY", "+++++++++++++++++" + getScrollY());
                if (opened) {


                    open(!(getScrollY() > ambit_scroll || getScrollY() > getHeight_top() / 3));

                } else {

                    open(getScrollY() < getHeight_top() - ambit_scroll || getScrollY() < getHeight_top() * 2 / 3);

                }

                break;

        }
        // 消费掉
        return true;
    }

    /**
     * 开闭抽屉
     *
     * @param open
     */
    public void open(boolean open) {
        setY_opened();

        this.opened = open;
        //打开
        if (open) {

//            Log.e("打开", "+++++++++++++++++++++++++++++");


            int startX = getScrollX();// 起始的坐标X
            int startY = getScrollY();// 起始的坐标Y

            int endX = 0;
            int endY = 0;

            int dx = endX - startX;// 增量X
            int dy = endY - startY;// 增量Y
            // 1px = 10
            int duration = Math.abs(dy) * 10;
            if (duration > duration_max) {
                duration = duration_max;
            }

            mScroller.startScroll(startX, startY, dx, dy, duration);
        } else {


            Log.e("关闭", "+++++++++++++++++++++++++++++" + getScrollY());
            int startX = getScrollX();// 起始的坐标X
            int startY = getScrollY();// 起始的坐标Y

            int endX = 0;
            int endY = getHeight_top();

            int dx = endX - startX;// 增量X
            int dy = endY - startY;// 增量Y

            // 1px = 10
            int duration = Math.abs(dy) * 10;
            if (duration > duration_max) {
                duration = duration_max;
            }

            // 模拟数据变化
            mScroller.startScroll(startX, startY, dx, dy, duration);
        }

        invalidate();// 触发ui绘制 --> draw() --> dispatchDraw()--> drawChild -->
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {// 如果正在计算的过程中
            // 更新滚动的位置
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }

    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

//        Log.e("y_now", ScreenUtils.getViewScreenLocation(view_bottom)[1] + "++++++++++++++++++++++");
//
//        Log.e("y_closed", y_opened - height_top + "++++++++++++++++++++++");

        if (onSwitchListener != null) {
            onSwitchListener.onSwitching(t - oldt < 0 ? true : false,
                    getY_now(), getY_opened(), getY_opened() - getHeight_top());
            if (getY_now() == getY_opened()) {
//                Log.e("true", "++++++++++++++++++++++++");
                onSwitchListener.onSwitched(true);
            }
            if (getY_now() == getY_opened() - getHeight_top()) {
//                Log.e("false", "++++++++++++++++++++++++");

                onSwitchListener.onSwitched(false);
            }

        }
    }

    public boolean isOpened() {
        return opened;
    }

    public int getDuration_max() {
        return duration_max;
    }

    /**
     * 设置松手后 开闭最长过渡时间
     *
     * @param duration_max
     */
    public void setDuration_max(int duration_max) {
        this.duration_max = duration_max;
    }

    public View getView_top() {
        return view_top;
    }

    public View getView_bottom() {
        return view_bottom;
    }

    public int getHeight_top() {
        return view_top.getMeasuredHeight();
    }

    /**
     * 获取 * y_opened:抽屉打开时view_bootom的top y
     */
    private void setY_opened(){

        if (y_opened<0){

            y_opened=getViewScreenLocation(view_bottom)[1];
            Log.e("y _open",y_opened+"++++++++++++++++++++");
        }
    }
    /**
     * y_opened:抽屉打开时view_bootom的top y
     *
     * @return
     */
    public int getY_opened() {
        if (y_opened<0){
            Log.e("还未计算出来","+++++++++++++++++++++++++++++++++++");
            return 0;
        }
        return y_opened;
    }

    /**
     * y_now:抽屉实时view_bootom的top y
     *
     * @return
     */
    public int getY_now() {
        return getViewScreenLocation(view_bottom)[1];
    }

    public int getAmbit_scroll() {
        return ambit_scroll;
    }

    /**
     * 修改滑动界限 值,值越大  开闭越难  单位ms
     *
     * @param ambit_scroll <height_top
     */

    public void setAmbit_scroll(int ambit_scroll) {
        this.ambit_scroll = ambit_scroll;
    }

    /**
     * 计算指定的 View 在屏幕中的坐标。
     */
    public  int[] getViewScreenLocation(View view) {
        int[] location = new int[2];
        // 获取控件在屏幕中的位置,返回的数组分别为控件左顶点的 x、y 的值
        view.getLocationOnScreen(location);

        return location;
    }
    public interface OnSwitchListener {

        /*
        滑动中
        y_now:实时view_bottom的top y, y_opened:抽屉打开时view_bootom的top y,y_closed:抽屉关闭时view_bottom的top y  top y:在屏幕中的top y坐标

         */
        public void onSwitching(boolean isToOpen, int y_now, int y_opened, int y_closed);

        /*
        滑动停止,状态是否开闭
         */
        public void onSwitched(boolean opened);
    }

    public void setOnSwitchListener(OnSwitchListener onSwitchListener) {
        this.onSwitchListener = onSwitchListener;
    }
}

2、activity实现代码,可打开可关闭抽屉动画,可监听动画距离——渐变效果

final TextView tv_middle = (TextView) findViewById(R.id.tv_middle);
final SlidingMenuVertical slidingMenuVertical = ((SlidingMenuVertical) findViewById(R.id.slidingMenu));
slidingMenuVertical.setDuration_max(2300);
slidingMenuVertical.setAmbit_scroll(100);
slidingMenuVertical.setOnSwitchListener(new SlidingMenuVertical.OnSwitchListener() {
    /*
           滑动中
y_now:实时view_bottom的top y, y_opened:抽屉打开时view_bootom的top y,y_closed:抽屉关闭时view_bottom的top y  top y:在屏幕中的top y坐标

            */
    @Override
    public void onSwitching(boolean isToOpen, int y_now, int y_opened, int y_closed) {

        tv_middle.setBackgroundColor(Color.argb((int) (1.0f * (y_opened - y_now) / (y_opened - y_closed) * 255),
                Color.red(0xff3F51B5), Color.green(0xff3F51B5), Color.blue(0xff3F51B5)));

        tv_middle.setTextColor(Color.argb((int) (1.0f * (y_opened - y_now) / (y_opened - y_closed) * 255),
                Color.red(0xffffffff), Color.green(0xffffffff), Color.blue(0xffffffff)));
    }

    @Override
    public void onSwitched(boolean opened) {

        if (opened) {
            tv_middle.setBackgroundColor(0xffffffff);
            tv_middle.setTextColor(0xff454545);
        }
    }
});

findViewById(R.id.tv_switch).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        slidingMenuVertical.open(!slidingMenuVertical.isOpened());
    }
});

 

3、layout.xml文件。

说明:SlidingMenuVertical里面第一个item就是抽屉内容——可以是view,可以是ViewGroup,第二个item就是抽屉下面的内容,第二个item里面ScrollView外部view可开关抽屉内容

<com.tianxin.choutis.SlidingMenuVertical
    android:id="@+id/myct"
    android:layout_below="@+id/kgte"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

<TextView
    android:id="@+id/myte"
    android:layout_width="match_parent"
    android:layout_height="150dp"
    android:background="@color/colorAccent"
    android:text="Hello World!" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_middle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="#454545" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#e6e6e6"
            android:overScrollMode="never"
            android:scrollbars="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="下面\n下面\n下面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n下面\n" />
        </ScrollView>
    </LinearLayout>

</com.tianxin.choutis.SlidingMenuVertical>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值