首頁

2013年6月11日 星期二

*~*轉*阿榮福利味 **~(中文繁化之首)--^ [Android ]**~ScreenOrientationControl - 手機螢幕旋轉及方向固定軟體****(橫向固定、縱向固定) ***~

*轉*阿榮福利味 **~
(中文繁化之首)--^ [Android ] ~~
Screen Orientation
 Control - 手機螢幕
旋轉及方向固定軟體****
(橫向固定、縱向固定) ***~

*
手機螢幕旋轉及強制
切換方向軟體 - Screen Orientation Control
常常想要利用手機橫向或縱向展示畫面
(如:瀏覽器、程式畫面),卻又因為手機
丟過來丟過去雞婆的自動旋轉,或者,晃來
晃去又弄不到滿意的方向嗎?這個APP不僅
可以常駐於左上角,還提供了關閉(OFF)、
橫向固定(Landscape)、縱向固定
(Portrait)的選項,能夠強制螢幕以橫向
或縱向顯示,這樣就算手機在傳遞給他人
的過程當中也不會自動旋轉且固定方向了,
非常實用。(阿榮)(下載
軟體名稱:Screen Orientation Control 
 画面回転制御
軟體作者:eflow inc.
介面語言:英文
軟體性質:免費軟體
軟體下載:[
Android]

[
Android]



     


                                           
 

  *

                         

QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ

^*Dynamic create script to execute~~*{ 动态创建要执行的脚本 }**~^



*Dynamic create script to execute~~
*{ 动态创建要执行的脚本  }*~^*TestComposer.java*










<zk>
 <window apply="pkg$.Test
Composer">
  <div id="div" />
  <textbox id="tb" rows="3" value=
"input any script you want
 here"></textbox>
  <div />
  <button id="btn" label="click
me to execute the script
above"></button>
 </window>
</zk>



*Textbox input restriction sample*
**文本框輸入限制樣品**
*<textbox id="telNo" xmlns:w
="client" >
  <attribute w:name=
"doKeyPress_">
    function(evt){
         if(this.getValue().length > 0 ){
           if (!this._shallIgnore(evt, "0123456789"))
              this.$doKeyPress_(evt);
         }else{
           if (!this._shallIgnore
(evt, "05"))
              this.$doKeyPress_(evt);
         }
    }
  </attribute>
</textbox>               *


*Require user to enter uppercase 
 chars only in textbox*
*要求用戶只有文本框輸入大寫字符*
*
< xmlns:w="client">
  <textbox id="Test" >
    <attribute w:name="doKeyPress_">
      function(evt){
           if (!this._shallIgnore(evt, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
              this.$doKeyPress_(evt);
      }
    </attribute>
   
  </textbox>
</zk>
*


*ckeditor startup with source mode*
*CKEDITOR源模式啟動*
*<  xmlns:w="
2005/zk/client">

 <ckeditor>
  <attribute w:name="bind_">
   function () {
    // set the startup mode
    CKEDITOR.config.startupMode =
 'source';
    //call the original method
    this.$bind_();
   }
  </attribute>
 </ckeditor>
</       *



*修改列表框選擇狀態組成*
*<zk>
  <window border="normal" title="hello" apply="pkg$.TestComposer">
 
   <div>Welcome to ZK Fiddle , run it right now!</div>
 
  <listbox id="box" multiple="true">
        <listhead sizable="true">
            <listheader label="label one" sort="auto" />
            <listheader label="label two" sort="auto" />
        </listhead>
        <listitem>
            <listcell label="data 1-1" />
            <listcell label="data 1-2" />
        </listitem>
        <listitem>
            <listcell label="data 2-1" />
            <listcell label="data 2-2" />
        </listitem>
        <listitem>
            <listcell label="data 3-1" />
            <listcell label="data 3-2" />
        </listitem>
        <listitem>
            <listcell label="data 4-1" />
            <listcell label="data 4-2" />
        </listitem>
    </listbox>
  </window>
</zk>
*


*Change Implementation of
Component, listbox getSelectedItem*
*更改組件,列表框getSelectedItem實施*
*<?init class="org.zkoss.zkplus.
databind.AnnotateDataBinderInit" ?>
<zk>
 <zscript>
  class customListbox extends org.zkoss.zul.Listbox {
   public Listitem getSelectedItem() {
    Listitem li = super.getSelectedItem();
    if (li == null) {
     // get first item if no one is selected
     li = getItemAtIndex(0);
    }
    if (li == null) {
     // create an invalid item if no 
 item in listbox
     li = new Listitem();
     People p = new People();
     p.setName("Invalid Name");
     p.setAge(999);
     li.setValue(p);
    }
    return li;
   }
  }
  page.getComponent
Definition("listbox", true).setImplementationClass(customListbox.class);
  // the class of people
  class People {
   String name;
   int age;
 
   public void setName
(String name) {
    this.name = name;
   }
   public void setAge(int age) {
    this.age = age;
   }
   public String getName() {
    return name;
   }
   public int getAge() {
    return age;
   }
  }
  // create data list
  List lpeoples = new ArrayList();
  List lpeoplesEmpty =
 new ArrayList();
  for (int i = 15;i != 20;i ++) {
   People p = new People();
   p.setName("Name "+i);
   p.setAge(i);
   lpeoples.add(p);
  }
 </zscript>
 <html><![CDATA[
  <ol>
   <li> check / uncheck the
&quot;clear data&quot;
 checkbox to clear
/ restore the data</li>
   <li> the value will show
 in textbox when header double
_clicked</li>
   <li> if an item selected,
 selected item is that one</li>
   <li> if no item selected,
 selected item is the first item</li>
   <li> if no item in listbox, will 
 create an invalid one</li>
  </ol>
  ]]>
 </html>
 <vbox>
  <checkbox label="clear data">
   <attribute name="onCheck">
    if (self.checked)
     listOfPeople.setModel(new BindingListModelList
(lpeoplesEmpty, true));
    else
     listOfPeople.setModel(new BindingListModelList(lpeoples, true));
   </attribute>
  </checkbox>
  <hbox>
   name: <textbox id="sName" />
  </hbox>
  <hbox>
   age: <textbox id="sAge" />
  </hbox>
 </vbox>
 <listbox id="listOfPeople"
 checkmark="true" multiple=
"true" model="@{lpeoples}"
  value="@{peopleData}"
 selectedItem="@{selectedPeople}">
  <listhead sizable="true">
   <attribute name="onDoubleClick">
    People p = (People)listOfPeople.getSelectedItem().
getValue();
    sName.setValue(p.getName());
    sAge.setValue(p.getAge().
toString());
   </attribute>
   <listheader label="Name"/>
   <listheader label="Age"/>
  </listhead>
  <listitem self="@{each=people}"
 value="@{people}">
   <listcell label="@{people.name}"/>
   <listcell label="@{people.age}"/>
  </listitem>
 </listbox>
</zk>        *


********************&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

**Multilizer下載& Key序號* *

**Multilizer下載& Key序號* *

*Multilizer 是個多國語言軟體
開發工具,同時也可用來作 
本地化語系的開發,它支援 
Visual C++、Borland 
Delphi 1-6、Microsoft .NET
、Visual Basic 4.0-6.0 的
程式資源,尤其相當方便
於用來抓取並翻譯 Delphi 的 RCData 字串資源。
 
官方網站:http://www2.multilizer.com
軟體語言:多國語言 含繁體中文和簡體中文介面 
Multilizer下載網址:
請按此!! 
KEY序號︰7D456200-00089328-00107281-09  *


************************************************************************

*基于Inter架构开发**~和优化Android应用**~

*基于Inter架构开发**~
和优化Android应用**~

*本文主要介绍如何在Intel Atom &
&架构下开发和优化Android应用程序!
如何开发和移植Android NDK应用**
**以及如何通过代码阶段&
&编译阶段来优化NDK应用**~
*•1.      Android* 应用程序的分类
•2.      Android* 本地开发套件(NDK)介绍
•3.      为Intel架构开发和移植NDK应用
•4.      本地代码开发注意点
•5.      通用性能优化
•6.      总结*

1).*Android* 应用程序的分类
Android应用程序大致可以分为两类:
一类是Dalvik应用,这类应用程序只包含 
Java代码和必须的xml,png等文件,
调用Android官方SDK的API,最后通过
编译为APK文件。
另一类应用是NDK应用,这类应用不仅
包含Java代码和必须的xml,png等文件,
还包含本地代码(.h, .c, .cc, )甚至还包括
汇编代码。所有本地代码通过MakeFile
编译为动态库(so文件),Java端通过JNI机
制去调用动态库。
其原理如下图所示:*
**
*
Android* 本地开发套件介绍
NDK的全称为本地开发套件,一般来说
会在以下两个场合中使用到:*
*1. 构建高性能要求的部分。如音视频的
编解码,图形图像的处理,会使用C或者
汇编等本地代码。因为Java代码的执行
需要虚拟机去解释,而本地代码直接编译
成二进制文件,在高性能要求的地方,
执行起来更加高效  !
*2. 重用已有代码。对于已有的C库,
C++库,可以通过NDK的机制来调用已有的库*
*
对于相应的应用开发,英特尔提供了硬件
加速管理器,可以显著提升应用开发过
程中Android模拟器的性能。具体可以
accelerated-execution-manager 获取关于
硬件加速管理器的更多内容。
Intel Atom架构开发和优化NDK应用
NDK应用的开发可以分为以下五个步骤:
 *
*
开发兼容Intel架构的NDK应用需要在第
二步编写MakeFile的时候
添加APP_ABI := x86 代码段。
对于已有的NDK应用向Intel Atom架构
移植需要遵循以下步骤:
*
*1. 如果是Dalvik应用,可以直接在Intel架构
上运行。只需要为相应的设备调整分辨率即可*
*2. 如果是NDK应用,如果仅包含C,C++代码
使用上面的方法重编译本地代码,生成在
Intel平台的动态库即可。如果本地代码中还
调用了第三方的动态库,则第三方动态库也
需要重编译一个x86平台的版本。如果包含了
汇编等和硬件平台紧密相关的代码,这部分
代码需要重新编写**
*
本地代码开发注意点
*强制内存对齐*
*由于平台差异,如果不进行强制内存对齐,
在其他平台设备中将数据写入一个文件,
在x86平台去加载会出现大小不一致导致加载
出错的情况。如下面一个结构图 testStruct:*
* struct TestStruct
{
    int mVar1;
    long long mVar2;
    int mVar3;
}                           *

*在ARM平台和Intel平台编译后,大小如
下图:ARM平台会自动采用double malign
的方式,占用24个字节,而x86平台占
用16个字节。
 
如果我们编译时添加强制对齐参数:
-malign-double 则占用相同大小的字节,
不会出现加载出错的情况。
 
将NEON 指令集(ARM*) 移植至SSE 
指令集(英特尔) 
汇编代码部分需要注意平台差异,比如大
字节序和小字节序存储问题;
对于寄存器的操作,需要注意对应寄存器
的大小限制,具体可以查表;
NEON指令集提供了一些本地的C库,这部
分代码虽然是C代码,但是在英特尔® 凌动
TM平台是不能运行的,需要重新编写* *

*通用新能优化 ~
*在本地代码编写阶段,尽可能将短小的
经常调用的函数写成内联函数,可以减少
函数调用的开销。使用float数据类型来
代替double可以充分使用Atom硬件浮点数
预算的功能。多线程编程来充分利用
英特尔® 凌动TM 芯片的超线程性能* *

*多线程编程*
*使用编译器选项 -opt-threads-per-core 
 针对每内核的 1-4 个线程进行调度*
*
本文介绍的编译器选项能够影响应用程序
使用的每内核硬件线程数。

-mCG_lrb_num_threads=1|2|3|4
默认值为 2) ( Composer XE 2013 第一版, 13.0.0.079.
 版本 未经正式归档/不支持的选项 )
Composer XE 2013 更新 1 和更高版本: 
  被支持选项 -opt-threads-per-core=
1/2/3/4 和(默认值为 4)替换    .
·         该选项不影响运行时使用的每内核
线程数量。它由 KMP_AFFINITY、OMP_
 NUM_THREADS 等设置进行控制。
·         使用该选项编译的代码可以在任何
(支持的硬件)每内核线程数上正常运行。
提示编译器将有多少条线程将在内核上运
行。该信息用以更加有效地优化应用性能,
尤其是在指令调度过程中。N 的价值应由
用户决定,以匹配在执行应用过程中每个
内核使用的线程数量。例如,如果应用使
用 OpenMP 实现并行化,使用每内核的线
程数(即 N 的值)作为 OpenMP 相似性
设置,在 MIC 上执行应用代码时
将使用该设置。

*多线程并行程序性能分析方法综述之
Thread Profiler 线程档案器*
*
2.5 Thread Profiler线程档案器
Intel® 线程档案器可以帮助调整Win32*和
OpenMP*线程化软件的性能。该工具通过
监控程序的运行来检测线程性能的相关问题,
包括线程过载和同步冲突,能够帮助查找负载
平衡, 同步开销等线程性能问题。可以进行关
键路径分析。线程档案器最后提供图形式的
检测结果,由此可以快速查明影响程序运行
时间的代码位置,并通过以图象形式生动显
示每个线程的实时工作状况从而大大简化了
调整程序性能的任务。
Intel® 线程档案器支持Intel® 64 和
 Microsoft Visual Studio环境,通常集成
到Intel® VTune性能分析器中使用**

*
2.5.1 线程档案器功能与使用
Win32*线程分析能力:
通过Win32* 线程API对软件线程化后的性能分析;
Win32线程关键路径分析,跟踪显示性能瓶颈--浏览直接影响
执行时间的数据;
Microsoft Visual Studio* .NET开发环境集成--运行线程档案器,
然后在Visual Studio .NET浏览结果;
如果不用Win32库,可以支持用户定义的同步原件。
OpenMP*线程分析能力:
线程档案器将OpenMP线程分析分成以下几类:
时间花费--并行代码;顺序代码;其他线程等待间隔;等待进入关键
部分或锁访问;关键部分和持锁操作;
线程任务量不均衡;
并行过载;
顺序过载。
特性:
自动并行性能分析--提供快速、简单、直接的并行性能分析,
不需要其他机制;
图形化显示线程状态和并串转换--揭示了性能优劣的原因,指明优化重点;
决定多处理器系统的可量测性--多个处理器协同工作的性能非常重要,
对于OpenMP线程,线程档案器以图形方式显示了多处理器系统下理想
的并行性能和程序的实际性能;
线程化设计确认--利用线程档案器检测线程化软件设计的效率。并行编
程的好处体现在高效率和低负载上,线程档案器能以图形方式揭示
程序的效率和过载情况。
Intel® 线程档案器同时显示并发视图和时间轴视图,这有助于理解
哪部分代码适合并行处理以及应用性能问题源于何处,如图2.15所示:

图2.13 同时查看并发和时间轴
通过双击时间轴视图上的转换进入源代码视图,从而准确查看线程
在源代码中进行转换工作的位置,如图2.16所示。这是理解线程应用行为的关键。

图2.14 查看源代码以发现线程问题
Intel® 线程档案器 3.0 Windows 版兼容现今的行业标准开发工具:
Microsoft Visual Studio .NET* 开发环境
Microsoft Visual C++* .NET 编译器 2005、2003、2002 版或 Visual C++ 6.0
Intel® VTune性能分析器 7.2 或 8.0
Intel® Fortran 和 C++ 编译器
Windows 线程和 POSIX* 线程
支持OpenMP

2.5.2 线程档案器实验
本实验通过Intel® 线程档案器来检测Win32线程程序中的性能问题。
(1)入门指南
打开\code\ThreadProfiler\potential lab 1\文件夹,
双击Potential Lab 1.sln文件;
在Build菜单里选择Configuration Manager,然后选择Release模式;
按如下方式配置项目属性:
选中Debug模式 (/Zi)


链接时保留Debug信息 (/DEBUG)

使用线程安全系统库 (/MD)
使用二进制文件可重定位功能 (/fixed:no)
在Build菜单里选择Build Solution,编译相关文件;
运行Intel® VTune性能分析器;
点击New Project;
在Category栏选择Threading Wizards,在下拉框中选择Intel Thread Profiler Wizard;
选择刚才编译好的可执行文件路径
(\code\Thread Profiler\Potential lab 1\Release\potential.exe),
点击Finish按钮,开始运行线程档案器;
运行结束后,可看到双重视图,如图2.15所示:

图2.15 并发级别和时间轴双重视图
双击Profile栏可打开Concurrency Level视图。如果默认不是
Concurrency Level视图,可点击图标进入;
点击其它分组图标,如grouping to thread图标和grouping to
 object图标,观察各自属性;
双击Timeline栏视图可打开时间轴视图,观察性能数据,用鼠标在

小范围拖拽可微观查看线程细节。
问题:
从刚才观察到的图表、数据分析,程序里有比较明显的性能问题吗?
(2)分析程序
返回到Profile栏的Concurrency Level视图,有多少时间消耗在串行
(只有一个线程)执行程序?多少时间消耗在完全的并行执行?
从这能得到什么启发?
点击图标,打开grouping to thread视图,该视图显示了程序中
所有线程在关键路径上的活跃程度。该程序一共运行过多少个线程?
有什么性能问题?
点击图标,打开grouping to object视图,是否有同步对象在关键
路径占了相当大比例,而它又是串行执行的?如果有,是哪一个?
双击Timeline栏,打开时间轴视图。从这些数据中所发现最明显的
特征是什么?如果有性能问题,应该从哪方面去解决它?
(3)负载平衡问题
编译和运行Potential多线程程序
打开\code\ThreadProfiler\Potential Lab 2\文件夹,
双击Potential Lab 2.sln文件;
注意到源代码中两个事件变量bSignaleSignal的定义和初始化,
以及tPoolComputePot函数中done变量的使用,它控制模拟完成
时线程的终结;
选择Release模式编译,并像前一小节设置好项目属性,编译程序;
打开VTune性能分析器,创建一个新的Thread Profiler活动,装入刚才
编译生成的可执行文件,开始分析。
根据线程档案器的分析数据诊断程序性能问题
观察关键路径视图,注意到大部分时间都消耗在Serial impact time,
即串行执行时间。并行级视图进一步确认了大部分时间都
只有一个线程在执行。
打开grouping to object视图,哪个对象占用了串行执行时间?这个对象
限制了其它对象对数据进行访问吗?如果是这样的话,能否改变程序
和数据访问的模式,以减少线程占有同步对象的时间?
注:同步对象eSignal是用来标识线程何时完成,而非用来保护数据,
主线程是惟一一个串行执行的。为了保证程序运行的正确性,主线程
串行执行很有必要。因此,修改数据不会影响到状况。
若问题不是由数据保护这些同步对象引起的,那可能是因为线程自
身引起的。打开grouping to thread视图,是否有一个或多个线程占用
了关键路径中的串行执行时间?如果有,是哪几个?
比较每个tpoolComuptePot (worker)线程的生存时间和活跃时间,
有哪些启发?

解决性能问题
注:两个worker线程活跃时间的差异表明两个线程的计算负载不平衡。
需要重新分配每个线程完成的任务,使任务分配更为平衡。
回到Microsoft Visual Studio界面,观察源代码:在computePot函数中
,每个线程用它分配的标识符(tid)来划分它要完成的任务块。然而,
在内部的循环需要用到外部循环的索引作为结束条件。因此,外部 
循环的索引越大时,内部循环的迭代次数也越多,而相应划分到的
线程所要完成的任务也越重。
有一个很好的方法能解决这种负载不平衡问题,即采用更灵活的
任务分配机制。例如,不采用将连续的迭代分配给一个线程,而交
叉分配给多个线程,比如只有两个线程时,可将奇数次的迭代分配
给一个线程,而将偶数次的迭代分配给另一个线程。
按照上面提示的方法,修改源代码的任务分配机制,结合线程
档案器,使程序的线程负载趋于平衡* ~~

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
            

 

*Android App***列表之下拉刷新 *^~!

*Android App**
*列表之下拉刷新 *^~!

*Android的ListView是应用 
最广的一个组件,功能强大,
扩展性灵活(不局限于ListView
本身一个类),前面的文章有
介绍分组,拖拽,3D立体, 
游标,圆角,而今天我们要 
介绍的是另外一个扩展 
ListView:下拉刷新的ListView。
  

  下拉刷新界面最初流行
于iphone应用界面,如图:
    然后在Android中也逐渐被应用,比如微博
,资讯类。
   所以,今天要实现的结果应该也是类似的,

先贴出最终完成效果,如下图,接下来我们一
步一步实现 *
*
*1. 流程分析
    下拉刷新最主要的流程是:
    (1). 下拉,显示提示头部界面(HeaderView),
这个过程提示用户"下拉刷新"
    (2). 下拉到一定程度,超出了刷新最基本的
下拉界限,我们认为达到了刷新的条件,提示
用户可以"松手刷新"了,效果上允许用户继续
下拉 
    (3). 用户松手,可能用户下拉远远不止提示
头部界面,所以这一步,先反弹回仅显示提示
头部界面,然后提示用户"正在加载"。
    (4). 加载完成后,隐藏提示头部界面。
    示意图如下:*
*
->->
2. 实现分析
    当前我们要实现上述流程,是基于
ListView的,所以对应ListView本身的功能
我们来分析一下实现原理:
    (1). 下拉,显示提示头部界面,这个过程
提示用户"下拉刷新"
        a. 下拉的操作,首先是监听滚动,
ListView提供了onScroll()方法
        b. 与下拉类似一个动作向下飞滑,所以
ListView的scrollState有种值:SCROLL_STATE_IDLE, SCROLL_STATE_
TOUCH_SCROLL, SCROLL_STATE_FLING,
意思容易理解,而我们要下拉的触发条件是SCROLL_STATE_TOUCH_SCROLL。
判断当前的下拉操作状态,ListView提供了
public void onScrollStateChanged
(AbsListView view, int scrollState) {}。
    c. 下拉的过程中,我们可能还需要下拉
到多少的边界值处理,重写onTouchEvent
(MotionEvent ev){}方法,可依
据ACTION_DOWN,ACTION_MOVE,
ACTION_UP实现更精细的判断。
    (2). 下拉到一定程度,超出了刷新最基本
的下拉界限,我们认为达到了刷新的条件,
提示用户可以"松手刷新"了,效果上允
许用户继续下拉
        a. 达到下拉刷新界限,一般指达
到header的高度的,所以有两步,第一,
获取header的高度,第二,
当header.getBottom()>=header的高度时,
我们认为就达到了刷新界限值
        b. 继续允许用户下拉,当header完全
下拉后,默认无法继续下拉,但是可以增加
header的PaddingTop实现这种效果  
    (3). 用户松手,可能用户下拉远远不止提示
头部界面,所以这一步,先反弹回仅显示提示
头部界面,然后提示用户"正在加载"。
        a. 松手后反弹,这个不能一下子弹回去
,看上去太突然,需要一步一步柔性的弹回
去,像弹簧一样,我们可以new一个Thread循
环计算减少PaddingTop,直到PaddingTop为0,
反弹结束。
        b. 正在加载,在子线程里处理后台任务
    (4). 加载完成后,隐藏提示头部界面。
        a. 后台任务完成后,我们需要隐
藏header,setSelection(1)即实现了从第2项
开始显示,间接隐藏了header。
上面我们分析了实现过程的轮廓,接下来,
通过细节说明和代码具体实现。*

*
3. 初始化
    一切状态显示都是用HeaderView显示的,
所以我们需要一个HeaderView的layout,
使用addHeaderView方法添加到ListView中。
    同时,默认状态下,HeaderView是不显示的,
只是在下拉后才显示,所以我们需要隐藏HeaderView且---
---不影响后续的下拉显示,用setSelection(1)。
    refresh_list_header.xml布局如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center">
    <ProgressBar android:id="@+id/refresh_list_header_progressbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        style="?android:attr/progressBarStyleSmall"
        android:visibility="gone">
    </ProgressBar>
    <ImageView android:id="@+id/refresh_list_header_pull_down"
        android:layout_width="9dip"
        android:layout_height="25dip"
        android:layout_gravity="center"
        android:src="@drawable/refresh_list_pull_down" />
    <ImageView android:id="@+id/refresh_list_header_release_up"
        android:layout_width="9dip"
        android:layout_height="25dip"
        android:layout_gravity="center"
        android:src="@drawable/refresh_list_release_up"
        android:visibility="gone" />
    <RelativeLayout android:layout_width="180dip"
        android:layout_height="wrap_content">
        <TextView android:id="@+id/refresh_list_header_text"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_alignParentTop="true"
            android:textSize="12dip"
            android:textColor="#192F06"
            android:paddingTop="8dip"
            android:text="@string/app_list_header_refresh_down"/>
        <TextView android:id="@+id/refresh_list_header_last_update"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_below="@id/refresh_list_header_text"
            android:textSize="12dip"
            android:textColor="#192F06"
            android:paddingBottom="8dip"
            android:text="@string/app_list_header_refresh_last_update"/>
    </RelativeLayout>
</LinearLayout>
    代码中在构造函数中添加init()方法加载如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private LinearLayout mHeaderLinearLayout = null;
private TextView mHeaderTextView = null;
private TextView mHeaderUpdateText = null;
private ImageView mHeaderPullDownImageView = null;
private ImageView mHeaderReleaseDownImageView = null;
private ProgressBar mHeaderProgressBar = null;
public RefreshListView(Context context) {
    this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}
void init(final Context context) {
   mHeaderLinearLayout = (LinearLayout) LayoutInflater.
from(context).inflate(R.layout.refresh_list_header, null);
    addHeaderView(mHeaderLinearLayout);
    mHeaderTextView = (TextView) findViewById(R.id.refresh_
list_header_text);
    mHeaderUpdateText = (TextView) findViewById(R.id.refresh_
list_header_last_update);
    mHeaderPullDownImageView = (ImageView) findViewById(R.id.refresh_list_header_pull_down);
    mHeaderReleaseDownImageView = (ImageView) findViewById(R.id.refresh_list_header_release_up);
    mHeaderProgressBar = (ProgressBar) findViewById(R.id.refresh_
list_header_progressbar);
    setSelection(1);
}
    默认就显示完成了。*

*4. HeaderView的默认高度测量
    因为下拉到HeaderView全部显示出来,
就由提示"下拉刷新"变为"松手刷新",全部显示
的出来的测量标准
就是header.getBottom()>=header的高度。
    所以,首先我们需要测量HeaderView的
默认高度。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//因为是在构造函数里测量高度,应该先measure一下
private void measureView(View child) {
    ViewGroup.LayoutParams p = child.getLayoutParams();
    if (p == null) {
        p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }
    int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
    int lpHeight = p.height;
    int childHeightSpec;
    if (lpHeight > 0) {
        childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
                MeasureSpec.EXACTLY);
    } else {
        childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                MeasureSpec.UNSPECIFIED);
    }
    child.measure(childWidthSpec, childHeightSpec);
}
    然后在init的上述代码后面加上调用measureView后,
使用getMeasureHeight()方法获取header的高度:
?
1
2
3
4
5
6
private int mHeaderHeight;
void init(final Context context) {
    ... ...
    measureView(mHeaderLinearLayout);
    mHeaderHeight = mHeaderLinearLayout.getMeasuredHeight();
}
*
*5. scrollState监听记录
    scrollState有3种,使用onScrollStateChanged() 方法监听记录。
?
1
2
3
4
5
private int mCurrentScrollState;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {  
    mCurrentScrollState = scrollState;
}
    然后即可使用mCurrentScrollState作为后面
判断的条件了。
6. 刷新状态分析
    因为一些地方需要知道我们处在正常状态
下还是进入下拉刷新状态还是松手反弹状态,
比如,
    (1). 在非正常的状态下,我们不小心飞滑了
一下(松手的瞬间容易出现这种情况),我们不能
setSelection(1)的,否则总是松手后header跳的
一下消失掉了。
    (2). 下拉后要做一个下拉效果的特殊处理,
需要用到OVER_PULL_REFRESH
(松手刷新状态下)
    (3). 松手反弹后要做一个反弹效果的特殊处理
,需要用到OVER_PULL_REFRESH和ENTER_PULL_REFRESH。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private final static int NONE_PULL_REFRESH = 0;   //正常状态
private final static int ENTER_PULL_REFRESH = 1//进入下拉刷新状态
private final static int OVER_PULL_REFRESH = 2;   //进入松手刷新状态
private final static int EXIT_PULL_REFRESH = 3;     //松手后反弹后加载状态
private int mPullRefreshState = 0;                         //记录刷新状态
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
 int visibleItemCount, int totalItemCount) {
    if (mCurrentScrollState ==SCROLL_STATE_TOUCH_SCROLL
            && firstVisibleItem == 0
            && (mHeaderLinearLayout.getBottom() >=
 0 && mHeaderLinearLayout.getBottom() < mHeaderHeight)) {
        //进入且仅进入下拉刷新状态
        if (mPullRefreshState == NONE_PULL_REFRESH) {
            mPullRefreshState = ENTER_PULL_REFRESH;
        }
    } else if (mCurrentScrollState ==SCROLL_STATE_TOUCH_SCROLL
            && firstVisibleItem == 0
            && (mHeaderLinearLayout.getBottom() >=
 mHeaderHeight)) {
        //下拉达到界限,进入松手刷新状态
        if (mPullRefreshState ==
 ENTER_PULL_REFRESH || mPullRefreshState == NONE_PULL_REFRESH) {
            mPullRefreshState = OVER_PULL_REFRESH;
            //下面是进入松手刷新状态需要做的一个显示改变
            mDownY = mMoveY;//用于后面的下拉特殊效果
            mHeaderTextView.setText("松手刷新");
            mHeaderPullDownImageView.setVisibility(View.GONE);
            mHeaderReleaseDownImageView.setVisibility
(View.VISIBLE);
        }
    } else if (mCurrentScrollState ==SCROLL_STATE_TOUCH_SCROLL
 && firstVisibleItem != 0) {
        //不刷新了
        if (mPullRefreshState == ENTER_PULL_REFRESH) {
            mPullRefreshState = NONE_PULL_REFRESH;
        }
    } else if (mCurrentScrollState == SCROLL_STATE_FLING &&
 firstVisibleItem == 0) {
        //飞滑状态,不能显示出header,也不能影响正常的飞滑
        //只在正常情况下才纠正位置
        if (mPullRefreshState == NONE_PULL_REFRESH) {
            setSelection(1);
        }
    }
}
  
mPullRefreshState将是后面我们处理边界的重要变量。
6. 下拉效果的特殊处理
    所谓的特殊处理,当header完全显示后,
下拉只按下拉1/3的距离下拉,给用户一种艰难下拉,该松手的弹簧感觉。
    这个在onTouchEvent里处理比较方便:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private float mDownY;
private float mMoveY;
@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //记下按下位置
            //改变
            mDownY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            //移动时手指的位置
            mMoveY = ev.getY();
            if (mPullRefreshState == OVER_PULL_REFRESH) {
                //注意下面的mDownY在onScroll的第二个else中被改变了
                mHeaderLinearLayout.
setPadding(mHeaderLinearLayout.getPaddingLeft(),
                        (int)((mMoveY - mDownY)/3), //1/3距离折扣
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
            }
            break;
        case MotionEvent.ACTION_UP:
            ... ...
            break;
    }
    return super.onTouchEvent(ev);
}
//重复贴出下面这段需要注意的代码
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
 int visibleItemCount, int totalItemCount) {
    ... ...
    else if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
            && firstVisibleItem == 0
            && (mHeaderLinearLayout.getBottom() >=
 mHeaderHeight)) {
        //下拉达到界限,进入松手刷新状态
        if (mPullRefreshState ==
 ENTER_PULL_REFRESH || mPullRefreshState == NONE_PULL_REFRESH) {  
            mPullRefreshState = OVER_PULL_REFRESH;
            mDownY = mMoveY; //为下拉1/3折扣效果记录开始位置
            mHeaderTextView.setText
("松手刷新");//显示松手刷新
            mHeaderPullDownImageView.setVisibility
(View.GONE);//隐藏"下拉刷新"
            mHeaderReleaseDownImageView.setVisibility
  (View.VISIBLE);//显示向上的箭头
        }
    }
    ... ... }
  onScroll里监听到了进入松手刷新状态,
onTouchEvent就开始在ACTION_MOVE中
处理1/3折扣问题。*

*7. 反弹效果的特殊处理
    松手后我们需要一个柔性的反弹效果,
意味着我们弹回去的过程需要分一步步走,
我的解决方案是:
    在子线程里计算PaddingTop,并减少到
原来的3/4,循环通知主线程,
直到PaddingTop小于1(这个值取一个小值,
合适即可)。
    松手后,当然是在onTouchEvent的ACTION_
UP条件下处理比较方便:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//因为涉及到handler数据处理,为方便我们定义如下常量
private final static int REFRESH_BACKING = 0;      //反弹中
private final static int REFRESH_BACED = 1;        //达到刷新界限,
反弹结束后
private final static int REFRESH_RETURN = 2;       //没有达到刷新
界限,返回
private final static int REFRESH_DONE = 3;         //加载数据结束
@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        ... ...
        case MotionEvent.ACTION_UP:
            //when you action up, it will do these:
            //1. roll back util header topPadding is 0
            //2. hide the header by setSelection(1)
            if (mPullRefreshState == OVER_PULL_REFRESH ||
 mPullRefreshState == ENTER_PULL_REFRESH) {
                new Thread() {
                    public void run() {
                        Message msg;
                        while(mHeaderLinearLayout.getPaddingTop() > 1) {
                            msg = mHandler.obtainMessage();
                            msg.what = REFRESH_BACKING;
                            mHandler.sendMessage(msg);
                            try {
                                sleep(5);//慢一点反弹,
别一下子就弹回去了
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        msg = mHandler.obtainMessage();
                        if (mPullRefreshState == OVER_PULL_REFRESH) {
                            msg.what = REFRESH_BACED;//
加载数据完成,结束返回
                        } else {
                            msg.what = REFRESH_RETURN;//
未达到刷新界限,直接返回
                        }
                        mHandler.sendMessage(msg);
                    };
                }.start();
            }
            break;
    }
    return super.onTouchEvent(ev);
}
private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case REFRESH_BACKING:
            mHeaderLinearLayout.setPadding
(mHeaderLinearLayout.getPaddingLeft(),
                    (int) (mHeaderLinearLayout.
getPaddingTop()*0.75f),
                    mHeaderLinearLayout.getPaddingRight(),
                    mHeaderLinearLayout.getPaddingBottom());
            break;
        case REFRESH_BACED:
            mHeaderTextView.setText("正在加载...");
            mHeaderProgressBar.setVisibility(View.VISIBLE);
            mHeaderPullDownImageView.setVisibility(View.GONE);
            mHeaderReleaseDownImageView.setVisibility(View.GONE);
            mPullRefreshState = EXIT_PULL_REFRESH;
            new Thread() {
                public void run() {
                    sleep(2000);//处理后台加载数据
                    Message msg = mHandler.obtainMessage();
                    msg.what = REFRESH_DONE;
                    //通知主线程加载数据完成
                    mHandler.sendMessage(msg);
                };
            }.start();
            break;
        case REFRESH_RETURN:
            //未达到刷新界限,返回
            mHeaderTextView.setText("下拉刷新");
            mHeaderProgressBar.setVisibility(View.INVISIBLE);
            mHeaderPullDownImageView.setVisibility(View.VISIBLE);
            mHeaderReleaseDownImageView.setVisibility(View.GONE);
            mHeaderLinearLayout.setPadding(mHeaderLinearLayout.
getPaddingLeft(),
                    0,
                    mHeaderLinearLayout.getPaddingRight(),
                    mHeaderLinearLayout.getPaddingBottom());
            mPullRefreshState = NONE_PULL_REFRESH;
            setSelection(1);
            break;
        case REFRESH_DONE:
            //刷新结束后,恢复原始默认状态
            mHeaderTextView.setText("下拉刷新");
            mHeaderProgressBar.setVisibility(View.INVISIBLE);
            mHeaderPullDownImageView.setVisibility(View.VISIBLE);
            mHeaderReleaseDownImageView.setVisibility(View.GONE);
            mHeaderUpdateText.setText(getContext().getString(R.string.app_list_header_refresh_last_update, 
                    mSimpleDateFormat.format(new Date())));
            mHeaderLinearLayout.setPadding
(mHeaderLinearLayout.getPaddingLeft(),
                    0,
                    mHeaderLinearLayout.getPaddingRight(),
                    mHeaderLinearLayout.getPaddingBottom());
            mPullRefreshState = NONE_PULL_REFRESH;
            setSelection(1);
            break;
        default:
            break;
        }
    }
};
   
为了一下子看的明确,我把效果中的数据
处理代码也贴出来了。*

*
8. 切入数据加载过程
    上面数据后台处理我们用sleep(2000)来处理,实际处理中,
作为公共组件,我们也不好把具体代码直接写在这里,
我们需要一个更灵活的分离:
    (1). 定义接口
    (2). 注入接口
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//定义接口
public interface RefreshListener {
    Object refreshing();                //加载数据
    void refreshed(Object obj);    //外部可扩展加载完成后的操作
}
//注入接口
private Object mRefreshObject = null; //传值
private RefreshListener mRefreshListener = null;
public void setOnRefreshListener(RefreshListener refreshListener) {
    this.mRefreshListener = refreshListener;
}
//我们需要重写上面的mHandler如下代码
case REFRESH_BACED:
    ... ...
    new Thread() {
        public void run() {
            if (mRefreshListener != null) {
                mRefreshObject = mRefreshListener.refreshing();
            }
            Message msg = mHandler.obtainMessage();
            msg.what = REFRESH_DONE;
            mHandler.sendMessage(msg);
        };
    }.start();
    break;
case REFRESH_DONE:
    ... ...
    mPullRefreshState = NONE_PULL_REFRESH;
    setSelection(1);
    if (mRefreshListener != null) {
        mRefreshListener.refreshed(mRefreshObject);
    }
    break;
    在其他地方我们就可以不修改这个listview组件的代码,使用如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public xxx implements RefreshListener{
  
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //类似如下
        ((RefreshListView) listView).setOnRefreshListener(this);
    }
  
    @Override
    public Object refreshing() {
        String result = null;
        //result = FileUtils.readTextFile(file);
        return result;
    }
  
    @Override
    public void refreshed(Object obj) {
        if (obj != null) {
           //扩展操作
        }
    };
}
  很方便了。*

*9. 扩展"更多"功能
    下拉刷新之外,我们也可以通过相同
方法使用FooterView切入底部"更多"过程,
这里我就不详细说明了
10. 源码
    上面的每段代码都看做是"零部件",需要
组合一下。
    因为我们上面实现了下拉刷新,还增加
了"更多"功能,我们直接命名这个类为
RefreshListView吧:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
package com.tianxia.lib.baseworld.widget;
  
import java.text.SimpleDateFormat;
import java.util.Date;
  
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
  
import com.tianxia.lib.baseworld.R;
  
/**
 * 下拉刷新,底部更多
 *
 */
public class RefreshListView extends ListView
implements OnScrollListener{
  
    private float mDownY;
    private float mMoveY;
  
    private int mHeaderHeight;
  
    private int mCurrentScrollState;
  
    private final static int NONE_PULL_REFRESH = 0;   
//正常状态
    private final static int ENTER_PULL_REFRESH = 1;  
//进入下拉刷新状态
    private final static int OVER_PULL_REFRESH = 2;   
//进入松手刷新状态
    private final static int EXIT_PULL_REFRESH = 3;   
//松手后反弹和加载状态
    private int mPullRefreshState = 0;                
 //记录刷新状态
  
    private final static int REFRESH_BACKING = 0;     
//反弹中
    private final static int REFRESH_BACED = 1;       
//达到刷新界限,反弹结束后
    private final static int REFRESH_RETURN = 2;      
 //没有达到刷新界限,返回
    private final static int REFRESH_DONE = 3;        
//加载数据结束
  
    private LinearLayout mHeaderLinearLayout = null;
    private LinearLayout mFooterLinearLayout = null;
    private TextView mHeaderTextView = null;
    private TextView mHeaderUpdateText = null;
    private ImageView mHeaderPullDownImageView = null;
    private ImageView mHeaderReleaseDownImageView = null;
    private ProgressBar mHeaderProgressBar = null;
    private TextView mFooterTextView = null;
    private ProgressBar mFooterProgressBar = null;
  
    private SimpleDateFormat mSimpleDateFormat;
  
    private Object mRefreshObject = null;
    private RefreshListener mRefreshListener = null;
    public void setOnRefreshListener
(RefreshListener refreshListener) {
        this.mRefreshListener = refreshListener;
    }
  
    public RefreshListView(Context context) {
        this(context, null);
    }
  
    public RefreshListView(Context context,
 AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
  
    void init(final Context context) {
        mHeaderLinearLayout = (LinearLayout)
 LayoutInflater.from(context).inflate(R.layout.
refresh_list_header, null);
        addHeaderView(mHeaderLinearLayout);
        mHeaderTextView = (TextView) findViewById
(R.id.refresh_list_header_text);
        mHeaderUpdateText = (TextView) findViewById(R.id.refresh_list_header_last_update);
        mHeaderPullDownImageView = (ImageView) findViewById(R.id.refresh_list_header_pull_down);
        mHeaderReleaseDownImageView = (ImageView) findViewById(R.id.refresh_list_header_release_up);
        mHeaderProgressBar = (ProgressBar) findViewById(R.id.refresh_list_header_progressbar);
  
        mFooterLinearLayout = (LinearLayout)
 LayoutInflater.from(context).inflate(R.layout.
refresh_list_footer, null);
        addFooterView(mFooterLinearLayout);
        mFooterProgressBar = (ProgressBar) findViewById(R.id.refresh_list_footer_progressbar);
        mFooterTextView = (TextView)
 mFooterLinearLayout.findViewById(R.id.refresh_list_footer_text);
        mFooterLinearLayout.setOnClickListener
(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (context.getString(R.string.
app_list_footer_more).equals(mFooterTextView.getText())) {
                    mFooterTextView.setText
(R.string.app_list_footer_loading);
                    mFooterProgressBar.setVisibility(View.VISIBLE);
                    if (mRefreshListener != null) {
                        mRefreshListener.more();
                    }
                }
            }
        });
  
        setSelection(1);
        setOnScrollListener(this);
        measureView(mHeaderLinearLayout);
        mHeaderHeight = mHeaderLinearLayout.getMeasuredHeight();
  
        mSimpleDateFormat = new SimpleDateFormat
("yyyy-MM-dd hh:mm");
        mHeaderUpdateText.setText(context.getString(R.string.app_list_header_refresh_last_update,
 mSimpleDateFormat.format(new Date())));
    }
  
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                mMoveY = ev.getY();
                if (mPullRefreshState == OVER_PULL_REFRESH) {
                    mHeaderLinearLayout.setPadding
(mHeaderLinearLayout.getPaddingLeft(),
                            (int)((mMoveY - mDownY)/3),
                            mHeaderLinearLayout.getPaddingRight(),
                            mHeaderLinearLayout.getPaddingBottom());
                }
                break;
            case MotionEvent.ACTION_UP:
                //when you action up, it will do these:
                //1. roll back util header topPadding is 0
                //2. hide the header by setSelection(1)
                if (mPullRefreshState == OVER_PULL_REFRESH ||
 mPullRefreshState == ENTER_PULL_REFRESH) {
                    new Thread() {
                        public void run() {
                            Message msg;
                            while(mHeaderLinearLayout.
getPaddingTop() > 1) {
                                msg = mHandler.obtainMessage();
                                msg.what = REFRESH_BACKING;
                                mHandler.sendMessage(msg);
                                try {
                                    sleep(5);
                                } catch
(InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            msg = mHandler.obtainMessage();
                            if (mPullRefreshState ==
 OVER_PULL_REFRESH) {
                                msg.what = REFRESH_BACED;
                            } else {
                                msg.what = REFRESH_RETURN;
                            }
                            mHandler.sendMessage(msg);
                        };
                    }.start();
                }
                break;
        }
        return super.onTouchEvent(ev);
    }
  
    @Override
    public void onScroll(AbsListView view,
 int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL
                && firstVisibleItem == 0
                && (mHeaderLinearLayout.getBottom() >=
 0 && mHeaderLinearLayout.getBottom() < mHeaderHeight)) {
            //进入且仅进入下拉刷新状态
            if (mPullRefreshState == NONE_PULL_REFRESH) {
                mPullRefreshState = ENTER_PULL_REFRESH;
            }
        } else if (mCurrentScrollState ==
 SCROLL_STATE_TOUCH_SCROLL
                && firstVisibleItem == 0
                && (mHeaderLinearLayout.getBottom() >=
 mHeaderHeight)) {
            //下拉达到界限,进入松手刷新状态
            if (mPullRefreshState ==
 ENTER_PULL_REFRESH || mPullRefreshState == NONE_PULL_REFRESH) {
                mPullRefreshState = OVER_PULL_REFRESH;
                mDownY = mMoveY; //为下拉1/3折扣效果记录开始位置
                mHeaderTextView.setText("松手刷新");//显示松手刷新
                mHeaderPullDownImageView.setVisibility
(View.GONE);//隐藏"下拉刷新"
                mHeaderReleaseDownImageView.setVisibility
(View.VISIBLE);//显示向上的箭头
            }
        } else if (mCurrentScrollState ==
 SCROLL_STATE_TOUCH_SCROLL && firstVisibleItem != 0) {
            //不刷新了
            if (mPullRefreshState == ENTER_PULL_REFRESH) {
                mPullRefreshState = NONE_PULL_REFRESH;
            }
        } else if (mCurrentScrollState ==
 SCROLL_STATE_FLING && firstVisibleItem == 0) {
            //飞滑状态,不能显示出header,也不能影响正常的飞滑
            //只在正常情况下才纠正位置
            if (mPullRefreshState == NONE_PULL_REFRESH) {
                setSelection(1);
            }
        }
    }
  
    @Override
    public void onScrollStateChanged
(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;
    }
  
    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(adapter);
        setSelection(1);
    }
  
    private void measureView(View child) {
        ViewGroup.LayoutParams p = child.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams
(ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }
  
        int childWidthSpec = ViewGroup.getChildMeasureSpec
(0, 0 + 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec =
MeasureSpec.makeMeasureSpec(lpHeight,
                    MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,
                    MeasureSpec.UNSPECIFIED);
        }
        child.measure(childWidthSpec, childHeightSpec);
    }
  
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case REFRESH_BACKING:
                mHeaderLinearLayout.setPadding
(mHeaderLinearLayout.getPaddingLeft(),
                        (int) (mHeaderLinearLayout.
getPaddingTop()*0.75f),
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
                break;
            case REFRESH_BACED:
                mHeaderTextView.setText("正在加载...");
                mHeaderProgressBar.setVisibility(View.VISIBLE);
                mHeaderPullDownImageView.setVisibility(View.GONE);
                mHeaderReleaseDownImageView.setVisibility(View.GONE);
                mPullRefreshState = EXIT_PULL_REFRESH;
                new Thread() {
                    public void run() {
                        if (mRefreshListener != null) {
                            mRefreshObject =
 mRefreshListener.refreshing();
                        }
                        Message msg = mHandler.obtainMessage();
                        msg.what = REFRESH_DONE;
                        mHandler.sendMessage(msg);
                    };
                }.start();
                break;
            case REFRESH_RETURN:
                mHeaderTextView.setText("下拉刷新");
                mHeaderProgressBar.setVisibility(View.INVISIBLE);
                mHeaderPullDownImageView.setVisibility(View.VISIBLE);
                mHeaderReleaseDownImageView.setVisibility(View.GONE);
                mHeaderLinearLayout.setPadding
(mHeaderLinearLayout.getPaddingLeft(),
                        0,
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
                mPullRefreshState = NONE_PULL_REFRESH;
                setSelection(1);
                break;
            case REFRESH_DONE:
                mHeaderTextView.setText("下拉刷新");
                mHeaderProgressBar.setVisibility(View.INVISIBLE);
                mHeaderPullDownImageView.setVisibility(View.VISIBLE);
                mHeaderReleaseDownImageView.setVisibility(View.GONE);
                mHeaderUpdateText.setText(getContext().getString(R.string.app_list_header_refresh_last_update,
                        mSimpleDateFormat.format(new Date())));
                mHeaderLinearLayout.setPadding
(mHeaderLinearLayout.getPaddingLeft(),
                        0,
                        mHeaderLinearLayout.getPaddingRight(),
                        mHeaderLinearLayout.getPaddingBottom());
                mPullRefreshState = NONE_PULL_REFRESH;
                setSelection(1);
                if (mRefreshListener != null) {
                    mRefreshListener.refreshed(mRefreshObject);
                }
                break;
            default:
                break;
            }
        }
    };
    public interface RefreshListener {
        Object refreshing();
        void refreshed(Object obj);
        void more();
    }
  
    public void finishFootView() {
        mFooterProgressBar.setVisibility(View.GONE);
        mFooterTextView.setText(R.string.app_list_footer_more);
    }
  
    public void addFootView() {
        if (getFooterViewsCount() == 0) {
            addFooterView(mFooterLinearLayout);
        }
    }
  
    public void removeFootView() {
        removeFooterView(mFooterLinearLayout);
    }
}
**
&这个只是一个原型,无论代码风格和逻辑处理&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&