android最基本的lsitvew实现下拉刷新,上拉加载更多的demo

接着上次来讲,这次来动手写一下listview的下拉刷新功能和上拉加载更多功能。

当然google在android4.0以上的API里面的提供了一个可以下拉加载更多的控件,这个小圆圈加载控件在豆瓣,知乎日报里面都有运用到,而我在下一篇博客也会提到。

先来了解一下最基本的listview的的加载功能吧。

首先是下拉刷新功能,我先说一下基本的思路。listveiw的面提供了一个addheader()方法,我们可以重写listview,然后用addheader方法加载我们自定义的加载布局。然后就是隐藏这个header,然后复写监听方法OnScrollListener()和OnTouch()方法,最后再提供一个接口方法来让用户实现加载数据。具体的我在代码里面都注释好了。

再来说一下上拉加载,这个相比于下拉加载就简单多了,我们可以addfooter()方法添加布局,然后监听OnScrollListener就可以了,当最后一个可见的item等于总数量的item时,就可以加载数据了。具体在代码里面斗注释好了。

效果图:

先发布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.listview_pulltorefresh.MainActivity" >

    <com.example.listview_pulltorefresh.RefreshListview
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </com.example.listview_pulltorefresh.RefreshListview>

</RelativeLayout>

header.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" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <LinearLayout
            android:id="@+id/layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/tip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉刷新" />

            <TextView
                android:id="@+id/lastrefresh_time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_toLeftOf="@id/layout"
            android:src="@drawable/pull_to_refresh_arrow" />
        <!-- android:indeterminateDrawable="@drawable/loading_anim" -->

        <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_toLeftOf="@id/layout"
            android:visibility="gone" />
    </RelativeLayout>

</LinearLayout>

footer.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" >

    <LinearLayout
        android:id="@+id/load_footer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <ProgressBar
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="正在加载"/>
    </LinearLayout>

</LinearLayout>

listitem.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:src="@drawable/ic_launcher"/>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="91dp"
        android:layout_toRightOf="@+id/image"
        android:text="数据列" />

</RelativeLayout>

自定义的listview方法:

package com.example.listview_pulltorefresh;

import java.sql.Date;
import java.text.SimpleDateFormat;

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.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class RefreshListview extends ListView {
	private View header;// 顶部布局文件
	private View footer;// 底部布局
	private int headerHeight;// 顶部布局文件的高度
	private int firstVisibleItem;// 当前第一个可见item的位置
	private boolean isRemark;// 标记当前listviews是否最顶端摁下
	private int startY;// 开始的Y值

	private int mscrollState;// 当前listview的滚动状态
	private int state;// 当前状态
	private static final int NONE = 0;// 正常状态
	private static final int PULL = 1;// 下拉状态
	private static final int RELEASE = 2;// 松开状态
	private static final int REFRESHING = 3;// 刷新状态

	private int mtotalItemCount;//全部item的数量
	private int lastVisableItem;//最后一个可见的item
	private boolean isLoading=false;//是否正在加载

	public RefreshListview(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListview(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	public RefreshListview(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		initView(context);
	}

	/**
	 * 添加顶部布局文件
	 *
	 * @param context
	 */
	private void initView(Context context) {
		LayoutInflater inflater = LayoutInflater.from(context);
		footer = inflater.inflate(R.layout.footer_loading, null, false);
		header = inflater.inflate(R.layout.header_layout, null, false);
		measureView(header);
		headerHeight = header.getMeasuredHeight();
		Log.i("test", "headHeight:" + headerHeight);
		topPadding(-headerHeight);
		this.addHeaderView(header);
		//先设置底部隐藏
		footer.setVisibility(View.GONE);
		this.addFooterView(footer);
		this.setOnScrollListener(new OnScrollListener() {

			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				// TODO Auto-generated method stub
				mscrollState = scrollState;

				//最后一个可见的item是总数量,并且当前滚动状态停止,就加载数据
				if (mtotalItemCount==lastVisableItem&&scrollState==OnScrollListener.SCROLL_STATE_IDLE) {
					if (!isLoading) {
						//加载数据
						isLoading=true;
						footer.setVisibility(VISIBLE);
						mListener2.onReflashMore();

					}

				}
			}

			/**
			 * firstvisebleitem第一个可见的位置
			 */
			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				// TODO Auto-generated method stub
				lastVisableItem=firstVisibleItem+visibleItemCount;
				mtotalItemCount=totalItemCount;
			}

		});

		this.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				// TODO Auto-generated method stub
				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					if (firstVisibleItem == 0) {
						isRemark = true;
						startY = (int) event.getY();
					}
				case MotionEvent.ACTION_MOVE:
					onMove(event);

					break;
				case MotionEvent.ACTION_UP:
					if (state == RELEASE) {

						state = REFRESHING;
						reflashViewByState();
						mListener.onrReflash(); // 加载数据
						// 在外部调用reflashcomplete
					} else if (state == PULL) {
						state = NONE;
						isRemark = false;
						reflashViewByState();
					}
					break;

				default:
					break;
				}
				return false;
			}
		});
	}

	/**
	 * 通过view获取layoutparams,然后初始化lp, 要调用measure()方法来设置子view的宽高
	 * measure的方法参数有变化的是用MeasureSpec.makeMeasureSpec设置
	 * 没有变化的用getChildMeasureSpec()方法设置
	 *
	 * @param view
	 */

	public void measureView(View view) {
		ViewGroup.LayoutParams lParams = view.getLayoutParams();
		if (lParams == null) {
			lParams = new ViewGroup.LayoutParams(
					ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
			// spec左右边距,padding内边距
			int width = ViewGroup.getChildMeasureSpec(0, 0, lParams.width);
			int height;
			int tempHeight = lParams.height;
			if (tempHeight > 0) {
				// 填充用exactly
				height = MeasureSpec.makeMeasureSpec(tempHeight,
						MeasureSpec.EXACTLY);

			} else {
				// 意思就是<=0时,则告诉父布局子view高度填充0
				height = MeasureSpec
						.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
			}
			view.measure(width, height);
		}

	}

	/**
	 * 设置header布局的上边距
	 *
	 * @param topPadding
	 */
	public void topPadding(int topPadding) {
		header.setPadding(header.getPaddingLeft(), topPadding,
				header.getPaddingRight(), header.getPaddingBottom());
		header.invalidate();
	}

	/**
	 * 判断移动过程中的操作
	 *
	 * @param event
	 */
	private void onMove(MotionEvent event) {
		// TODO Auto-generated method stub
		if (!isRemark) {
			return;
		}
		int tempY = (int) event.getY();// 获取当前y
		int space = tempY - startY;// 显示的高度
		int topPadding = space - headerHeight;// 因为是要用负值,所以减去高度
		switch (state) {
		case NONE:
			if (space > 0) {
				state = PULL;
				reflashViewByState();
			}

			break;
		case PULL:
			topPadding(topPadding);
			// 大于heigh+30且在滑动时,则是可以刷新
			if (space > headerHeight + 30
					&& mscrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
				state = RELEASE;
				reflashViewByState();
			}

			break;

		case RELEASE:
			topPadding(topPadding);
			// 为释放状态时,则可以回到下拉状态
			if (space < headerHeight + 30) {
				state = PULL;
				reflashViewByState();
			} else if (space <= 0) {
				state = NONE;
				isRemark = false;
				reflashViewByState();
			}

			break;

		case REFRESHING:

			break;

		default:
			break;
		}
	}

	/**
	 * 改变下拉过程中的header布局中的控件的内容
	 */
	public void reflashViewByState() {
		TextView tip = (TextView) header.findViewById(R.id.tip);
		ImageView arrow = (ImageView) header.findViewById(R.id.arrow);
		ProgressBar progressBar = (ProgressBar) header
				.findViewById(R.id.progress);
		// RotateAnimation旋转动画,旋转的角度,相对与自己,中心位置
		RotateAnimation animation = new RotateAnimation(0, 180,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		animation.setDuration(500);// 时间间隔
		animation.setFillAfter(true);// 保存状态
		RotateAnimation animation2 = new RotateAnimation(180, 0,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		animation2.setDuration(500);// 时间间隔
		animation2.setFillAfter(true);// 保存状态
		switch (state) {
		case NONE:
			arrow.clearAnimation();
			topPadding(-headerHeight);
			break;
		case PULL:
			arrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(GONE);
			arrow.clearAnimation();
			arrow.setAnimation(animation2);
			tip.setText("下拉可以刷新");
			break;
		case RELEASE:
			arrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(GONE);
			arrow.clearAnimation();
			arrow.setAnimation(animation);
			tip.setText("松开可以刷新");
			break;
		case REFRESHING:
			topPadding(50);
			arrow.clearAnimation();
			arrow.setVisibility(View.GONE);
			progressBar.setVisibility(VISIBLE);
			tip.setText("正在刷新");
			break;

		default:
			break;
		}
	}

	/**
	 * 获取完数据
	 */
	public void reflashComplete() {
		state = NONE;
		isRemark = false;
		reflashViewByState();
		TextView lastTimeReflash = (TextView) header
				.findViewById(R.id.lastrefresh_time);
		SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
		Date date = new Date(System.currentTimeMillis());
		String timeString = format.format(date);
		lastTimeReflash.setText(timeString);
	}

	/**
	 * 底部加载完毕
	 */
	public void reflashFooterComplete() {
		isLoading=false;
		footer.setVisibility(GONE);
	}

	/**
	 * 刷新数据接口
	 *
	 * @author nickming
	 *
	 */
	public interface OnReflashListener {
		public void onrReflash();
	}

	public OnReflashListener mListener;// 刷新数据的接口

	public void setOnReflashListener(OnReflashListener listener) {
		mListener = listener;
	}

	/**
	 * 加载更多接口
	 * @author nickming
	 *
	 */
	public interface OnReflashMoreListener{
		public void onReflashMore();
	}

	public OnReflashMoreListener mListener2;

	public void  setOnReflashMoreListener(OnReflashMoreListener listener) {
		mListener2=listener;
	}

}

MainActivity.class

package com.example.listview_pulltorefresh;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

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

import com.example.listview_pulltorefresh.RefreshListview.OnReflashListener;
import com.example.listview_pulltorefresh.RefreshListview.OnReflashMoreListener;

public class MainActivity extends Activity {
	private RefreshListview mlistview;
	MyAdapter adapter;
	List<DataBean> mdata;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mlistview = (RefreshListview) findViewById(R.id.listview);
		mdata = new ArrayList<DataBean>();
		for (int i = 0; i < 20; i++) {
			DataBean dataBean = new DataBean();
			dataBean.setTextString("数据列:" + i);
			mdata.add(dataBean);
		}
		adapter = new MyAdapter(this, mdata);
		mlistview.setAdapter(adapter);
		mlistview.setOnReflashListener(new OnReflashListener() {

			@Override
			public void onrReflash() {
				// TODO Auto-generated method stub
				// 模拟网络延时
				Handler mHandler = new Handler();
				mHandler.postDelayed(new Runnable() {

					@Override
					public void run() {
						// TODO Auto-generated method stub
						// 获取最新数据
						addHeaderData();
						// 通知布局显示
						adapter.notifyDataSetChanged();
						// listview刷新
						mlistview.reflashComplete();
					}
				}, 3000);

			}
		});

		mlistview.setOnReflashMoreListener(new OnReflashMoreListener() {

			@Override
			public void onReflashMore() {
				// TODO Auto-generated method stub
				Handler mHandler=new Handler();
				mHandler.postDelayed(new Runnable() {

					@Override
					public void run() {
						// TODO Auto-generated method stub
						addFooterData();
						adapter.notifyDataSetChanged();
						mlistview.reflashFooterComplete();

					}
				}, 2000);
			}
		});
	}

	public void addHeaderData() {
		for (int i = 0; i < 10; i++) {
			DataBean dataBean = new DataBean();
			String nameString = UUID.randomUUID().toString();
			dataBean.setTextString("最新数据:" + nameString);
			mdata.add(0, dataBean);// 放在最前面
		}
	}

	public void addFooterData() {
		for (int i = 0; i < 10; i++) {
			DataBean dataBean = new DataBean();
			String nameString = UUID.randomUUID().toString();
			dataBean.setTextString("最新数据:" + nameString);
			mdata.add( dataBean);// 放在最后面
		}
	}
}

adapter

package com.example.listview_pulltorefresh;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter {

	private Context mcontext;
	private List<DataBean> mData;
	LayoutInflater inflater;

	public MyAdapter(Context context, List<DataBean> mData) {
		super();
		this.mcontext = context;
		this.mData = mData;
		inflater=LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mData.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return mData.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		viewHolder holder=null;
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.listitem, parent,false);
			holder = new viewHolder();
			holder.textView = (TextView) convertView
					.findViewById(R.id.textView1);
			convertView.setTag(holder);

		}else {
			holder=(viewHolder) convertView.getTag();
		}
		holder.textView.setText(mData.get(position).getTextString());

		return convertView;
	}

	class viewHolder {
		TextView textView;
	}

}

DataBean.class

package com.example.listview_pulltorefresh;

import android.widget.TextView;

public class DataBean {

	String textString;

	public DataBean() {
		// TODO Auto-generated constructor stub
	}

	public DataBean(String textString) {
		super();
		this.textString = textString;
	}

	public String getTextString() {
		return textString;
	}

	public void setTextString(String textString) {
		this.textString = textString;
	}

}

基本上没什么问题了,其实实现还是很简单的,不过就是要多多练习。

时间: 2024-11-09 01:55:57

android最基本的lsitvew实现下拉刷新,上拉加载更多的demo的相关文章

十分钟实现ListView下拉刷新上滑加载更多

说到ListView下拉刷新几乎每个APP都会用到,所以ListView下拉刷新是很重要的,就像ListView优化一样是你必会的东西. ListView实现下拉刷新如果我们开发人员自己编写相对来说比较费事的,当我们使用第三方库之后我们再来开发这个功能就会省事很多.相比与自己实现可以少编写不少代码,Android-PullToRefresh库可以轻松实现ListView的下拉刷新功能. 要使用Android—PullToRefesh库对ListView实现下拉刷新要经过以下几个步骤: 1.下载A

Android下拉刷新库,利用viewdraghelper实现,集成了下拉刷新,底部加载更多,数据初始加载显示loading等功能

项目Github地址:https://github.com/sddyljsx/pulltorefresh Android下拉刷新库,利用viewdraghelper实现. 集成了下拉刷新,底部加载更多,以及刚进入加载数据的loadview.包括了listview与gridview的改写. 效果1: 效果2: 效果3: 效果4: 效果5: 使用说明: imageList=(ListView)findViewById(R.id.image_list); imageAdapter=new ImageA

最新Android ListView 下拉刷新 上滑加载

开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,之前大家最常用的应该是pull to refresh或它的变种版吧,google官方在最新的android.support.v4包中增加了一个新类SwipeRefreshLayout,地址 这个类的作用就是提供官方的下拉刷新,并且效果相当不错,而上拉加载更多则用我们自定义的listview,也是相当简单. 下拉刷新 简单的介绍下: 首先它是一个viewgroup,但是它只允许有一个子控件,子控件能是任何view,使用的时候,所在

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果(五) 五一劳动节马上来临,小伙伴有妹有很激动哟,首先祝天下所有的程序猿节日快乐!这个五一对于我来说有点不一样,我的人生从这个五一就转弯了,爱情长跑8年的我结婚了,一会支付宝账号我会公布出去,请自觉打款!谢谢合作. 灯光闪起来: 舞蹈跳起来: 歌曲唱起来: -------------------------------------------------------------------------------------

Android ListView 下拉刷新 点击加载更多

最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例. 效果图 下拉刷新: 加载更多: CustomListView.java [java] view plaincopy package com.example.uitest.view; import java.util.Date; import com.example.uitest.R; import android.content.Context; import android.uti

支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)

首先看效果 下拉刷新:        上划加载        在项目更新的过程中,遇到了一个将XListView换成recyclerView的需求,而且更换完之后大体效果不能变,但是对于下拉刷新这样的效果,谷歌给出的解决方案是把RecyclerView放在一个SwipeRefreshLayout中,但是这样其实是拉下一个小圆形控件实现的,和XListView的header效果不同.在网上找了很多的别人代码,都没有实现我想要的效果,于是自己动手写了一个. 具体实现的效果有以下几条 下拉刷新功能:

Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView

在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/details/78781682 ,这里是看了之后,结合自己实际遇到的问题写的. 首先引入包. //下拉框 implementation 'com.android.support:recyclerview-v7:28.0.0-beta01' implementation 'com.scwang.smar

Android如何定制一个下拉刷新,上滑加载更多的容器

前言 下拉刷新和上滑加载更多,是一种比较常用的列表数据交互方式. android提供了原生的下拉刷新容器 SwipeRefreshLayout,可惜样式不能定制. 于是打算自己实现一个专用的.但是下拉刷新和上滑,非常考验对android布局与父子触摸机制的功底,因此参考gitHub上的一个热门的下拉刷新项目 之所以选择他是因为它一个类就完成了所有View的适配,非常的精简强力. 需求 咱对下拉刷新.上滑加载更多的控件,需求如下: 1:下拉刷新,拖动到一定距离,提示文字变成 放手刷新 2:刷新完成

Android 自定义 ListView 上下拉动&ldquo;刷新最新&rdquo;和&ldquo;加载更多&rdquo;歌曲列表

本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码太多,点击此处下载,自己调试一下. 下载 Demo 环境 Windows 2008 R2 64 位 Eclipse ADT V22.6.2,Android 4.4.3 SAMSUNG GT-I9008L,Android OS 2.2.2 测试数据 本演示的歌曲信息,共有 20 条,包括歌手名.歌曲名.时长.缩

android 安卓 listview 支持下拉刷新 上拉加载更多

[1]重写listView import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGrou