能滑动隐藏/显示的头部的控件

google推出的coordinatorlayout对于能看不能用的项目来说是不是很是纠结,今天就来打造一个。

先上效果图哈
1.全部隐藏头部的
这里写图片描述

2.有固定一部分不隐藏的
这里写图片描述

是不是可以满足大部分的需求了,而且刷新控件可以随便换哦,换成啥都可以,这里为了测试就使用了v4包里面的SwipeRefreshLayout。

看完效果就来分析下该怎么做(下面的分析要有view的事件分发基础)
1.使用事件分发的拦截操作:这个方法有一个弊端,就是当onInterceptTouchEvent 拦截后ziview就不能获得事件了,也就是要先松下手才能继续滚动列表,这体验有点生硬,pass
2.事件都交给列表处理(比如都交给listview):这个有一个问题就是不滑动列表,滑动头部就不能滑动了。pass
3.有没有什么方法会在子view执行时父view也知道呢?哈哈。。是不是想到了dispatchTouchEvent()。

就是在父view的dispatchTouchEvent处理父view的滚动

先上下布局文件哈

<com.example.testfellow.RefreshLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_ll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.testfellow.MainActivity" >

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

        <TextView
            android:layout_width="match_parent"
            android:textSize="30sp"
            android:layout_height="wrap_content"
            android:text="hello row 1" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
             android:textSize="30sp"
            android:text="hello row 2" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
             android:textSize="30sp"
            android:text="hello row 3" />
        <!--这里可以设置成固定在头部  -->
         <LinearLayout
             android:id="@+id/fixed_ll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/darker_gray"
        android:orientation="vertical" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
             android:textSize="30sp"
            android:text="hello row 22222" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
             android:textSize="30sp"
            android:text="hello row 222223" />
    </LinearLayout>
    </LinearLayout>


    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/rfl"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <ListView
            android:id="@+id/lv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </ListView>
    </android.support.v4.widget.SwipeRefreshLayout>

</com.example.testfellow.RefreshLinearLayout>

布局分两部分:头部和列表部分。
好现在来看下主角

测量方法

/**
     * 拉长容器
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = getDefaultSize(0, widthMeasureSpec);
        // 一个头一个容器
        measureChild(headerView, widthMeasureSpec, heightMeasureSpec);
        measureChild(contentView, widthMeasureSpec, heightMeasureSpec);
        headerViewHeight = headerView.getMeasuredHeight();
        int measuredHeight = getDefaultSize(0, heightMeasureSpec)
                + headerViewHeight;
        setMeasuredDimension(measuredWidth, measuredHeight);

        if (fixedView != null) {
            headerViewHeight -= fixedView.getMeasuredHeight();
        }
    }

测量方法是不是要把自己拉长来啊,让它能容纳两个view的高度

这里写图片描述

这样就可以滚动了 ,如果在滚动时在改变布局会很卡顿。所以先把他们的高度算好。

/**
     * 控制父view 的滚动
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            handler.removeCallbacksAndMessages(null);
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            downY = lastY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            moveY = ev.getY();
            destY = moveY - lastY;
            // 条件判断
            judgeCondination();
            if (!isCanRefresh && !isCanMore) {
                if (!isTouchSlopOk) {
                    final float touchDestY = downY - moveY;
                    if (Math.abs(touchDestY) > mTouchSlop) {
                        isTouchSlopOk = true;
                    }
                } else {
                    // 处理跟随滚动
                    handleMove();
                }
            }
            lastY = moveY;
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            resetState();
            // 为了使头部在滚动view到头部时一定可见
            handler.sendMessageDelayed(handler.obtainMessage(), 1000);
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    protected void onDetachedFromWindow() {
        // TODO Auto-generated method stub
        handler.removeCallbacksAndMessages(null);
        super.onDetachedFromWindow();
    }

    /**
     * 自动还原处理 不出现显示部分的情况
     */
    private void autoScrollerHandler() {
        // TODO Auto-generated method stub
        final int scrollY = getScrollY();
        if (mTarget instanceof AbsListView) {
            final AbsListView absListView = (AbsListView) mTarget;
            int firstVisiblePosition = absListView.getFirstVisiblePosition();
            if (firstVisiblePosition == 0) {
                View childView = getChildAt(0);
                if (childView.getTop() < headerViewHeight) {// 显示
                    if (scrollY != 0)
                        mScroller.startScroll(0, scrollY, 0, -scrollY);
                } else {// 自动判断
                    // conditionScroll(scrollY);
                }
            } else {
                // conditionScroll(scrollY);
            }
        } else if (mTarget instanceof ScrollView) {// scrollview .....
            final ScrollView scrollView = (ScrollView) mTarget;
            if (scrollView.getScrollY() < headerViewHeight) {
                if (scrollY != 0)
                    mScroller.startScroll(0, scrollY, 0, -scrollY);
            } else {
                // conditionScroll(scrollY);
            }
        }

        ViewCompat.postInvalidateOnAnimation(this);
    }

    /**
     * 暂时不用了
     * 
     * @param scrollY
     */
    private void conditionScroll(final int scrollY) {
        if (scrollY < headerViewHeight / 2) {// show
            if (scrollY != 0)
                mScroller.startScroll(0, scrollY, 0, -scrollY);
        } else {// hide
            if (headerViewHeight != scrollY)
                mScroller
                        .startScroll(0, scrollY, 0, headerViewHeight - scrollY);
        }
    }

    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            int currY = mScroller.getCurrY();
            scrollTo(0, currY);
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

接下来的代码就更平时自定义滑动没多大区别了,只不过平时在onTouchEvent里面写代码,现在是在dispatchTouchEvent里面写。

比较简单就不分析了

再看下使用代码

private RefreshLinearLayout refresh_ll;
    private SwipeRefreshLayout rfl;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        refresh_ll = (RefreshLinearLayout) findViewById(R.id.root_ll);

        rfl = (SwipeRefreshLayout) findViewById(R.id.rfl);

        rfl.setOnRefreshListener(new OnRefreshListener() {

            @Override
            public void onRefresh() {
                // TODO Auto-generated method stub
                rfl.postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub

                        rfl.setRefreshing(false);
                    }
                }, 1500);
            }
        });

        listView = (ListView) findViewById(R.id.lv);

        ArrayList<String> datas = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            datas.add("item=" + i);
        }
        listView.setAdapter(new ArrayAdapter<>(this,
                android.R.layout.simple_list_item_1, datas));

        refresh_ll.setTargetView(listView);

        View fixll = findViewById(R.id.fixed_ll);
        //设置固定头部
//      refresh_ll.setFixedView(fixll);
    }

如果固定头部设置了就可以固定 不设置整个头部都会随着隐藏。

好了 代码传送 ..不知道出什么鬼了 cadn上传资源不显示

把全部代码贴下把

public class RefreshLinearLayout extends LinearLayout {

    private float lastY;
    private float downY;
    private float moveY;
    private float destY;
    // 头部滚动的view
    private View headerView;
    // 能滚动的view
    private View contentView;
    // 头部高度
    private int headerViewHeight;
    // listview scrollView...
    private View mTarget;
    // 是否第一次
    private boolean isFrist = true;
    // 能刷新不
    private boolean isCanRefresh;
    // 能加载更多不
    private boolean isCanMore;
    // 是否满足最小滚动距离
    private boolean isTouchSlopOk;

    // 滚动辅助类
    private Scroller mScroller;
    private int mTouchSlop;

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            autoScrollerHandler();
        };
    };
    private View fixedView;

    public RefreshLinearLayout(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public RefreshLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public RefreshLinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context) {
        // TODO Auto-generated method stub
        mScroller = new Scroller(context);
        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        mTouchSlop = ViewConfigurationCompat
                .getScaledPagingTouchSlop(viewConfiguration);
    }

    /**
     * 控制父view 的滚动
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            handler.removeCallbacksAndMessages(null);
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            }
            downY = lastY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            moveY = ev.getY();
            destY = moveY - lastY;
            // 条件判断
            judgeCondination();
            if (!isCanRefresh && !isCanMore) {
                if (!isTouchSlopOk) {
                    final float touchDestY = downY - moveY;
                    if (Math.abs(touchDestY) > mTouchSlop) {
                        isTouchSlopOk = true;
                    }
                } else {
                    // 处理跟随滚动
                    handleMove();
                }
            }
            lastY = moveY;
            break;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            resetState();
            // 为了使头部在滚动view到头部时一定可见
            handler.sendMessageDelayed(handler.obtainMessage(), 1000);
            break;
        default:
            break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    protected void onDetachedFromWindow() {
        // TODO Auto-generated method stub
        handler.removeCallbacksAndMessages(null);
        super.onDetachedFromWindow();
    }

    /**
     * 自动还原处理 不出现显示部分的情况
     */
    private void autoScrollerHandler() {
        // TODO Auto-generated method stub
        final int scrollY = getScrollY();
        if (mTarget instanceof AbsListView) {
            final AbsListView absListView = (AbsListView) mTarget;
            int firstVisiblePosition = absListView.getFirstVisiblePosition();
            if (firstVisiblePosition == 0) {
                View childView = getChildAt(0);
                if (childView.getTop() < headerViewHeight) {// 显示
                    if (scrollY != 0)
                        mScroller.startScroll(0, scrollY, 0, -scrollY);
                } else {// 自动判断
                    // conditionScroll(scrollY);
                }
            } else {
                // conditionScroll(scrollY);
            }
        } else if (mTarget instanceof ScrollView) {// scrollview .....
            final ScrollView scrollView = (ScrollView) mTarget;
            if (scrollView.getScrollY() < headerViewHeight) {
                if (scrollY != 0)
                    mScroller.startScroll(0, scrollY, 0, -scrollY);
            } else {
                // conditionScroll(scrollY);
            }
        }

        ViewCompat.postInvalidateOnAnimation(this);
    }

    /**
     * 暂时不用了
     * 
     * @param scrollY
     */
    private void conditionScroll(final int scrollY) {
        if (scrollY < headerViewHeight / 2) {// show
            if (scrollY != 0)
                mScroller.startScroll(0, scrollY, 0, -scrollY);
        } else {// hide
            if (headerViewHeight != scrollY)
                mScroller
                        .startScroll(0, scrollY, 0, headerViewHeight - scrollY);
        }
    }

    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            int currY = mScroller.getCurrY();
            scrollTo(0, currY);
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    /**
     * 还原状态
     */
    private void resetState() {
        isFrist = true;
        isCanRefresh = true;
        isCanMore = true;
        isTouchSlopOk = false;
    }

    /**
     * 处理跟随滚动
     */
    private void handleMove() {
        final int scrollY = getScrollY();
        int sy = (int) (-destY * 1.15f);
        if (destY < 0) {// 向上滑
            final int futureY = scrollY + sy;
            if (futureY > headerViewHeight) {
                sy = headerViewHeight - scrollY;
            }
            if (scrollY < headerViewHeight) {
                scrollBy(0, sy);
            }
        } else {// 向下滑
            final int futureY = scrollY + sy;
            if (futureY < 0) {
                sy = -scrollY;
            }
            if (scrollY > 0) {
                scrollBy(0, sy);
            }
        }
    }

    /**
     * 滚动前的必要条件判断
     */
    private void judgeCondination() {
        if (isFrist && destY != 0) {
            isCanRefresh = !canChildScrollUp();
            // 向上还是能滚动
            if (destY < 0) {
                isCanRefresh = false;
            }
            isCanMore = !canChildScrollDown();
            // 向下还是能滚动
            if (destY > 0) {
                isCanMore = false;
            }
            isFrist = false;
        }
    }

    /**
     * 还能否向上滚动
     * 
     * @return
     */
    private boolean canChildScrollUp() {
        if (mTarget == null) {
            return true;
        }
        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (mTarget instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) mTarget;
                return absListView.getChildCount() > 0
                        && (absListView.getFirstVisiblePosition() > 0 || absListView
                                .getChildAt(0).getTop() < absListView
                                .getPaddingTop());
            } else {
                return mTarget.getScrollY() > 0;
            }
        } else {
            return ViewCompat.canScrollVertically(mTarget, -1);
        }
    }

    /**
     * 还能否向上滚动
     * 
     * @return
     */
    private boolean canChildScrollDown() {
        if (mTarget == null) {
            return true;
        }
        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (mTarget instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) mTarget;
                final int lastVisiblePosition = absListView
                        .getLastVisiblePosition();
                final int childIndex = lastVisiblePosition
                        - absListView.getFirstVisiblePosition();
                return absListView.getChildCount() > 0
                        && (lastVisiblePosition < absListView.getCount() || absListView
                                .getChildAt(childIndex).getBottom() > absListView
                                .getHeight() - absListView.getPaddingTop());
            } else {
                return mTarget.getScrollY() < mTarget.getHeight() - getHeight();
            }
        } else {
            return ViewCompat.canScrollVertically(mTarget, 1);
        }
    }

    @Override
    protected void onFinishInflate() {
        // TODO Auto-generated method stub
        super.onFinishInflate();
        if (getChildCount() > 2) {
            throw new IllegalStateException(
                    "RefreshLinearlayout 只能拥有两个childview");
        }
        headerView = getChildAt(0);
        contentView = getChildAt(getChildCount() - 1);
    }

    /**
     * 设置能滚动的view
     * 
     * @param view
     */
    public void setTargetView(View view) {
        this.mTarget = view;
    }

    /**
     * 设置固定的view
     * @param view
     */

    public void setFixedView(View view) {
        this.fixedView = view;
    }

    /**
     * 拉长容器
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = getDefaultSize(0, widthMeasureSpec);
        // 一个头一个容器
        measureChild(headerView, widthMeasureSpec, heightMeasureSpec);
        measureChild(contentView, widthMeasureSpec, heightMeasureSpec);
        headerViewHeight = headerView.getMeasuredHeight();
        int measuredHeight = getDefaultSize(0, heightMeasureSpec)
                + headerViewHeight;
        setMeasuredDimension(measuredWidth, measuredHeight);

        if (fixedView != null) {
            headerViewHeight -= fixedView.getMeasuredHeight();
        }
    }
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值