Android学习(44) -- 自定义控件(8) 下拉刷新(添加header)

来源:互联网 发布:淘宝鉴权失败什么意思 编辑:程序博客网 时间:2024/06/01 13:45


1、定义HeaderView的布局

2、创建一个类去继承ListView,并重写两个构造器

3、在MainActivity中使用自定义ListView展示数据

4、在自定义ListView中将HeaderView添加到ListView上部

5、实现onTouch方法

1、自定义HeaderView   

layout_header.xml




自定义ProgressBar

1、定义一个动画

<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android"    android:drawable="@drawable/indicate_rotate"    android:fromDegrees="0"    android:pivotX="50%"    android:pivotY="50%"    android:toDegrees="360" ></rotate>


2、在布局中使用

        <ProgressBar            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:indeterminate="true"            android:indeterminateDuration="1000"            android:indeterminateDrawable="@drawable/indeterminate_drawable" />

3、整体实现

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:gravity="center_horizontal"    android:orientation="horizontal" >    <RelativeLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginBottom="10dp"        android:layout_marginTop="10dp" >        <ImageView            android:id="@+id/iv_arrow"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:background="@drawable/indicator_arrow" />        <ProgressBar            android:id="@+id/pb_rotate"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:indeterminate="true"            android:indeterminateDrawable="@drawable/indeterminate_drawable"            android:indeterminateDuration="1000"            android:visibility="invisible" />    </RelativeLayout>    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout_marginBottom="10dp"        android:layout_marginLeft="15dp"        android:layout_marginTop="10dp"        android:orientation="vertical" >        <TextView            android:id="@+id/tv_state"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="下拉刷新"            android:textColor="#aa000000"            android:textSize="20sp" />        <TextView            android:id="@+id/tv_time"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="最后刷新:2016-5-5"            android:textColor="@android:color/darker_gray"            android:textSize="14sp" />    </LinearLayout></LinearLayout>




2、自定义ListView中将HeaderView添加到ListView上部

1、自定义ListView
2、 将HeaderView添加到ListView上部
      a、使用 addHeaderView(v)  将一个View将添加到ListView的顶部   需要在setAdapter之前使用
      b、隐藏HeaderView    将paddingTop设置一个headerView高度的负值去 隐藏HeaderView
      c、动态的获取  计算HeaderView的高度,有两种方式:

getMeasuredHeight():获取测量完的高度,只要在onMeasure方法执行完,就可以用
                   它获取到宽高,在自定义控件内部多使用这个使用view.measure(0,0)方法可以主动通知系统去测量,然后就可以直接使用它获取宽高

//获取头部的布局//final View headerView = View.inflate(MainActivity.this,//R.layout.layout_header, null);//  第 一种   获取动态 自定义组件的宽 高 //headerView.measure(0, 0); //自动的通知系统获取高度和宽度//int headerViewHeight = headerView.getMeasuredHeight();////将头部进行隐藏//headerView.setPadding(0, -headerViewHeight, 0, 0);//refLv.addHeaderView(headerView);



getHeight():必须在onLayout方法执行完后,才能获得宽高
//第二种   获取自动加载View的高度的方式  使用监听的方式 ////使用inflate是异步的  要想获取到加载组件的高度 需要使用 getViewTreeObserver 进行监听//headerView.getViewTreeObserver().addOnGlobalLayoutListener(//new OnGlobalLayoutListener() {////@Override//public void onGlobalLayout() {//////要先remove掉一下//headerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);//////获取头部布局的高度//int headerViewHeight = headerView.getHeight();////将头部进行隐藏//headerView.setPadding(0, -headerViewHeight, 0, 0);//refLv.addHeaderView(headerView);//}//});



3、代码

public class RefreshListView extends ListView {private View headerView;//HeaderViewprivate int headerViewHeight;//HeaderViewHeightpublic RefreshListView(Context context, AttributeSet attrs) {super(context, attrs);}public RefreshListView(Context context) {super(context);}/** *  */private void init(){initHeaderView();}/** * 添加一个HeaderView */private void initHeaderView() {//获取头部的布局headerView = View.inflate(getContext(),R.layout.layout_header, null);//获取自定义组件的宽 高headerView.measure(0, 0);headerViewHeight = headerView.getMeasuredHeight();//将头部进行隐藏headerView.setPadding(0, -headerViewHeight, 0, 0);//将头部添加到RefreshListView中addHeaderView(headerView);}}

3、使用onTouchEvent处理下拉

1、定义一个变量 downY  用于获取按下时  Y轴的坐标

2、在ACTION_MOVE中,计算出下拉时 HeaderView的 Y轴的坐标值

      用移动到的点值 减去 按下时点的值  得到移动的距离

      用 -headerViewHeight 加上 得到移动距离  就是新的headerViewHeight

     只有当顶部的数据是第一条Item的时候  并且 paddingTop > -headerViewHeight 的时候 进行设置  setPadding ------

     用新的值 设置setPadding即可 看到HeaderView下拉的效果   

3、------当下拉时,点击的是item 2下拉的时候是没有问题的,当向上滑动的时候 触发的item 为6 ,也就是说上滑的 速度明显比较快,因为ListView会自动监听垂直滑动,所以会滚动比较快,我们要想实现向下向上都是 同一个item的话 ,需要在ACTION_DOWN中设置 true, 拦截TouchMove,不让listview处理该次move事件,会造成listview无法滑动


@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downY = (int) ev.getY();break;case MotionEvent.ACTION_MOVE://获取到移动的大小int delaY = (int)ev.getY() - downY ;//计算出新的headerViewHeightint paddingTop = -headerViewHeight + delaY;if(paddingTop > -headerViewHeight && getFirstVisiblePosition() == 0){headerView.setPadding(0, paddingTop, 0, 0);//拦截TouchMove,不让listview处理该次move事件,会造成listview无法滑动return true;}break;case MotionEvent.ACTION_UP:break;}return super.onTouchEvent(ev);}



4、下拉刷新的三种状态以及动画

      a、定义三个常量 并设置默认状态为下拉刷新状态

private final int PULL_REFRESH = 0;//下拉刷新的状态private final int RELEASE_REFRESH = 1;//松开刷新的状态private final int REFRESHING = 2;//正在刷新的状态private int currentState = PULL_REFRESH;


   b、对子控件进行初始化,

   c、    定义RotateAnimation动画   定义两个旋转动画变量, 并定义一个初始化动画的方法,用于初始化动画

      

//旋转动画private RotateAnimation  upAnimation, downAnimation; /** * 初始化动画 */private void initRotateAnimation() {//向上的动画upAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);upAnimation.setDuration(300);upAnimation.setFillAfter(true);//向下的动画downAnimation = new RotateAnimation( -180, -360,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);downAnimation.setDuration(300);downAnimation.setFillAfter(true);}


   d、定义方法用于根据状态进行更新HeaderView

        当下拉刷新状态时,文本设置为 下拉刷新 ,设置向下的动画

       当松开刷新状态时,文本设置为 松开刷新 ,设置向上的动画

当正在刷新时,清除动画,ImageView隐藏,ProgressBar显示, 文本设置为 正在刷新  

  

/** * 根据  currentState 来更新 HeaderView */private void refreshHeaderView(){switch (currentState) {case PULL_REFRESH: //下拉刷新的状态tv_state.setText("下拉刷新");iv_arrow.startAnimation(downAnimation);break;case RELEASE_REFRESH: //松开刷新的状态tv_state.setText("松开刷新");iv_arrow.startAnimation(upAnimation);break;case REFRESHING://正在刷新的状态//清除动画  可能动画未执行完毕  所以需要清除动画iv_arrow.clearAnimation();//让图标隐藏iv_arrow.setVisibility(View.INVISIBLE);//ProgressBar 显示可见pb_rotate.setVisibility(View.VISIBLE);tv_state.setText("正在刷新...");break;}}

e、     在 onTouchEvent方法中 的ACTION_MOVE, 

当新的HeaderViewHeight的值 paddingTop >=0 并且 当前状态是 PULL_REFRESH 的时候,由下拉刷新 进入到 松开刷新状态 ,并调用根据currentState更新UI方法refreshHeaderView()

        当新的HeaderViewHeight的值 paddingTop< 0 并且 当前状态是 RELEASE_REFRESH的时候,由松开刷新 进入到 下拉刷新状态 并调用根据currentState更新UI方法refreshHeaderView()

   

           在 onTouchEvent方法中 的ACTION_UP,有两种状态

a、当currentState为下拉刷新状态,则隐藏HeaderView

b、当 currentState为松开刷新状态,则显示HeaderView,并且将currentState重置为刷新中的状态,并调用根据currentState更新UI的方法

  

           在 onTouchEvent方法中 的ACTION_MOVE,当currentState 为REFRESHING的时候,终止操作。

public boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downY = (int) ev.getY();break;case MotionEvent.ACTION_MOVE://如果在刷新中的话 就不让操作了if(currentState == REFRESHING){break;}//获取到移动的大小int delaY = (int)(ev.getY() - downY) ;//计算出新的headerViewHeightint paddingTop = -headerViewHeight + delaY;if(paddingTop > -headerViewHeight && getFirstVisiblePosition() ==  0){headerView.setPadding(0, paddingTop, 0, 0);if(paddingTop >= 0  &¤tState == PULL_REFRESH){//下拉刷新状态 进入松开刷新状态currentState = RELEASE_REFRESH;refreshHeaderView();}else if(paddingTop < 0&& currentState == RELEASE_REFRESH){//松开刷新状态 进入下拉刷新状态currentState = PULL_REFRESH;refreshHeaderView();}//拦截TouchMove,不让listview处理该次move事件,会造成listview无法滑动return true;}break;case MotionEvent.ACTION_UP:if(currentState == PULL_REFRESH){//隐藏HeaderViewheaderView.setPadding(0, -headerViewHeight, 0, 0);}else if(currentState == RELEASE_REFRESH){headerView.setPadding(0, 0, 0, 0);currentState = REFRESHING;refreshHeaderView();//模拟延时//new Handler().postDelayed(new Runnable() {////@Override//public void run() {//completeRefresh();//}//}, 3000); }break;}return super.onTouchEvent(ev);}

f、完成刷新后,重置下拉刷新状态,要在请求完数据,更新Adapter之后,在UI线程中调用该方法,在e中 模拟调用了该方法 ---ACTION_UP中

/** * 完成刷新 重置为下拉刷新状态 * 在请求完数据 更新Adapter之后  在UI线程中调用该方法 */public void completeRefresh(){//隐藏HeaderViewheaderView.setPadding(0, -headerViewHeight, 0, 0);//状态改变currentState = PULL_REFRESH;//ProgressBar隐藏pb_rotate.setVisibility(View.INVISIBLE);//图片显示iv_arrow.setVisibility(View.VISIBLE);tv_state.setText("下拉刷新");tv_time.setText("最后刷新: " + getCurrentTime());}/** * 获取当前时间 * @return */private String getCurrentTime(){SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");return format.format(new Date());}


5、对外提供一个接口,用于处理刷新过程中业务逻辑请求的处理

/** * 定义刷新监听接口,用于处理刷新过程中业务逻辑请求的处理 * @author Denny * @date 2016-4-26 */public interface OnRefreshListener{//下来刷新的时候调用void onPullRefresh();}private OnRefreshListener  listener;public void setOnRefreshListener( OnRefreshListener listener){this.listener = listener;}


4、在MainActivity中实现调用

1、定义一个模拟请求数据 通过Handler更新UI的方法   在Handler更新UI后 重置状态

private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { //更新UI myAdapter.notifyDataSetChanged();//重置状态refLv.completeRefresh(); };};       //模拟向服务器请求数据private void requestDataFromServer(){new Thread(){public void run() {//模拟请求服务的一个时间长度SystemClock.sleep(3000);//将数据添加到头部list.add(0, "下拉刷新的数据");//更新UIhandler.sendEmptyMessage(0);};}.start();}

2、通过调用setOnRefreshListener来实现  请求服务器数据,然后更新UI

 

refLv.setOnRefreshListener(new OnRefreshListener() {public void onPullRefresh() {//Log.e("MainActivity", "进入正在刷新状态,此时应请求服务器数据");//需要联网请求服务器数据,然后更新UIrequestDataFromServer();}});

完整代码

MainActivity

public class MainActivity extends Activity {private RefreshListView refLv;private ArrayList<String> list = new ArrayList<String>();private MyAdapter myAdapter;private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { //更新UI myAdapter.notifyDataSetChanged();//重置状态refLv.completeRefresh(); };};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initView();initData();}/** * 初始化View */private void initView() {setContentView(R.layout.activity_main);refLv = (RefreshListView) findViewById(R.id.refLv);}/** * 初始化数据 */private void initData() {for (int i = 0; i < 15; i++) {list.add("ListView原来数据 :" + i);}//获取头部的布局//final View headerView = View.inflate(MainActivity.this,//R.layout.layout_header, null);//1、第一种   获取自动加载View的高度的方式  使用监听的方式 ////使用inflate是异步的  要想获取到加载组件的高度 需要使用 getViewTreeObserver 进行监听//headerView.getViewTreeObserver().addOnGlobalLayoutListener(//new OnGlobalLayoutListener() {////@Override//public void onGlobalLayout() {//////要先remove掉一下//headerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);//////获取头部布局的高度//int headerViewHeight = headerView.getHeight();////将头部进行隐藏//headerView.setPadding(0, -headerViewHeight, 0, 0);//refLv.addHeaderView(headerView);//}//});//  第 一种   获取动态 自定义组件的宽 高 //headerView.measure(0, 0); //自动的通知系统获取高度和宽度//int headerViewHeight = headerView.getMeasuredHeight();////将头部进行隐藏//headerView.setPadding(0, -headerViewHeight, 0, 0);//refLv.addHeaderView(headerView);myAdapter = new MyAdapter();refLv.setAdapter(myAdapter);refLv.setOnRefreshListener(new OnRefreshListener() {public void onPullRefresh() {//Log.e("MainActivity", "进入正在刷新状态,此时应请求服务器数据");//需要联网请求服务器数据,然后更新UIrequestDataFromServer();}});}//模拟向服务器请求数据private void requestDataFromServer(){new Thread(){public void run() {//模拟请求服务的一个时间长度SystemClock.sleep(3000);//将数据添加到头部list.add(0, "下拉刷新的数据");//更新UIhandler.sendEmptyMessage(0);};}.start();}class MyAdapter extends BaseAdapter {@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {TextView tv = new TextView(MainActivity.this);tv.setPadding(20, 20, 20, 20);tv.setTextSize(18);tv.setText(list.get(position));return tv;}}}


RefreshListView
public class RefreshListView extends ListView {private View headerView;//HeaderViewprivate int headerViewHeight;//HeaderViewHeightprivate int downY ;//按下时 的 Y坐标点  private final int PULL_REFRESH = 0;//下拉刷新的状态private final int RELEASE_REFRESH = 1;//松开刷新的状态private final int REFRESHING = 2;//正在刷新的状态private int currentState = PULL_REFRESH;private ImageView iv_arrow;private ProgressBar pb_rotate;private TextView tv_state;private TextView tv_time;//旋转动画private RotateAnimation  upAnimation, downAnimation; public RefreshListView(Context context, AttributeSet attrs) {super(context, attrs);init();}public RefreshListView(Context context) {super(context);init();}/** * 初始化ListView中的数据 */private void init(){//初始化HeaderViewinitHeaderView();//初始化动画initRotateAnimation();}/** * 初始化动画 */private void initRotateAnimation() {//向上的动画upAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);upAnimation.setDuration(300);upAnimation.setFillAfter(true);//向下的动画downAnimation = new RotateAnimation( -180, -360,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);downAnimation.setDuration(300);downAnimation.setFillAfter(true);}/** * 添加一个HeaderView */private void initHeaderView() {//获取头部的布局headerView = View.inflate(getContext(),R.layout.layout_header, null);iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow);pb_rotate = (ProgressBar) headerView.findViewById(R.id.pb_rotate);tv_state = (TextView) headerView.findViewById(R.id.tv_state);tv_time = (TextView) headerView.findViewById(R.id.tv_time);//获取自定义组件的宽 高headerView.measure(0, 0);headerViewHeight = headerView.getMeasuredHeight();//将头部进行隐藏headerView.setPadding(0, -headerViewHeight, 0, 0);//将头部添加到RefreshListView中addHeaderView(headerView);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:downY = (int) ev.getY();break;case MotionEvent.ACTION_MOVE://如果在刷新中的话 就不让操作了if(currentState == REFRESHING){break;}//获取到移动的大小int delaY = (int)(ev.getY() - downY) ;//计算出新的headerViewHeightint paddingTop = -headerViewHeight + delaY;if(paddingTop > -headerViewHeight && getFirstVisiblePosition() ==  0){headerView.setPadding(0, paddingTop, 0, 0);if(paddingTop >= 0  &¤tState == PULL_REFRESH){//下拉刷新状态 进入松开刷新状态currentState = RELEASE_REFRESH;refreshHeaderView();}else if(paddingTop < 0&& currentState == RELEASE_REFRESH){//松开刷新状态 进入下拉刷新状态currentState = PULL_REFRESH;refreshHeaderView();}//拦截TouchMove,不让listview处理该次move事件,会造成listview无法滑动return true;}break;case MotionEvent.ACTION_UP:if(currentState == PULL_REFRESH){//隐藏HeaderViewheaderView.setPadding(0, -headerViewHeight, 0, 0);}else if(currentState == RELEASE_REFRESH){headerView.setPadding(0, 0, 0, 0);currentState = REFRESHING;refreshHeaderView();//模拟延时//new Handler().postDelayed(new Runnable() {////@Override//public void run() {//completeRefresh();//}//}, 3000);if(listener != null){listener.onPullRefresh();}}break;}return super.onTouchEvent(ev);}/** * 根据  currentState 来更新 HeaderView */private void refreshHeaderView(){switch (currentState) {case PULL_REFRESH: //下拉刷新的状态tv_state.setText("下拉刷新");iv_arrow.startAnimation(downAnimation);break;case RELEASE_REFRESH: //松开刷新的状态tv_state.setText("松开刷新");iv_arrow.startAnimation(upAnimation);break;case REFRESHING://正在刷新的状态//清除动画  可能动画未执行完毕  所以需要清除动画iv_arrow.clearAnimation();//让图标隐藏iv_arrow.setVisibility(View.INVISIBLE);//ProgressBar 显示可见pb_rotate.setVisibility(View.VISIBLE);tv_state.setText("正在刷新...");break;}}/** * 完成刷新 重置为下拉刷新状态 * 在请求完数据 更新Adapter之后  在UI线程中调用该方法 */public void completeRefresh(){//隐藏HeaderViewheaderView.setPadding(0, -headerViewHeight, 0, 0);//状态改变currentState = PULL_REFRESH;//ProgressBar隐藏pb_rotate.setVisibility(View.INVISIBLE);//图片显示iv_arrow.setVisibility(View.VISIBLE);tv_state.setText("下拉刷新");tv_time.setText("最后刷新: " + getCurrentTime());}/** * 获取当前时间 * @return */private String getCurrentTime(){SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");return format.format(new Date());}/** * 定义刷新监听接口,用于处理刷新过程中业务逻辑请求的处理 * @author Denny * @date 2016-4-26 */public interface OnRefreshListener{//下来刷新的时候调用void onPullRefresh();}private OnRefreshListener  listener;public void setOnRefreshListener( OnRefreshListener listener){this.listener = listener;}}




0 0
原创粉丝点击