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

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

2015-05-14 11:44 597 查看
接着上次来讲,这次来动手写一下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;
}

}


基本上没什么问题了,其实实现还是很简单的,不过就是要多多练习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐