您的位置:首页 > 移动开发 > Android开发

android之我自己实现的下拉刷新

2015-07-26 22:05 501 查看
在上周的时候,我在针对我想要的问题研究了一下PullToRefreshListView的使用之后,我说过要自己去实现一个下拉刷新,其实PullToRefreshListView一般来说够用了,只是当在我们的项目中出现比较私人的定制时,就显得比较复杂了,所以这周我花了点时间自己写了一个下拉刷新,由于我不怎么写界面,所以写的速度自然就没有那么快,不过至少完成了,下面就分享一下我的下拉刷新的代码吧,代码中的注释还是比较充足的

首先是布局部分layout_refresh_headview.xml,写的是下拉刷新的头部分

<?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" >
    
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        >
    <!-- 左边的加载图标 -->
    <RelativeLayout 
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:paddingRight="10dp"
        >
        <ImageView 
        android:id="@+id/layout_refresh_headview_image_load"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:src="@drawable/ic_launcher"
        android:visibility="visible"
        />
        
        <ProgressBar 
            android:id="@+id/layout_refresh_headview_progress_load"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:visibility="gone"
            />
    </RelativeLayout>
    <!-- 右边的加载文字 -->
    <LinearLayout 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >
        <TextView 
            android:id="@+id/layout_refresh_headview_text_load"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="load_text"
            />
        <TextView 
            android:id="@+id/layout_refresh_headview_text_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="last_load_time"
            />
        
    </LinearLayout>
    </LinearLayout>
    

</LinearLayout>
然后是头部分包括集合的,基本的思路是让调用者可以当做一个带有下拉刷新的控件使用,调用起来独立一点

layout_my_pulltorefresh_listview.xml

<?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="vertical" >
    
    <include 
        android:id="@+id/pull_to_refresh_head"
        layout="@layout/layout_refresh_headview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    <ListView 
        android:id="@+id/layout_my_pulltorefresh_listview_listview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:divider="@drawable/ic_launcher"
        android:dividerHeight="1dp"
        ></ListView>

</LinearLayout>
布局文件写好之后就是关键的代码了,下面是我写的自定义类

package com.example.mypulltorefresh;

import java.util.ArrayList;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.animation.RotateAnimation;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class YPullToRefreshListView extends LinearLayout implements OnTouchListener{

	//下拉刷新的头部视图
	private View mHeadView;
	//头部视图的参数
	private LayoutParams mHeadViewParams;
	//加载时的进度条
	private ProgressBar mLoadingProgress;
	//加载时的图片
	private ImageView mLoadingImage;
	//加载时的提示文字
	private TextView mLoadingText;
	//上一次加载的时间
	private TextView mLastLoadingTime;
	//上一次刷新的时间值
	private String mLastLoadTimeValue = "";
	
	
	//判定是手指有移动的最小值
	private int mMoveJudgeValue;
	//头部View的高度
	private int mHeadViewHight;
	
	
	//显示的ListView
	private ListView mListView;
	//支持显示的ListView的数据
	private ArrayList<String> mListViewData = new ArrayList<String>();
	//支持显示的adapter
	private ArrayAdapter<String> mListViewAdapter;
	
	//手指落下的y坐标
	private float mDownY;
	
	//此时放开即可刷新
	private static final String PULL_TO_REFRESH = "pull_to_refresh";
	//此时正在刷新
	private static final String REFRESHING = "refreshing";
	//此时放开取消刷新
	private static final String PULL_TO_CALCEL_REFRESH = "pull_to_calcel_refresh";
	//刷新结束
	private static final String REFRESH_FINISHED = "refresh_finished";
	
	private String mCurrentRefreshStatus = REFRESH_FINISHED;//当前的刷新状态
	
	private String mLastRefreshStatus;
	
	//刷新的回调
	private PullToRefreshListener mPullToRefreshListener;
	
	
	public YPullToRefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initMyView(context);
		initLoadingHead(context);
		initListView(context);
	}
	
	private void initMyView(Context context){
		LayoutInflater.from(context).inflate(R.layout.layout_my_pulltorefresh_listview, this, true);
	}

	private void initLoadingHead(Context context){
		mHeadView = (View)findViewById(R.id.pull_to_refresh_head);
//		mHeadView = (View)LayoutInflater.from(context).inflate(R.layout.layout_refresh_headview,null);
		mLoadingProgress = (ProgressBar)mHeadView.findViewById(R.id.layout_refresh_headview_progress_load);
		mLoadingImage = (ImageView)mHeadView.findViewById(R.id.layout_refresh_headview_image_load);
		mLoadingText = (TextView)mHeadView.findViewById(R.id.layout_refresh_headview_text_load);
		mLastLoadingTime = (TextView)mHeadView.findViewById(R.id.layout_refresh_headview_text_time);
		//获取最小判定手指移动距离
		mMoveJudgeValue = ViewConfiguration.get(context).getScaledTouchSlop();
	}
	
	private void initListView(Context context){
		//这种方式不保险,应该用R.id来完成,只是为了适应所有项目需要自定义个R工具类,在这里就不扩展了
		mListView = (ListView) findViewById(R.id.layout_my_pulltorefresh_listview_listview);
		mListView.setOnTouchListener(this);
	}
	
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(changed){
			mHeadViewParams = (LayoutParams)mHeadView.getLayoutParams();
			mHeadViewHight = mHeadView.getHeight();
			mHeadViewParams.topMargin = -mHeadViewHight;//这里将高度设为负值以便于隐藏
		}
	}
	
	/**
	 * 可以由外部指定ListView;
	 * @param listview
	 */
	public void setShowListView(ListView listview){
		mListView = listview;
	}
	
	/**
	 * 外部设置显示的数据,这里的写法是让数据由外部管理
	 * @param list
	 */
	public void setListViewData(ArrayList<String> list){
		mListViewData = list;
		mListViewAdapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, mListViewData);
		mListView.setAdapter(mListViewAdapter);
		mListViewAdapter.notifyDataSetChanged();
		
	}
	
	/**
	 * 设置刷新的监听器
	 * @param listener
	 */
	public void setPullToRefreshListener(PullToRefreshListener pullToRefreshListener){
		mPullToRefreshListener = pullToRefreshListener;
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		
		//在开始判断当前的手指移动对是否可以放开刷新之前,先要判断当前的状态,如果正在刷新,则不改变状态
		if(!isCurrentCanChangeRefreshStatis())
			return false;//封装判断当前能否改变刷新状态,方便以后扩展
		switch (event.getAction()) {
		//down事件负责记录手指点击坐标
		case MotionEvent.ACTION_DOWN:
			mDownY = event.getRawY();//存储点击下来时Y的坐标
			break;
			//move事件负责记录状态
		case MotionEvent.ACTION_MOVE:
			//模拟:-5(现在的坐标)-(-3(手指放下坐标))下移 = -2	;-3(现在的坐标)-(-5(手指放下的坐标))上移 = 2
			int distance = (int)(event.getRawY() - mDownY);
			if(distance < 0){//上移
				//如果有上拉加载更多则可以在这里添加
			}else{//下移
				int absolutedistance = 0;
				if(distance<0){//先将绝对值算出来
					absolutedistance = -distance;
				}else{
					absolutedistance = distance;
				}
				if(absolutedistance < mMoveJudgeValue){//小于最小移动判定
					return false;
				}
				
				//这里应该要有2种状态
				//1、手指还没移动到可以刷新的状态,此时mHeadViewParams.topMargin < 0
//				if(absolutedistance < mHeadViewHight){
				if(mHeadViewParams.topMargin < 0){
					mLastRefreshStatus = mCurrentRefreshStatus;
					mCurrentRefreshStatus = PULL_TO_CALCEL_REFRESH;
					//更新headview的显示
					if(!mLastRefreshStatus.equals(mCurrentRefreshStatus))
					headViewonPullToRefresh();
				//2、手指移动到了可以刷新的状态,此时mHeadViewParams.topMargin >= 0
				}else{
					mLastRefreshStatus = mCurrentRefreshStatus;
					mCurrentRefreshStatus = PULL_TO_REFRESH;
					//更新headview的显示
					if(!mLastRefreshStatus.equals(mCurrentRefreshStatus))
					headViewcanPullToRefresh();
				}
				// 通过偏移下拉头的topMargin值,来实现下拉效果  ,这里是调节headview显示的高度
				mHeadViewParams.topMargin = (distance / 2) - mHeadViewHight;  
                mHeadView.setLayoutParams(mHeadViewParams);  
			}
			
			
			
			
			break;
			//up事件负责根据记录的状态判断对应处理
		case MotionEvent.ACTION_UP:
			//放开时时可以刷新的状态
			if(mCurrentRefreshStatus.equals(PULL_TO_REFRESH)){
				downPullToRefreshing();//下拉刷新
			}else
			if(mCurrentRefreshStatus.equals(PULL_TO_CALCEL_REFRESH)){
				canceldownPullToRefresh();
			}else{
				refreshingFinished();
			}
			break;
		}
		return false;
	}
	
	/**
	 * 判断当前能否改变刷新状态,目前只判断是否在刷新状态
	 * @return
	 */
	private boolean isCurrentCanChangeRefreshStatis(){
		//当前是否正在刷新
		if(mCurrentRefreshStatus.equals(REFRESHING))
			return false;//正在刷新则不作处理
		View firstChild = mListView.getChildAt(0);  
		if(null == firstChild)//列表没有内容时可以下拉刷新
			return true;
		//当前是否可以下拉刷新,是否在集合最上方
		int firstVisiblePos = mListView.getFirstVisiblePosition(); 
		if(firstVisiblePos != 0)//可见的第一项不是第0个时
			return false;
		return true;
	}
	
	/**
	 * 刷新
	 */
	private void downPullToRefreshing(){
		mLastRefreshStatus = mCurrentRefreshStatus;
		mCurrentRefreshStatus = REFRESHING;
		//更新头部的显示为正在刷新
		headViewrefreshing();
		//让外部刷新数据
		mPullToRefreshListener.refresh();//让外部加载数据
	}
	
	/**
	 * 取消下拉刷新
	 */
	private void canceldownPullToRefresh(){
		//更新头部的显示消失
		refreshingFinished();
	}
	
	/**
	 * 正在向操作到可以下拉刷新的过程中操作
	 */
	private void headViewonPullToRefresh(){
		//改变箭头,这样写是因为下拉到可以刷新后再手指回移时有一个恢复的动画
		rotateArrow(mLoadingImage, 180f, 360f, 100);
		//改变文字
		mLoadingText.setText("继续下拉可刷新");
		//设置上次刷新的时间
		mLastLoadingTime.setText(mLastLoadTimeValue);
	}
	
	/**
	 * 已经可以放手下拉刷新
	 */
	private void headViewcanPullToRefresh(){
		//可以刷新时,旋转180度
		rotateArrow(mLoadingImage, 0f, 180f, 100);
		//改变文字
		mLoadingText.setText("放手刷新");
	}
	
	/**
	 * 正在刷新
	 */
	private void headViewrefreshing(){
		//调整headView高度
		//这里一开始纠结了一下,一开始是设为一开始记录的hight的,后来想起来其实设为0才是
		//希望的视图效果
		mHeadViewParams.topMargin = 0;
		mHeadView.setLayoutParams(mHeadViewParams);
		//隐藏箭头
		mLoadingImage.clearAnimation();//不清除的话会导致动画残留
		mLoadingImage.setVisibility(GONE);
		//显示正在加载的进度
		mLoadingProgress.setVisibility(VISIBLE);
		//改变文字
		mLoadingText.setText("正在刷新");
	}
	
	/**
	 * 刷新结束
	 */
	public void refreshingFinished(){
		//调整headView高度
		mHeadViewParams.topMargin = -mHeadViewHight;
		mHeadView.setLayoutParams(mHeadViewParams);
		//隐藏箭头
		mLoadingImage.setVisibility(VISIBLE);
				//显示正在加载的进度
		mLoadingProgress.setVisibility(GONE);
		//刷新数据
		mListViewAdapter.notifyDataSetChanged();
		
		mLastRefreshStatus = mCurrentRefreshStatus;
		mCurrentRefreshStatus = REFRESH_FINISHED;
	}
	
	
	public interface PullToRefreshListener{
		public void refresh();
	}
	
	/**
	 * 旋转图片
	 * @param arrow			需要旋转的图片
	 * @param fromAngle		旋转的开始角度
	 * @param toAngle		旋转的结束角度
	 * @param time			持续的时间
	 */
    private void rotateArrow(ImageView arrow,float fromAngle,float toAngle,int time) {  
        float pivotX = arrow.getWidth() / 2f;  
        float pivotY = arrow.getHeight() / 2f;  
        RotateAnimation animation = new RotateAnimation(fromAngle, toAngle, pivotX, pivotY);  
        animation.setDuration(time);  
        animation.setFillAfter(true);//是否动画播放完毕后保持状态
        arrow.startAnimation(animation);  
    } 
	
}
可以说我的代码里面注释还是比较全的,平时写代码也习惯写的比较全,一些不熟悉的方法也会写上注释

那么下面就是我的测试程序

package com.example.mypulltorefresh;

import java.util.ArrayList;

import com.example.mypulltorefresh.YPullToRefreshListView.PullToRefreshListener;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;

public class MainActivity extends Activity {

	private ArrayList<String> mListData = new ArrayList<String>();
	private int mCount = 0;
	private YPullToRefreshListView mMyListView;
	private Handler mHandler;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mHandler = new Handler(this.getMainLooper());
		mMyListView = (YPullToRefreshListView)findViewById(R.id.my_pulltorefresh);
		mListData.add("xxxx");
		mListData.add("yyyy");
		initData();
	}
	
	private void initData(){
		mMyListView.setListViewData(mListData);
		mMyListView.setPullToRefreshListener(new PullToRefreshListener() {
			@Override
			public void refresh() {
				
				new Thread(new Runnable() {
					
					@Override
					public void run() {
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						for(int i = 1;i <=10;i++){
							mListData.add(mCount+"");
							mCount +=1;
						}
						
						mHandler.post(new Runnable() {
							
							@Override
							public void run() {
								mMyListView.refreshingFinished();
							}
						});
					}
				}).start();
				
				
				
			}
		});
	}

}


可以说代码是非常齐全了



这样以后有什么特殊的定制就比较好实现了,现在放在这边备份一份
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: