Android打造通用的下拉刷新组件

还记得上一篇 blog 的内容吗?如果不记得建议先去了解一下,Android 事件处理全面剖析 ,因为下拉刷新需要用到手势的处理,而上一篇文章中,对事件处理做了很详细的说明,了解了事件的处理机制,对理解本篇文章有很大的帮助。好了,这里就当大家都已经对事件处理有了一定的了解,开始我们的下拉刷新征程。

还是老规矩,先上效果图,再根据效果图来分析实现的原理; 
这里写图片描述

一 、分析原理 
我们都知道,listView 控件为我们提供了 addHeaderView、和 addFootView 的方法,我们通过此方法可以很方便的实现下拉刷新效果;但不是所有的控件都有 addHeaderView 方法,比如,scrollView、TextView 等都没有addHeaderView 方法,所以这些控件就需要我们自己通过其他方式实现下拉刷新的效果,一个项目中,为了通用性和复用性,往往也不会把 listView 控件单独分离出来实现下拉刷新的效果,这时,就需要一个能对所有的控件达到通用的下拉刷新效果。 
这里很容易想到用自定义 ViewGroup 来实现,让自定义的 ViewGroup 包含两个控件,一个是下拉刷新的headerView、 另一个是需要展示数据的控件contentView,contentView可以是任何控件;headerView 和 contentView 垂直布局,并且初始状态让 headerView 滚动到看不到的位置。基本的思路就是这样,接下来就是对事件处理。

二、代码实现 
代码实现可以分为四个小点: 
1、自定义 ViewGroup 的实现 
在前面我的 blog 中有一篇写的是关于自定义 View 的内容,Android自定义View,你必须知道的几点 如果对这篇 blog 了解的同学相信对你来说,自定义 ViewGroup 也没什么难度,自定义 ViewGroup 相对自定义 View 还是较容易的。 
自定义 ViewGroup 需要重写的两个方法是

<code class=" hljs cs" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">protected</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onMeasure</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> widthMeasureSpec, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> heightMeasureSpec)</code>
<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">protected</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onLayout</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> changed, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> l, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> t, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> r, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> b)</code>

onMeasure方法相对较简单,只需要对子 View 进行测量即可,这里贴出onMeasure的代码,注解也比较详细。

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">protected</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onMeasure</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> widthMeasureSpec, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> heightMeasureSpec) {
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*获取 ViewGroup 的宽度*/</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> width = MeasureSpec.getSize(widthMeasureSpec) ;
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*获取 ViewGroup 的高度*/</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> height = MeasureSpec.getSize(heightMeasureSpec) ;
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*这里不懂的同学可以去参考我前面写的一篇blog 自定义View*/</span>
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*测量 refreshView 的宽高,这里把高度设为固定值*/</span>
        measureChild(mHeaderView,widthMeasureSpec,MeasureSpec.makeMeasureSpec(mHeaderHeight ,MeasureSpec.EXACTLY));
        Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"==========mHeaderView============"</span>+mHeaderView.getMeasuredHeight()) ;
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*测量 mContentView 的宽高,高度为最大值只能为ViewGroup 的高度*/</span>
        measureChild(mContentView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
        mRefreshHeight = mTextView.getMeasuredHeight() ;
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*千万别忘记调用测量方法*/</span>
        setMeasuredDimension(width,height);
    }</code>

onLayout方法就是根据我们测量子 View 的宽高,来布局子 View,前面我们分析原理的时候说到了,这里需要用到垂直布局,也就是先布局 headerVeiw,再在 headerView 下面布局 contentView,这里 headerView 的隐藏操作也放在 onLayout 方法中,所以就得判断是否是第一次,防止重复隐藏。具体实现代码如下

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">protected</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onLayout</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> changed, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> l, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> t, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> r, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> b) {
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*布局刷新的头部 headerView*/</span>
mHeaderView.layout(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,mHeaderView.getMeasuredWidth(),mHeaderView.getMeasuredHeight());
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*布局内容区域 contentView*/</span>   mContentView.layout(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,mHeaderView.getMeasuredHeight(),mContentView.getMeasuredWidth(),             mHeaderView.getMeasuredHeight()+mContentView.getMeasuredHeight());
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (isFirst){ 
            <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*第一次把 headerView隐藏*/</span>
            scrollTo(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,mHeaderView.getMeasuredHeight());
        }
        isFirst = <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span> ;
    }</code>

上面讲到了两个 View,一个是 headerView ,另一个是 contentView,讲了这么久,相信大家都会问,这两个 View 从何而来?先来分析 headerView, headerView 它是一个固定的、不会变的。所以这里我们可以直接通过 xml 来定义,然后再代码中通过 addView 方法把 headerView 添加进去。 
headerView xml 中的代码

<code class=" hljs xml" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-pi" style="box-sizing: border-box; color: rgb(136, 136, 255);"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">RelativeLayout</span> <span class="hljs-attribute" style="box-sizing: border-box;">xmlns:android</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"http://schemas.android.com/apk/res/android"</span>
                <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
                <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"130dp"</span>
                <span class="hljs-attribute" style="box-sizing: border-box;">android:background</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"@mipmap/refresh_bg"</span>></span>

    <span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">TextView
</span>        <span class="hljs-attribute" style="box-sizing: border-box;">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"@+id/id_txt_header"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:gravity</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"center"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:text</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"下拉可以刷新"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_marginTop</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"70dp"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"60dp"</span>/></span>

    <span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">ImageView
</span>        <span class="hljs-attribute" style="box-sizing: border-box;">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"@+id/id_anim_header"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:scaleType</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"centerCrop"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"60dp"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_marginTop</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"70dp"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:src</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"@drawable/refresh_anim"</span>
        /></span>
<span class="hljs-tag" style="box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">RelativeLayout</span>></span></code>

在初始化 ViewGroup 的时候调用 addView

<code class=" hljs avrasm" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">        mHeaderView = mInflater<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.inflate</span>(R<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.layout</span><span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.refresh</span>_header_view,null) <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">;</span>
        mTextView = mHeaderView<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.findViewById</span>(R<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.id</span><span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.id</span>_txt_header) <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">;</span>
        mAnimView = (ImageView) mHeaderView<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.findViewById</span>(R<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.id</span><span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.id</span>_anim_header)<span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">;</span>
        mAnimDrawable = (AnimationDrawable) mAnimView<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.getDrawable</span>()<span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">;</span>
        mHeaderHeight = (int) TypedValue<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.applyDimension</span>(TypedValue<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.COMPLEX</span>_UNIT_DIP,<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">130</span>,
                getResources()<span class="hljs-preprocessor" style="box-sizing: border-box; color: rgb(136, 0, 0);">.getDisplayMetrics</span>()) <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">;</span>
        addView(mHeaderView)<span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">;</span></code>

再来分析 contentView,我们知道 contentView 是变化的,根据不同的界面展示不同的 contentView,所以可以在界面的 xml 中通过 把需要展示的 View 放入自定义的容器 ViewGroup 中。

<code class=" hljs xml" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">moon.pullrefresh.RefreshView
</span>        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
        <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"wrap_content"</span>></span>
        <span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">ListView
</span>            <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
            <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"wrap_content"</span>
            <span class="hljs-attribute" style="box-sizing: border-box;">android:id</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"@+id/id_listview"</span>></span><span class="hljs-tag" style="box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">ListView</span>></span>
    <span class="hljs-tag" style="box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">moon.pullrefresh.RefreshView</span>></span></code>

那么问题来了,怎么在自定义的 ViewGroup 中获取 contentView 呢?可以自己先考虑下,我们都知道,ViewGroup 有这样一个方法 addView;

<code class=" hljs cs" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child) {
        addView(child, -<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">1</span>);
    }

    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> index) {
        LayoutParams <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span> = child.getLayoutParams();
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span> == <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>) {
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span> = generateDefaultLayoutParams();
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span> == <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>) {
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">throw</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">new</span> IllegalArgumentException(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"generateDefaultLayoutParams() cannot return null"</span>);
            }
        }
        addView(child, index, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>);
    }

    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> width, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> height) {
        final LayoutParams <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span> = generateDefaultLayoutParams();
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>.width = width;
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>.height = height;
        addView(child, -<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">1</span>, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>);
    }

    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child, LayoutParams <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>) {
        addView(child, -<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">1</span>, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>);
    }

    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> index, LayoutParams <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>) {
        requestLayout();
        invalidate(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span>);
        addViewInner(child, index, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span>);
    }</code>

所以我们可以在 addView 的时候,获取 contentView,但又有一个问题,参数个数不同,addView 的调用也不同,我们在add content的时候已经先 add 了一个 headerView,所以这里肯定是调用含有一个 int index 参数的方法,再看 xml 中 viewGroup 包含

<code class=" hljs avrasm" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-label" style="box-sizing: border-box; color: rgb(136, 136, 255);">android:</span>layout_width=<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
<span class="hljs-label" style="box-sizing: border-box; color: rgb(136, 136, 255);">android:</span>layout_height=<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"wrap_content"</span></code>

所以可以断定这里调用的是

<code class=" hljs cs" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> index, LayoutParams <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>) {
        requestLayout();
        invalidate(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span>);
        addViewInner(child, index, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">params</span>, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span>);
    }</code>

所以我们可以通过重写带有三个参数的 addView 方法来获取 contentView

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">addView</span>(View child, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> index, LayoutParams params) {
        mContentView = child ;
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*
        * 这里判断是否是 listView 的 AdapterView
        * 如果是 scrollView,也需要在此判断,
        * 这里可以扩展任意的contentView
        *  这也是关键代码之一
        * */</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (mContentView <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">instanceof</span> AdapterView){
            mAdapter = (AdapterView)  mContentView;
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">super</span>.addView(child, index, params);
    }</code>

2、事件拦截的实现 
通过上一篇blog我们知道了事件传递的顺序,所以想要在 ViewGroup 中相应 onTouchEvent 事件则需要在 onInterceptTouchEvent中对事件进行拦截。 具体拦截代码如下

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onInterceptTouchEvent</span>(MotionEvent ev) {
        Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"====onInterceptTouchEvent===="</span>);
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*是否已经拖拽,也就是是否已经拦截的意思,如果还在拦截中,继续拦截*/</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (mIsBeginDrag){
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span> ;
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(ev.getAction() == MotionEvent.ACTION_DOWN){
            mDownY = (<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span>) ev.getY();
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (ev.getAction() == MotionEvent.ACTION_MOVE){
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> currentY = (<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span>) ev.getY();
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (isIntercept(currentY-mDownY)){
                ev.setAction(MotionEvent.ACTION_DOWN);
                onTouchEvent(ev) ;
                requestDisallowInterceptTouchEvent(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span>);
                mIsBeginDrag = <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span> ;
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span> ;
            }
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">super</span>.onInterceptTouchEvent(ev);
    }</code>

具体判断拦截操作是在isIntercept方法中,进入此方法看看

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">isIntercept</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> distance){
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(distance > <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>){
            Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"====mAdapter===="</span>+mAdapter);
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(mAdapter != <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>){
                Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"====mAdapter===="</span>+mAdapter);
                View firstChild =  mAdapter.getChildAt(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>);
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(firstChild != <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>){
                    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (firstChild.getTop() == <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>){
                        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span> ;
                    }
                }
            }
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span> ;
    }</code>

代码也很简单,因为这里只处理了一种控件,为了达到通用,则需要在此方法中加入判断,判断 contentView是否是 scrollView、TextView 等,根据不同的控件设置不同的拦截条件。

3、事件处理的实现 
这里为了方便起见,我把事件转化成了GestureDetector的 onTouchEvent 来处理,这里面只要对一下几个方法操作即可

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onDown</span>(MotionEvent e) {
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span>;
    }
    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onShowPress</span>(MotionEvent e) {
    }
    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onSingleTapUp</span>(MotionEvent e) {
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span>;
    }
    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onScroll</span>(MotionEvent e1, MotionEvent e2, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">float</span> distanceX, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">float</span> distanceY) {
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span>;
    }
    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onLongPress</span>(MotionEvent e) {
    }
    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onFling</span>(MotionEvent e1, MotionEvent e2, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">float</span> velocityX, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">float</span> velocityY) {
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span>;
    }</code>

还是根据上一篇 blog 的知识可以知道,在onDown方法中,必须方法 true

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onDown</span>(MotionEvent e) {
         <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*根据我前面所讲的Android事件处理全面剖析可知,这里应该返回true*/</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span>;
    }</code>

然后就是对onScroll的处理

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onScroll</span>(MotionEvent e1, MotionEvent e2, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">float</span> distanceX, <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">float</span> distanceY) {
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*这里是让下拉的View 越拉越紧,给人的感觉时越要用力*/</span>
        distanceY = distanceY *  (<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0.8</span>f * (getScrollY() * <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">1.0</span>f / mHeaderHeight));
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*设置界限,滚动的距离不能低于0,也不能高于 headerView 的高度*/</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> scrollY = cling(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>, mHeaderHeight, getScrollY()+(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span>) distanceY) ;
        Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"=======onScroll===="</span>+distanceY+<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">",scrollY=="</span>+scrollY+<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">",getScrollY()="</span>+getScrollY());
        scrollTo(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,scrollY);
        <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*如果达到了下拉刷新的界限,值改变下拉刷新的状态*/</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (scrollY < mHeaderHeight-mRefreshHeight){
            ((TextView)mTextView).setText(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"松开可以刷新"</span>);
            STATUS = STATUS_REFRESH ;
        }<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">else</span>{
            ((TextView)mTextView).setText(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"下拉可以刷新"</span>);
            STATUS = STATUS_HIDE ;
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span>;
    }</code>

在手指抬起的时候,需要释放拦截事件,并且根据当前状态来执行相应的操作,如果可以刷新则刷新,未达到刷新的条件这回复原位

<code class=" hljs cs" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    @Override
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> boolean <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">onTouchEvent</span>(MotionEvent <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">event</span>) {
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">event</span>.getAction() == MotionEvent.ACTION_UP||<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">event</span>.getAction() == MotionEvent.ACTION_CANCEL){
            mIsBeginDrag = <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span> ;
            scrollNormal() ;
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> mGesture.onTouchEvent(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">event</span>);
    }</code>
<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">scrollNormal</span>(){
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (STATUS == STATUS_REFRESH){
            STATUS = STATUS_HIDE ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> scroll = mHeaderHeight - mRefreshHeight -getScrollY() ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> currentDuration = (<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span>) (mDuration*<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0.6</span>f* scroll/(mHeaderHeight - mRefreshHeight));
            mScroller.startScroll(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,getScrollY(),<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,scroll,currentDuration);
            <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*测试*/</span>
            postDelayed(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">new</span> Runnable() {
                <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">run</span>() {
                    stopRefresh() ;
                }
            },<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">1000</span>) ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(mListener != <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>){
                mListener.onRefresh();
            }
            mAnimView.setVisibility(VISIBLE);
            mAnimDrawable.start();
            invalidate();
        }<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">else</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(STATUS == STATUS_HIDE){
            STATUS = STATUS_NORMAL ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> scroll = mHeaderHeight - getScrollY() ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> currentDuration = mDuration* scroll/mHeaderHeight ;
            mScroller.startScroll(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,getScrollY(),<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,scroll,currentDuration);
            mAnimView.setVisibility(View.INVISIBLE);
            mAnimDrawable.stop();
            invalidate();
        }
    }</code>

这里还用到了mScroller.startScroll(0,getScrollY(),0,scroll,currentDuration);知识点,可以参考我的 blog Scroller 的运用案例(一) 
4、定义刷新回调接口

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-javadoc" style="box-sizing: border-box; color: rgb(136, 136, 255);">/**
     * 定义下拉刷新刷新回调接口
     */</span>
    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">interface</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">OnRefreshListener</span>{</span>
        <span class="hljs-javadoc" style="box-sizing: border-box; color: rgb(136, 136, 255);">/**
         * 开始刷新
         */</span>
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> onRefresh() ;
    }</code>

在开始刷新的时候执行回调函数

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">       <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (STATUS == STATUS_REFRESH){
            STATUS = STATUS_HIDE ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> scroll = mHeaderHeight - mRefreshHeight -getScrollY() ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> currentDuration = (<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span>) (mDuration*<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0.6</span>f* scroll/(mHeaderHeight - mRefreshHeight));
            mScroller.startScroll(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,getScrollY(),<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>,scroll,currentDuration);
            <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);">/*测试*/</span>
            postDelayed(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">new</span> Runnable() {
                <span class="hljs-annotation" style="box-sizing: border-box; color: rgb(136, 136, 136);">@Override</span>
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">void</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">run</span>() {
                    stopRefresh() ;
                }
            },<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">1000</span>) ;
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(mListener != <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>){
                mListener.onRefresh();
            }
            mAnimView.setVisibility(VISIBLE);
            mAnimDrawable.start();
            invalidate();
        }</code>

以上就是通用型下拉刷新的实现过程。

三、总结 
我喜欢在写blog 的后面加些总结,可以说是对本篇 blog 所涉及到的知识的一次巩固、并对内容的提炼,从而对本篇 blog 有一个较深的理性认知,希望大家通过我的 blog 不单单能掌握blog 中所实现的内容,更应该掌握实现内容所用到的知识点,从而扩展到其他功能。 
1、自定义 ViewGroup 的实现 
a,重写 onMeasure 方法,主要是测量子 View的大小 
b、重写 onLayout 方法,根据需求布局子 View 
2、事件拦截onInterceptTouchEvent,请参考Android 事件处理全面剖析 
3、事件处理 onTouchEvent,请参考Android 事件处理全面剖析 
4、GestureDetector类事件转换 
5、Scroller 的运用,请参考Scroller 的运用案例(一)

说好了是通用型的下拉刷新,但是好像没实现啊,那么再来看一张TextView 的下拉刷新效果,contentView 只是一个 TextView 
先看 xml

<code class=" hljs xml" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">RelativeLayout</span> <span class="hljs-attribute" style="box-sizing: border-box;">xmlns:android</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"http://schemas.android.com/apk/res/android"</span>
                <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
                <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>></span>

    <span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">moon.pullrefresh.RefreshView
</span>    <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
    <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"wrap_content"</span>></span>
       <span class="hljs-tag" style="box-sizing: border-box;"><<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">TextView
</span>           <span class="hljs-attribute" style="box-sizing: border-box;">android:gravity</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"center"</span>
           <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_width</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"match_parent"</span>
           <span class="hljs-attribute" style="box-sizing: border-box;">android:layout_height</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"wrap_content"</span>
           <span class="hljs-attribute" style="box-sizing: border-box;">android:text</span>=<span class="hljs-value" style="box-sizing: border-box; color: rgb(136, 0, 0);">"@string/hello_world"</span>/></span>
    <span class="hljs-comment" style="box-sizing: border-box; color: rgb(136, 136, 136);"><!--<ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/id_listview"></ListView>--></span>
<span class="hljs-tag" style="box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">moon.pullrefresh.RefreshView</span>></span>
<span class="hljs-tag" style="box-sizing: border-box;"></<span class="hljs-title" style="box-sizing: border-box; font-weight: bold;">RelativeLayout</span>></span></code>

再修改 RefreshView

<code class=" hljs java" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;">    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">boolean</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(136, 0, 0); font-weight: bold;">isIntercept</span>(<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">int</span> distance){
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(distance > <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>){
            Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"====mAdapter===="</span>+mAdapter);
            <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(mAdapter != <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>){
                Log.v(<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"zgy"</span>,<span class="hljs-string" style="box-sizing: border-box; color: rgb(136, 0, 0);">"====mAdapter===="</span>+mAdapter);
                View firstChild =  mAdapter.getChildAt(<span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>);
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span>(firstChild != <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">null</span>){
                    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (firstChild.getTop() == <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>){
                        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span> ;
                    }
                }
            }<span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">else</span> {
                <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (mContentView.getTop() == <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>){
                    <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">true</span> ;
                }
            }
        }
        <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">false</span> ;
    }</code>

只是在原来的基础上加了两句话

<code class=" hljs bash" style="box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; padding: 0.5em; color: rgb(0, 0, 0); border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; display: block; background-color: transparent !important;"><span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">else</span> {
      <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">if</span> (mContentView.getTop() == <span class="hljs-number" style="box-sizing: border-box; color: rgb(0, 136, 0);">0</span>){
          <span class="hljs-keyword" style="box-sizing: border-box; font-weight: bold;">return</span> <span class="hljs-literal" style="box-sizing: border-box; color: rgb(0, 136, 0);">true</span> ;
      }
}</code>

效果图 
这里写图片描述

源码下载地址 Android打造通用的下拉刷新组件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值