Android 仿百度手机助手首页滑动效果

今天看到百度手机助手首页上的滑动效果非常nice,主要功能归结为:

1、当手指上划时,顶部搜索栏随手指移动距离而缩小到隐藏,隐藏后内容还是可以继续移动
2、手指下滑时,当显示内容达到第一个时,顶部搜索栏逐渐变大显示

自己实现用到的知识:

1、android事件传递机制:捕获到手指移动事件后,根据移动的方向与功能栏的高度对功能栏大小进行修改 。由于listview与功能栏高度要同时移动,需要重写了dispatchTouchEvent方法,直接调用this.onTouchEvent(ev);进行所有事件的捕捉分析
2、自定义viewgroup:测量view高度

额外添加的功能:

功能栏高度<=1/2时,会自动隐藏
功能栏高度>1/2时,会自动改变到最大值
代码实现在MotionEvent.ACTION_UP

效果展示,录像工具太卡,将就着看吧:
这里写图片描述这里写图片描述

本来想做成一个通用的工具类,工作比较忙就偷懒了,在ScrollHideLayout类中定义了两个常量,直接写死了需要被捕获滑动事件与改变大小的viewID,使用时可以自己再次封装,或者直接修改ID,虽然不推荐,但是省事哈!!!

private int scrollViewId = R.id.scrollView; //滑动后变化功能栏的viewid
private int changeViewId = R.id.changeView;// 大小随之变化的viewid

完整代码:
1、自定义的viewgroup

package com.example.materialtest.widget;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;

import com.example.materialtest.R;

/**
 * 滑动隐藏控件
 * 
 *
 */
public class ScrollHideLayout extends LinearLayout implements OnScrollListener {

    private static final String TAG = ScrollHideLayout.class.getSimpleName();

    private int scrollViewId = R.id.scrollView;
    private int changeViewId = R.id.changeView;

    private int changeViewMaxHeight;
    private PointF touchPoint = new PointF();

    private View changeView;
    private AbsListView scrollView;

    private RectF scrollViewRect = new RectF();

    public ScrollHideLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    public ScrollHideLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

        if (null == changeView || null == scrollView) {
            for (int i = 0; i < len; i++) {
                View child = getChildAt(i);
                // 滑动控件
                if (child.getId() == scrollViewId && child instanceof AbsListView) {
                    scrollView = (AbsListView) child;
                    setScrollViewRect();
                }
                if (child.getId() == changeViewId) {
                    changeView = child;
                    changeView.setMinimumHeight(0);

                    changeViewMaxHeight = changeView.getMeasuredHeight();
                }

            }
        } else {
            // 重新计算滚动控件的位置
            setScrollViewRect();
        }

        Log.i(TAG, "find scrollview and changeView :" + scrollViewId + "," + changeViewId);
        Log.i(TAG, "scrollview rect:" + changeView.getLayoutParams().getClass().getCanonicalName());

        if (null == changeView || null == scrollView) {
            throw new IllegalArgumentException("could not foud changeView or scrollView");
        }
    }

    private void setScrollViewRect() {
        // 获取滚动控件的范围
        float left = ViewCompat.getX(scrollView);
        float top = ViewCompat.getY(scrollView);
        float right = left + scrollView.getMeasuredWidth();
        float bottom = top + scrollView.getMeasuredHeight();

        scrollViewRect.left = left;
        scrollViewRect.top = top;
        scrollViewRect.right = right;
        scrollViewRect.bottom = bottom;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        if (!isScrollViewTouch(ev)) {
            return false;
        }

        final android.view.ViewGroup.LayoutParams params = changeView.getLayoutParams();

        switch (ev.getAction()) {

        case MotionEvent.ACTION_DOWN:
            touchPoint.x = ev.getX();
            touchPoint.y = ev.getY();
            break;

        case MotionEvent.ACTION_MOVE:

            int height = params.height;

            // 滑动控件移动事件
            float distance = ev.getY() - touchPoint.y;

            // 最大高度,不能向下拖动
            if (height >= changeViewMaxHeight && distance > 0) {
                touchPoint.y = ev.getY();
                break;
            }
            // 已经隐藏 不能向上滑动
            if (height <= 0 && distance < 0) {
                touchPoint.y = ev.getY();
                break;
            }

            // listview到达顶部才可以向下拖动
            if (distance > 0 && scrollView.getFirstVisiblePosition() != 0) {
                touchPoint.y = ev.getY();
                break;
            }

            height = Math.round(height + distance);

            if (height > changeViewMaxHeight) {
                height = changeViewMaxHeight;
            }

            if (height <= 0 && distance < 0) {
                height = 0;
                // TODO onhide
            }

            params.height = height;

            changeView.requestLayout();

            touchPoint.x = ev.getX();
            touchPoint.y = ev.getY();

            break;

        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:

            // 高度超过一半,自动隐藏
            int[] values = null;
            // 向上滑动,剩余位置不足一半
            if (params.height <= changeViewMaxHeight / 2) {
                values = new int[] { params.height, 0 };
            } else {
                values = new int[] { params.height, changeViewMaxHeight };
            }

            if (null != values) {
                ValueAnimator anim = ObjectAnimator.ofInt(changeView, "translationY", values);
                anim.addUpdateListener(new AnimatorUpdateListener() {

                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int value = (int) animation.getAnimatedValue();
                        params.height = value;
                        changeView.requestLayout();
                    }
                });
                anim.setDuration(250);
                anim.setTarget(changeView);
                anim.start();
            }

            break;
        }

        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // 直接拦截事件
        this.onTouchEvent(ev);
        return super.dispatchTouchEvent(ev);
    }

    private boolean isScrollViewTouch(MotionEvent ev) {

        float x = ev.getX();
        float y = ev.getY();
        return (x >= scrollViewRect.left && x <= scrollViewRect.right) && (y >= scrollViewRect.top && y <= scrollViewRect.bottom);
    }

    /**
     * @return Whether it is possible for the child view of this layout to
     *         scroll up. Override this if the child view is a custom view.
     */
    public boolean canChildScrollUp() {
        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (scrollView instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) scrollView;
                return absListView.getChildCount() > 0
                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0).getTop() < absListView.getPaddingTop());
            } else {
                return ViewCompat.canScrollVertically(scrollView, -1) || scrollView.getScrollY() > 0;
            }
        } else {
            return ViewCompat.canScrollVertically(scrollView, -1);
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub

    }
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值