下拉头布局,弹簧效果,头布局滑动和ScrollVIew滑动冲突的解决

package com.zft.tygj.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
import android.widget.ScrollView;

import com.zft.tygj.R;


public class MyPullHeaderView extends LinearLayout {

    private View headerView;
    private ScrollView scrollView;
    private int mTouchSlop;
    private float lastY;
    private int headerHeight;
    private float offsetRadio = 2.0f;
    public boolean isDispatchEvent;


    private boolean headScrollEnable = true;

    public MyPullHeaderView(Context context) {
        super(context);
    }

    public MyPullHeaderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyPullHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    public void setHeadScrollEnable(boolean headScrollEnable) {
        this.headScrollEnable = headScrollEnable;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        headerView = findViewById(R.id.headerView);
        scrollView = (ScrollView) findViewById(R.id.myScrollView);
        init();
    }


    /**
     * 初始化
     */
    private void init() {

        mTouchSlop = (int) (ViewConfiguration.get(getContext()).getScaledTouchSlop() * 1.5);
        // 得到Header的高度,这个高度需要用这种方式得到,在onLayout方法里面得到的高度始终是0
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                refreshLoadingViewsSize();
                getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });
    }


    /**
     * 初始化padding,我们根据header的高度来设置top paddingbottom padding
     */
    public void refreshLoadingViewsSize() {
        // 得到header的内容高度,它将会作为拖动刷新的一个临界值,如果拖动距离大于这个高度
        headerHeight = (headerView != null) ? headerView.getHeight() : 0;

        int pLeft = getPaddingLeft();
        int pTop = -headerHeight;
        int pRight = getPaddingRight();
        int pBottom = 0;
        setPadding(pLeft, pTop, pRight, pBottom);


    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                lastY = ev.getY();
                break;

            case MotionEvent.ACTION_MOVE:

                float deltaY = ev.getY() - lastY;

                if (headScrollEnable && isInterceptMoveEvent(deltaY)) {

                    onTouchEvent(ev);

                    return false;

                } else {
                    lastY = ev.getY();
                }

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

                int mScrollY = getScrollY();

                boolean flag = true;

                //解决滑动事件触发点击事件
                if (mScrollY < 0 && mScrollY > -headerHeight//
                        || headerView.getLayoutParams().height > headerHeight) {
                    //当头布局处于滑动状态,屏蔽下层的点击事件
                    flag = false;
                }

                //触摸事件消失,设置头布局位置,要么全显示,要么全隐藏
                onTouchEvent(ev);

                if (!flag) {
                    return false;
                }

                break;


        }


        return super.dispatchTouchEvent(ev);
    }


    /**
     * TODO touch
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {


        switch (event.getAction()) {

            case MotionEvent.ACTION_MOVE:
                float deltaY = event.getY() - lastY;
                lastY = event.getY();
                handlerSelfMoveEvent(deltaY);
                break;


            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

                handlerSelfUpEvent();

                break;
        }

        return super.onTouchEvent(event);
    }


    /**
     * 处理自身的up事件
     */
    private void handlerSelfUpEvent() {
        int mScrollY = getScrollY();
        if (isHeaderViewShowing()) {

            if (Math.abs(mScrollY) < headerHeight / 2) {//回退回去
                setAnimator(Math.abs(mScrollY));

            } else {//直接显示
                setAnimator(-(headerHeight - Math.abs(mScrollY)));
            }

        }

        if (headerView.getLayoutParams().height > headerHeight) {
            setViewHeight(headerView, headerHeight);
        }


    }


    /**
     * 处理自身的move事件 此方法不好,不推荐
     */
    private void handlerSelfMoveEvent1(float deltaY) {

        int mScrollY = getScrollY();

        //向下滑动 deltaY>0  向上滑动 deltaY<0
        //mScrollY=0完全隐藏,mScrollY==-headerHeight全部显示
        if (deltaY > 0) {// 向下滑动布局

            if (mScrollY > -headerHeight) {//还没有显示完全

                if (Math.abs(mScrollY) + Math.abs(deltaY) > headerHeight) {//移动超标了

                    deltaY = headerHeight - Math.abs(mScrollY);
                }

                float mOffsetRadio = getOffsetRadio(mScrollY);
                scrollBy(0, (int) (-deltaY / mOffsetRadio));
            }

        } else if (deltaY < 0) {//向上滑动 deltaY<0

            if (mScrollY < 0 //还没有隐藏完全
                    && headerView.getLayoutParams().height <= headerHeight) {//头布局在放大的时候,不处理

                if (Math.abs(mScrollY) - Math.abs(deltaY) < 0) {//移动超标了
                    deltaY = 0 - Math.abs(mScrollY);
                }
                scrollBy(0, (int) -deltaY);

            }
        }


        //头布局拉伸效果
        if (mScrollY <= -headerHeight) {
            //改变头布局的高度
            getOffsetRadio1();
            int afterViewHeight = getAfterViewHeight(headerView, (int) (deltaY / getOffsetRadio1()));
            setViewHeight(headerView, afterViewHeight);
        }


    }

    /**
     * 处理自身的move事件
     */
    private void handlerSelfMoveEvent(float deltaY) {

        int mScrollY = getScrollY();

        //向下滑动 deltaY>0  向上滑动 deltaY<0
        //mScrollY=0完全隐藏,mScrollY==-headerHeight全部显示
        if (deltaY > 0) {// 向下滑动布局

            if (mScrollY > -headerHeight) {

                float mOffsetRadio = getOffsetRadio(mScrollY);
                float myY = deltaY / mOffsetRadio;

                if (Math.abs(mScrollY) + myY > headerHeight) {//移动超标了
                    scrollTo(0, -headerHeight);
                } else {
                    scrollTo(0, (int) (mScrollY - myY));
                }


            } else if (mScrollY < -headerHeight) {
                scrollTo(0, -headerHeight);
            }


        } else if (deltaY < 0) {//向上滑动 deltaY<0

            if (mScrollY < 0) {

                if (headerView.getLayoutParams().height <= headerHeight) {//头布局 不 处于弹簧状态

                    if (Math.abs(mScrollY) - Math.abs(deltaY) < 0) {//移动超标了
                        scrollTo(0, 0);
                    } else {
                        scrollTo(0, (int) (mScrollY - deltaY));
                    }
                }

            } else if (mScrollY > 0) {
                scrollTo(0, 0);
            }
        }


        //头布局拉伸效果==头布局弹簧效果
        if (mScrollY <= -headerHeight) {
            //改变头布局的高度
            float offsetRadio = getOffsetRadio1();
            int afterViewHeight = getAfterViewHeight(headerView, (int) (deltaY / offsetRadio));
            setViewHeight(headerView, afterViewHeight);
        }

    }

    /**
     * 是否拦截事件
     */
    private boolean isInterceptMoveEvent(float deltaY) {

        boolean intercept = false;


        //向下滑动 deltaY>0  向上滑动 deltaY<0
        if (deltaY > 0 && scrollView.getScrollY() == 0) {
            intercept = true;

        } else if (deltaY < 0) {

            int mScrollY = getScrollY();
            if (mScrollY < 0 && mScrollY >= -headerHeight) {
                intercept = true;
            }
        }


        return intercept;
    }


    /**
     * 头布局拉伸的阻尼系数
     */
    private float getOffsetRadio1() {

        int nowHeight = headerView.getLayoutParams().height;

        return nowHeight * 1.0f * 2 / headerHeight;
    }


    /**
     * 改变 布局的高度
     */
    public int getAfterViewHeight(View view, int upDateHeight) {
        int nowHeight = view.getLayoutParams().height;
        return nowHeight + upDateHeight;
    }

    /**
     * 设置高度
     */
    public void setViewHeight(View view, int afterHeight) {

        if (afterHeight <= headerHeight) {
            afterHeight = headerHeight;
        } else if (afterHeight >= headerHeight * 2) {
            afterHeight = headerHeight * 2;
        }

        view.getLayoutParams().height = afterHeight;
        view.setLayoutParams(view.getLayoutParams());
    }

    private float getOffsetRadio(int mScrollY) {

        float ratio = Math.abs(mScrollY) * 2f / headerHeight;

        float offsetRadio = 5.0f - ratio * 4;

        if (offsetRadio <= 1.0) {
            offsetRadio = 1.0f;
        }


        return offsetRadio;
    }


    /**
     * headerView 是否正在展示
     */
    private boolean isHeaderViewShowing() {

        int mScrollY = getScrollY();
        if (mScrollY < 0) {
            return true;
        }
        return false;
    }


    private void setAnimator(int endY) {

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, endY);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {


            private int lastValue;

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int value = (int) valueAnimator.getAnimatedValue();
                int y = value - lastValue;
                lastValue = value;

                Log.i("TAG", "===y" + getScrollY());
                scrollBy(0, y);
            }
        });


        valueAnimator.setDuration(200).start();

    }

    public void backInitialState() {

        int scrollY = getScrollY();
        if (scrollY != 0) {
            setAnimator(Math.abs(scrollY));
        }

    }


}

package com.zhangfan.myheadscroll;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView tv1;
    private TextView tv2;
    private TextView tv3;
    private LinearLayout headView;
    private LinearLayout ll_addView;
    private ScrollView scrollView;

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

    private void initView() {
        tv1 = (TextView) findViewById(R.id.tv1);
        tv2 = (TextView) findViewById(R.id.tv2);
        tv3 = (TextView) findViewById(R.id.tv3);
        headView = (LinearLayout) findViewById(R.id.headView);
        ll_addView = (LinearLayout) findViewById(R.id.ll_addView);
        scrollView = (ScrollView) findViewById(R.id.scrollView);

        tv1.setOnClickListener(this);
        tv2.setOnClickListener(this);
        tv3.setOnClickListener(this);
        headView.setOnClickListener(this);


        for (int i = 0; i < 20; i++) {


            TextView textView = new TextView(this);
            textView.setText("测试" + i);
            textView.setTextColor(Color.BLACK);
            textView.setTextSize(50);

            LinearLayout.LayoutParams layoutParams = new LinearLayout.//
                    LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);

            textView.setLayoutParams(layoutParams);

            textView.setGravity(Gravity.CENTER);

            ll_addView.addView(textView);

            final int finalI = i;
            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    TextView textView = (TextView) view;
                    textView.setText("点击测试" + finalI);
                }
            });

        }


    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv1:

                ((TextView) v).setText("1111");

                break;
            case R.id.tv2:
                ((TextView) v).setText("222");
                break;
            case R.id.tv3:
                ((TextView) v).setText("222");
                break;
            case R.id.headView:

                break;
        }
    }
}



//==========================XML==============

<?xml version="1.0" encoding="utf-8"?>
<com.zhangfan.myheadscroll.myview.MyPullView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.zhangfan.myheadscroll.MainActivity">


    <LinearLayout
        android:id="@+id/headView"
        android:layout_width="match_parent"
        android:layout_height="400px"
        android:background="#FFFFCCCC"
        android:clickable="true"
        android:orientation="horizontal">


        <TextView
            android:id="@+id/tv1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="true"
            android:gravity="center"
            android:text="Title1"
            android:textColor="#000000"
            android:textSize="50px"/>

        <TextView
            android:id="@+id/tv2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="true"
            android:gravity="center"
            android:text="Title2"
            android:textColor="#000000"
            android:textSize="50px"/>

        <TextView
            android:id="@+id/tv3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="true"
            android:gravity="center"
            android:text="Title3"
            android:textColor="#000000"
            android:textSize="50px"/>


    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="200px"
        android:clickable="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="true"
            android:gravity="center"
            android:text="固定的布局"
            android:textColor="#000000"
            android:textSize="50px"/>


    </LinearLayout>


    <com.zhangfan.myheadscroll.myview.MyScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">


        <LinearLayout
            android:id="@+id/ll_addView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical">


        </LinearLayout>


    </com.zhangfan.myheadscroll.myview.MyScrollView>


</com.zhangfan.myheadscroll.myview.MyPullView>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值