• Android自己定义控件--下拉刷新的实现


    我们在使用ListView的时候。非常多情况下须要用到下拉刷新的功能。为了了解下拉刷新的底层实现原理,我採用自己定义ListView控件的方式来实现效果。

    实现的基本原理是:自己定义ListView,给ListView载入头布局。然后动态的控制头布局的现实与隐藏。ListView初始化的时候,头布局是隐藏的,当手指往下拉的时候,依据手指移动的距离与头布局的高度的关系来控制头布局的显示。具体的控制思路详见后边的代码。代码中的凝视非常具体。先来看一下效果图:

                              

    头布局的实现代码例如以下:

    <?

    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="match_parent" android:orientation="horizontal" > <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="5dp" android:padding="5dp" > <ImageView android:id="@+id/iv_arror" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:padding="10dp" android:src="@drawable/refresh_listview_arrow" /> <ProgressBar android:id="@+id/pb_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:indeterminateDrawable="@drawable/custom_progress" android:visibility="invisible" > </ProgressBar> </RelativeLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginLeft="25dp" android:layout_marginTop="12dp" android:orientation="vertical" android:padding="5dp" > <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="12dp" android:text="下拉刷新" android:textColor="#FF0000" android:textSize="20sp" /> <TextView android:id="@+id/tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="3dp" android:text="2015-03-10 17:07:07" android:textColor="@android:color/darker_gray" android:textSize="16sp" /> </LinearLayout> </LinearLayout>

    接着定义一个RefreshListView,继承自ListView,重写它的三个构造方法。在RefreshlistView初始化的时候,载入一个头布局。这个头布局默认是隐藏的。

    <span style="white-space:pre">	</span>// 初始化头布局
    	private void initHeaderView() {
    		// 给当前的ListView加入下拉刷新的头布局
    		mHeaderView = View.inflate(getContext(), R.layout.list_refresh_header,
    				null);
    		tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title);
    		tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time);
    		// 下拉箭头
    		ivArror = (ImageView) mHeaderView.findViewById(R.id.iv_arror);
    		// 进度条
    		pb = (ProgressBar) mHeaderView.findViewById(R.id.pb_list);
    
    		this.addHeaderView(mHeaderView);
    		// 初始化这个下拉刷新的ListView的时候,县隐藏掉下拉刷新的头布局,然后通过代码动态的控制头布局的显示与隐藏
    		// 隐藏头布局
    		// 先測量一把头布局
    		mHeaderView.measure(0, 0);
    		// 获得測量得到的高度
    		mHeaderViewHeight = mHeaderView.getMeasuredHeight();
    		// 设置paddingtop为负高度,隐藏头布局
    		mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
    
    		// 初始化完毕布局,初始化两个动画效果
    
    		initAnimation();
    		// 设置当前时间
    		tvTime.setText("最后刷新时间:" + getCurrentTime());
    	}

    当手指触摸屏幕时。触发OnTouchEvent事件,依据手指在Y轴方向上的移动偏移量来动态的控制头布局的显示与隐藏。并即时更新下拉框的显示的状态。详细的代码例如以下:

    <span style="white-space:pre">	</span>/**
    	 * 下拉屏幕的时候。将头布局展示出来,重写一下ListView的触摸屏幕的事件
    	 */
    	@Override
    	public boolean onTouchEvent(MotionEvent ev) {
    		switch (ev.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			// 手指按下的时候获得当前屏幕的Y坐标
    			startY = (int) ev.getRawY();
    
    			break;
    		case MotionEvent.ACTION_MOVE:
    			// 手指移动的时候。获得当前屏幕的Y坐标,与開始的Y坐标做比較
    
    			if (startY == -1) {
    				// 确保startY有效
    				startY = (int) ev.getRawY();
    			}
    
    			int endY = (int) ev.getRawY();
    			// 屏幕Y方向移动的距离
    			int dy = endY - startY;
    			// 仅仅有屏幕向下滑,也就是dy>0,而且当前的ListView显示的是第一个条目。那个隐藏的下拉刷新的头布局才慢慢的随手指滑出来
    			if (dy > 0 && this.getFirstVisiblePosition() == 0) {
    				// 同意下拉刷新框出来
    				// 下拉刷新框出来事实上就是给头布局设置padding。首先依据手指移动的距离计算一下padding
    				int padding = dy - mHeaderViewHeight;//
    				mHeaderView.setPadding(0, padding, 0, 0);
    				// 更新刷新框的状态
    				// 一共同拥有三种状态,下拉刷新、正在刷新、松开刷新
    				// 定义这三种状态
    				// 假设padding大于0说明下拉刷新的框已经所有拉出来了,将状态改为松开刷新
    				if (padding > 0 && mCurrentState != STATE_RELEASE_REFRESH) {
    					// 状态改为松开刷新,将下拉框显示的状态更新一下
    					mCurrentState = STATE_RELEASE_REFRESH;
    					// 更新当前下拉框显示的状态
    					updateState();
    				}
    				if (padding < 0 && mCurrentState != STATE_PULL_REFRESH) {
    					// 状态改为下拉刷新
    					mCurrentState = STATE_PULL_REFRESH;
    					updateState();
    				}
    
    			}
    
    			break;
    		case MotionEvent.ACTION_UP:
    			// 手指抬起来的时候重置startY。

    将状态改为正在刷新 startY = -1; if (mCurrentState == STATE_RELEASE_REFRESH) { // 假设当前为松开刷新,则手指一抬起来就将状态改为正在 mCurrentState = STATE_REFRESHING; // 头布局的padding设置为0000 mHeaderView.setPadding(0, 0, 0, 0); updateState(); } else { // 没有全然拉出来。就将头布局隐藏 mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); } break; } return super.onTouchEvent(ev); } /** * 更新当前下拉框显示的状态 */ private void updateState() { // 推断当前状态是什么,依据当前状态更新下拉框显示 switch (mCurrentState) { case STATE_PULL_REFRESH: // 下拉刷新 tvTitle.setText("下拉刷新"); ivArror.setVisibility(View.VISIBLE); pb.setVisibility(View.INVISIBLE); // 给箭头设置动画 ivArror.startAnimation(animDown); break; case STATE_REFRESHING: // 正在刷新 tvTitle.setText("正在刷新..."); ivArror.clearAnimation();// 必须先清除动画,才干隐藏 ivArror.setVisibility(View.INVISIBLE); pb.setVisibility(View.VISIBLE); // 正在刷新的时候调用接口中的方法 if (mListener != null) { // 正在刷新 mListener.onRefresh(); } break; case STATE_RELEASE_REFRESH: // 松开刷新 tvTitle.setText("松开刷新"); ivArror.setVisibility(View.VISIBLE); pb.setVisibility(View.INVISIBLE); ivArror.startAnimation(animUp); break; } }

    为了监听下拉刷新的动作,自己定义一个监听接口。当正在刷新的时候运行onRefresh方法。对外提供一个设置监听的方法,监听器由子类实现。详细的正在刷新运行的逻辑由子类在onRefresh中实现。

    // 下拉刷新逻辑的实现方式:给当前的ListView设置下拉刷新的监听器,监听器的本质就是一个接口
    	/**
    	 * 下拉刷新的监听器
    	 * 
    	 * @author ZHY
    	 * 
    	 */
    	public interface onRefreshListener {
    		// 刷新的时候运行的方法,谁实现这个接口。谁运行此方法
    		public void onRefresh();
    
    	}
    
    	onRefreshListener mListener;
    
    	// 有了监听器之后,提供一个设置监听的方法
    	public void setOnRefreshListener(onRefreshListener listener) {
    		// 这个Listneer是从子类中闯过来的,是子类定义的监听
    		mListener = listener;
    	}

    刷新完毕之后,改变下拉刷新的状态,而且隐藏下拉框

    <span style="white-space:pre">	</span>// 刷新完毕的时候。将下拉框隐藏
    	/**
    	 * 调用server的数据刷新。刷新完毕之后调用的方法 载入完毕之后的回调
    	 * 
    	 * @param isSuccess
    	 */
    	public void onRefreshCompleted(boolean isSuccess) {
    
    		// 将状态改成下拉刷新,然后隐藏下拉框
    		mCurrentState = STATE_PULL_REFRESH;
    		tvTitle.setText("下拉刷新");
    		ivArror.setVisibility(View.VISIBLE);
    		pb.setVisibility(View.INVISIBLE);
    
    		mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏
    		// 假设刷新成功,则更新最后一次刷新时间,否则不更新时间
    		if (isSuccess) {
    			tvTime.setText("最后刷新时间:" + getCurrentTime());
    
    		}
    
    	}
    

    这一部分工作做完之后。一个能够实现下拉刷新功能的ListView就定义好了。在MainActivity中就能够直接使用了

    源代码下载


  • 相关阅读:
    Linux内核之 I/O多路复用
    Linux内核之 页高速缓存与页回写
    Linux内核之 块I/O层及I/O调度
    Linux内核之 文件I/O
    C++雾中风景15:聊聊让人抓狂的Name Mangling
    音频数据增强及python实现
    深度学习中“过拟合”的产生原因和解决方法
    资料-资源(不定期更新)
    论文翻译:2020_Acoustic Echo Cancellation Challenge Datasets And Testingframework
    语音信号处理概念
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7227799.html
Copyright © 2020-2023  润新知