一,自定义一个类,继承ListView。代码如下:
package com.example.view;
import com.example.text.R;
import android.content.Context;
import android.widget.AbsListView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
/**
* 下拉刷新,上拉自动加载的自定义view,增加滑动删除功能
* @author Administrator
*
*/
public class PullToFlashView extends ListView implements OnScrollListener,OnClickListener{
/**
* 下拉状态
* @param context
*/
private static final int PULL_TO_REFRESH = 1; //下拉-默认为初始状态 准备下拉刷新
private static final int RELEASE_TO_REFRESH = 2; //释放刷新
private static final int REFRESHING = 3; //正在刷新
private static final String TAG = "PullRefreshListView";
private OnRefreshListener mOnRefreshListener;
/**
* 组件滑动监听器 scroll 当view在进行下拉滑动的时候,判断滑动的距离,
* 如果达到可以进行刷新的临界点时候,回调当前接口中的方法
* Listener that will receive notifications every time the list scrolls.
*/
private OnScrollListener mOnScrollListener;
//下拉刷新的的头部view
private LinearLayout mRefreshView;
private ImageView mRefreshViewImage;
private ProgressBar mRefreshViewProgress;
private TextView mRefreshViewText;//文字提醒
private TextView mRefreshViewLastUpdated;//时间更新
private int mRefreshState;
private int mCurrentScrollState;
private RotateAnimation mFlipAnimation; //旋转动画
private RotateAnimation mReverseFlipAnimation;
private int mRefreshViewHeight;
private int mRefreshOriginalTopPadding;
private int mLastMotionY;
public PullToFlashView(Context context) {
super(context);
init(context);
}
public PullToFlashView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mFlipAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mFlipAnimation.setInterpolator(new LinearInterpolator());
mFlipAnimation.setDuration(250);
mFlipAnimation.setFillAfter(true);
mReverseFlipAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
mReverseFlipAnimation.setDuration(250);
mReverseFlipAnimation.setFillAfter(true);
mRefreshView = (LinearLayout) View.inflate(context, R.layout.pull_to_refresh_header, null);
mRefreshViewText = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);
mRefreshViewImage = (ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);
mRefreshViewProgress = (ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);
mRefreshViewLastUpdated = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);
mRefreshState = PULL_TO_REFRESH;
mRefreshViewImage.setMinimumHeight(50); //设置下拉最小的高度为50
setFadingEdgeLength(0);
setHeaderDividersEnabled(false);
//把refreshview加入到listview的头部
addHeaderView(mRefreshView);
super.setOnScrollListener(this);
mRefreshView.setOnClickListener(this);
mRefreshView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mRefreshViewHeight = mRefreshView.getMeasuredHeight();
mRefreshOriginalTopPadding = -mRefreshViewHeight;
resetHeaderPadding();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
int x = (int) ev.getX();
int y = (int) ev.getY();
return super.dispatchTouchEvent(ev);
}
/**
* Set the listener that will receive notifications every time the list scrolls.
*
* @param l The scroll listener.
*/
@Override
public void setOnScrollListener(OnScrollListener l) {
mOnScrollListener = l;
}
/**
* 注册listview下拉刷新回到接口
* Register a callback to be invoked when this list should be refreshed.
*
* @param onRefreshListener The callback to run.
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}
/**
* 进行设置设置上一次更新的时候
*
* Set a text to represent when the list was last updated.
* @param lastUpdated Last updated at.
*/
public void setLastUpdated(CharSequence lastUpdated) {
if (lastUpdated != null) {
mRefreshViewLastUpdated.setVisibility(View.VISIBLE);
mRefreshViewLastUpdated.setText(lastUpdated);
} else {
mRefreshViewLastUpdated.setVisibility(View.GONE);
}
}
/**
* touch事件处理
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
final int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastMotionY = y;
break;
case MotionEvent.ACTION_MOVE:
int offsetY = (int) event.getY();
int deltY = Math.round(offsetY - mLastMotionY);
mLastMotionY = offsetY;
if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
deltY = deltY / 2;
mRefreshOriginalTopPadding += deltY;
if (mRefreshOriginalTopPadding < -mRefreshViewHeight) {
mRefreshOriginalTopPadding = -mRefreshViewHeight;
}
resetHeaderPadding();
}
break;
case MotionEvent.ACTION_UP:
//当手指抬开得时候 进行判断下拉的距离 ,如果>=临界值,那么进行刷洗,否则回归原位
if (!isVerticalScrollBarEnabled()) {
setVerticalScrollBarEnabled(true);
}
if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
if (mRefreshView.getBottom() >= mRefreshViewHeight
&& mRefreshState == RELEASE_TO_REFRESH) {
//准备开始刷新
prepareForRefresh();
} else {
// Abort refresh
resetHeader();
}
}
break;
}
return super.onTouchEvent(event);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && mRefreshState != REFRESHING) {
if (firstVisibleItem == 0) {
if ((mRefreshView.getBottom() >= mRefreshViewHeight)
&& mRefreshState != RELEASE_TO_REFRESH) {
mRefreshViewText.setText(R.string.pull_to_refresh_release_label_it);
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mFlipAnimation);
mRefreshState = RELEASE_TO_REFRESH;
} else if (mRefreshView.getBottom() < mRefreshViewHeight
&& mRefreshState != PULL_TO_REFRESH) {
mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it);
mRefreshViewImage.clearAnimation();
mRefreshViewImage.startAnimation(mReverseFlipAnimation);
mRefreshState = PULL_TO_REFRESH;
}
}
}
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
mCurrentScrollState = scrollState;
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
/**
* Sets the header padding back to original size.
*/
private void resetHeaderPadding() {
mRefreshView.setPadding(
mRefreshView.getPaddingLeft(),
mRefreshOriginalTopPadding,
mRefreshView.getPaddingRight(),
mRefreshView.getPaddingBottom());
}
public void prepareForRefresh() {
if (mRefreshState != REFRESHING) {
mRefreshState = REFRESHING;
mRefreshOriginalTopPadding = 0;
resetHeaderPadding();
mRefreshViewImage.clearAnimation();
mRefreshViewImage.setVisibility(View.GONE);
mRefreshViewProgress.setVisibility(View.VISIBLE);
mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label_it);
onRefresh();
}
}
private void resetHeader() {
mRefreshState = PULL_TO_REFRESH;
mRefreshOriginalTopPadding = -mRefreshViewHeight;
resetHeaderPadding();
mRefreshViewImage.clearAnimation();
mRefreshViewImage.setVisibility(View.VISIBLE);
mRefreshViewProgress.setVisibility(View.GONE);
mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it);
}
/**
* 开始回调刷新
*/
public void onRefresh() {
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
/**
* Resets the list to a normal state after a refresh.
*/
public void onRefreshComplete() {
Log.d(TAG, "onRefreshComplete");
resetHeader();
}
@Override
public void onClick(View v) {
Log.d(TAG, "onClick");
}
/**
* Interface definition for a callback to be invoked when list should be
* refreshed.
*/
public interface OnRefreshListener {
/**
* Called when the list should be refreshed.
* <p>
* A call to {@link PullToRefreshListView #onRefreshComplete()} is
* expected to indicate that the refresh has completed.
*/
public void onRefresh();
}
}
上面所说的头布局(R.layout.pull_to_refresh_header)XML资源文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pull_to_refresh_header"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#F3F3F3"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="23dip" >
<LinearLayout
android:id="@+id/pull_to_refresh_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:orientation="vertical" >
<TextView
android:id="@+id/pull_to_refresh_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/pull_to_refresh_pull_label"
android:textColor="#777777"
android:textSize="16sp" />
<TextView
android:id="@+id/pull_to_refresh_updated_at"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/pull_to_refresh_updated_at"
android:textColor="#999999"
android:textSize="14sp" />
</LinearLayout>
<ProgressBar
android:id="@+id/pull_to_refresh_progress"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_marginRight="22dip"
android:layout_marginTop="5dip"
android:layout_toLeftOf="@+id/pull_to_refresh_view"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/ic_loading_refresh"
android:visibility="gone" />
<ImageView
android:id="@+id/pull_to_refresh_image"
android:layout_width="32dip"
android:layout_height="32dip"
android:layout_marginRight="20dip"
android:layout_marginTop="5dip"
android:layout_toLeftOf="@+id/pull_to_refresh_view"
android:contentDescription="@string/app_name"
android:gravity="center"
android:src="@drawable/ic_refresh_down" />
</RelativeLayout>
<View
android:layout_width="fill_parent"
android:layout_height="15dip" />
</LinearLayout
这里要说明一下,上面Xml文件里面的ProgressBar利用的是旋转动画实现的,其实现过程如下:
自定义一个xml布局 ic_loading_refresh,放在drawable文件夹下,里面drawable属性对应的图片资源(ic_loading)自定义即可
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_loading"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" />
自定义的view写好了,接着就是在Activity中实现啦
public class PullListviewActivity extends Activity {
/**
* 数据源
*/
private List<String> mTitles;
/**
* 适配器
*/
private PullAdapter mPullAdapter;
/**
* 下拉刷新的listview
*/
private PullToFlashView mPullToFlashView;
private LayoutInflater mLayoutInflater;
private View load_more;
private TextView load_more_tv; // listview底部加载view 显示数据
private ProgressBar load_more_progress; // listview底部加载view 显示进度
private LinearLayout load_next_page_layout;
private Handler newHandler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1){
refreshTitles();
UIUtils.savePullToRefreshLastUpdateAt(mPullToFlashView,UIUtils.DEMO_PULL_TIME_KEY);
//刷新view
mPullAdapter.notifyDataSetChanged();
//刷新完成
mPullToFlashView.onRefreshComplete();
}else if(msg.what==2){
moreTitles();
//刷新view
mPullAdapter.notifyDataSetChanged();
//showToastMsgShort("加载更多数据成功...");
load_more_tv.setText("数据加载完成");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pull_listview);
//
mLayoutInflater= getLayoutInflater();
//初始化组件
mPullToFlashView = (PullToFlashView) findViewById(R.id.lv_pull_item);
//特别注意 里边的view的控件可以根据当前的状态 修改字符串信息
load_more = mLayoutInflater.inflate(R.layout.load_more_footview_layout,
null);
load_more_tv = (TextView) load_more
.findViewById(R.id.load_next_page_text);
load_more_progress = (ProgressBar) load_more
.findViewById(R.id.load_next_page_progress);
load_next_page_layout = (LinearLayout) load_more
.findViewById(R.id.load_next_page_layout);
//listview添加尾部 上拉加载更多view
mPullToFlashView.addFooterView(load_more, null, false);
load_more_tv.setText("上拉加载更多数据...");
initTitles();
//初始化 上次下拉刷新的时间
UIUtils.setPullToRefreshLastUpdated(mPullToFlashView,UIUtils.DEMO_PULL_TIME_KEY,this);
mPullAdapter=new PullAdapter();
mPullToFlashView.setAdapter(mPullAdapter);
//listview item点击事件处理
mPullToFlashView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
});
//listview 开始下拉刷新回调函数
mPullToFlashView.setOnRefreshListener(new PullToFlashView.OnRefreshListener() {
@Override
public void onRefresh() {
//进行加载数据
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
newHandler.sendEmptyMessage(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
);
//listview 滑动 进行上拉加载更多
mPullToFlashView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
if (mPullToFlashView.getLastVisiblePosition() == (mPullToFlashView
.getCount() - 1)) {
load_more_tv.setText("正在加载最新数据....");
//进行获取最新数据
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
newHandler.sendEmptyMessage(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
//正在滑动中,当前listview正在滑动 可以暂停图片加载器或者其他一些耗时操作
} else {
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
private void initTitles(){
mTitles=new ArrayList<String>();
for(int i=0;i<20;i++){
int index=i+1;
mTitles.add("当前是:"+index+"");
}
}
private void refreshTitles(){
List<String> newTitles=new ArrayList<String>();
for(int i=0;i<5;i++){
int index=i+1;
newTitles.add("新数据是:"+index+"");
}
newTitles.addAll(mTitles);
mTitles.removeAll(mTitles);
mTitles.addAll(newTitles);
}
private void moreTitles(){
List<String> newTitles=new ArrayList<String>();
for(int i=0;i<8;i++){
int index=i+1;
newTitles.add("更多数据是:"+index+"");
}
mTitles.addAll(newTitles);
}
class PullAdapter extends BaseAdapter{
@Override
public int getCount() {
return mTitles.size();
}
@Override
public Object getItem(int position) {
return mTitles.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Hondler _Hondler=null;
if(convertView==null){
_Hondler=new Hondler();
convertView=mLayoutInflater.inflate(R.layout.lv_main_item,null);
_Hondler.tv_item=(TextView)convertView.findViewById(R.id.tv_item);
convertView.setTag(_Hondler);
}else
{
_Hondler=(Hondler)convertView.getTag();
}
_Hondler.tv_item.setText(mTitles.get(position));
return convertView;
}
}
static class Hondler{
TextView tv_item;
}
}
底部,上拉加载更多,xml布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/load_next_page_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:gravity="center"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dip"
android:layout_marginTop="5dip"
android:gravity="center" >
<ProgressBar
android:id="@+id/load_next_page_progress"
android:layout_width="20dip"
android:layout_height="20dip"
android:layout_centerVertical="true"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/ic_loading_refresh"/>
<TextView
android:id="@+id/load_next_page_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:layout_marginLeft="4dip"
android:layout_toRightOf="@id/load_next_page_progress"
android:clickable="true"
android:textColor="@color/black"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>
显示上次加载时间的实现类UIUtils如下:
package com.example.utils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.annotation.SuppressLint;
import android.content.Context;
import com.example.text.PullListviewActivity;
import com.example.text.SharedPreferencesHelper;
import com.example.view.PullToFlashView;
import com.ruiqi.application.FDApplication;
/**
* 当前类注释:进行PullToRefreshListView 下拉刷新控件的更新时间设置
*
*/
public class UIUtils {
public static final String DEMO_PULL_TIME_KEY="demo_pull_time_key";
/**
* 设置上次更新数据时间
* @param listView
* @param key
*/
public static void setPullToRefreshLastUpdated(PullToFlashView listView, String key,Context context) {
SharedPreferencesHelper spHelper = SharedPreferencesHelper.getInstance(context);
long lastUpdateTimeStamp = spHelper.getLongValue(key);
listView.setLastUpdated(getUpdateTimeString(lastUpdateTimeStamp));
}
/**
* 保存更新数据时间
* @param listView
* @param key
*/
public static void savePullToRefreshLastUpdateAt(PullToFlashView listView, String key) {
listView.onRefreshComplete();
SharedPreferencesHelper spHelper = SharedPreferencesHelper.getInstance(FDApplication.getInstance());
long lastUpdateTimeStamp=System.currentTimeMillis();
spHelper.putLongValue(key, lastUpdateTimeStamp);
listView.setLastUpdated(getUpdateTimeString(lastUpdateTimeStamp));
}
/**
* 更新时间字符串
* @param timestamp
* @return
*/
@SuppressLint("SimpleDateFormat")
public static String getUpdateTimeString(long timestamp) {
if (timestamp <= 0) {
return "上次更新时间:";
} else {
String textDate = "上次更新时间:";
Calendar now = Calendar.getInstance();
Calendar c = Calendar.getInstance();
c.setTimeInMillis(timestamp);
if (c.get(Calendar.YEAR) == now.get(Calendar.YEAR)
&& c.get(Calendar.MONTH) == now.get(Calendar.MONTH)
&& c.get(Calendar.DATE) == now.get(Calendar.DATE)) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
return textDate += sdf.format(c.getTime());
} else if (c.get(Calendar.YEAR) == now.get(Calendar.YEAR)) {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm");
return textDate += sdf.format(c.getTime());
} else {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm");
return textDate += sdf.format(c.getTime());
}
}
}
}
主布局文件如下:
<?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">
<LinearLayout
android:id="@+id/linear_bar"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:orientation="vertical"
android:background="#FF6600"
android:visibility="gone"
>
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="49dp"
android:background="#FF6600"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="下拉刷新测试"/>
</RelativeLayout>
<com.example.view.PullToFlashView
android:id="@+id/lv_pull_item"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"/>
</LinearLayout>
好了,上面就是自定义实现下拉刷新和 上拉加载的所有代码了,其实实现逻辑很简单,只是我写的复杂了。就将就着看吧~~~