Android--ListView下拉刷新
整理了下以前写的小项目,ListView的下拉刷新,虽然小但还是想纪念下。。适合新手看,大神略过。。。
效果图:
代码: 实体类
package com.example.listviewrefreshdemo; /** * @author 超超boy */ public class ApkEntity { private String name; private String des; private String info; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }
MainActivity:
package com.example.listviewrefreshdemo; import java.util.ArrayList; import com.example.listviewrefreshdemo.RefreshListView.IReflashListener; import android.app.Activity; import android.os.Bundle; import android.os.Handler; /** * @author 超超boy */ public class MainActivity extends Activity implements IReflashListener{ ArrayList<ApkEntity> apk_list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setData(); showList(apk_list); } MyAdapter adapter; RefreshListView listview; private void showList(ArrayList<ApkEntity> apk_list) { if (adapter == null) { listview = (RefreshListView) findViewById(R.id.listview); listview.setIReflashListener(this); adapter = new MyAdapter(this, apk_list); listview.setAdapter(adapter); } else { adapter.onDateChange(apk_list); } } private void setData() { apk_list = new ArrayList<ApkEntity>(); for (int i = 0; i < 10; i++) { ApkEntity entity = new ApkEntity(); entity.setName("默认数据"); entity.setDes("这是一个神奇的应用"); entity.setInfo("50w用户"); apk_list.add(entity); } } private void setReflashData() { for (int i = 0; i < 2; i++) { ApkEntity entity = new ApkEntity(); entity.setName("刷新数据"); entity.setDes("这是一个神奇的应用"); entity.setInfo("50w用户"); apk_list.add(i,entity); } } @Override public void onReflash() { // TODO Auto-generated method stub\ Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub //获取最新数据 setReflashData(); //通知界面显示 showList(apk_list); //通知listview 刷新数据完毕; listview.reflashComplete(); } }, 2000); } /** * 回掉方法 */ /*public void onReflash() { //获取最新数据 setReflashData(); //通知界面显示 showList(apk_list); //通知listview刷新数据完毕 listview.reflashComplete(); }*/ }
RefreshListView:
package com.example.listviewrefreshdemo; import java.text.SimpleDateFormat; import java.util.Date; import android.R.anim; import android.R.interpolator; 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.AbsListView.OnScrollListener; import android.widget.ProgressBar; import android.widget.TextView; /** * @author 超超boy */ public class RefreshListView extends ListView implements OnScrollListener{ LayoutInflater inflater; View header; int headerHeight; //顶部布局文件的高度 int firstVisibleItem; //当前第一个可见的item的位置 int scrollState; //listview当前滚动状态 boolean isRemark; //标记,当前是在listview的最顶部摁下的 int startY; //摁下时的Y值 int state; //当前的状态 final int NONE = 0;// 正常状态; final int PULL = 1;// 提示下拉状态; final int RELESE = 2;// 提示释放状态; final int REFLASHING = 3;// 刷新状态; IReflashListener reflashListener; public RefreshListView(Context context) { super(context); initView(context); } public RefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(context); } public RefreshListView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } /** * 初始化界面,添加顶部布局文件到ListView * @param context */ private void initView(Context context){ inflater = LayoutInflater.from(context); header =inflater.inflate(R.layout.header, null); measureView(header); headerHeight = header.getMeasuredHeight(); Log.i("tag", "headerHeight="+headerHeight); topPadding(-headerHeight); //负的高度,把它能没啦 this.addHeaderView(header); this.setOnScrollListener(this); } /** * 通知父布局,占用的宽高。 * @param view */ private void measureView(View view){ ViewGroup.LayoutParams p = view.getLayoutParams(); if(p == null){ p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } //spec:header的左右边距,padding内边距,childDimension字布局的宽度 int width = ViewGroup.getChildMeasureSpec(0, 0, p.width); int height; int tempHeight = p.height; if(tempHeight > 0){ //高度不是空,需要填充这个布局 //EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小; height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); }else{ //UNSPECIFIED(未指定),父元素不对子元素施加任何束缚,子元素可以得到任意想要的大小; height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } view.measure(width, height); } /** * 设置header布局的上边距 * @param topPadding */ private void topPadding(int topPadding){ //header的上边距 header.setPadding(header.getPaddingLeft(), topPadding, header.getPaddingRight(), header.getPaddingBottom()); header.invalidate(); } /** * 取得当前的滚动状态 */ public void onScrollStateChanged(AbsListView view, int scrollState) { this.scrollState = scrollState; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; //当前第一个可见的item的位置 } /** * 触摸事件 */ public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: //下拉 if (firstVisibleItem == 0) { //在界面最顶端 isRemark = true; //标记,当前是在listview的最顶部摁下的 startY = (int) ev.getY(); } break; case MotionEvent.ACTION_MOVE: onMove(ev); break; case MotionEvent.ACTION_UP: //抬起 if (state == RELESE) { //释放 state = REFLASHING; //正在刷新 // 加载最新数据; reflashViewByState(); reflashListener.onReflash(); } else if (state == PULL) { //下拉过程中,没到一定距离的时候 state = NONE; // 正常状态; isRemark = false; //在顶部的标记 reflashViewByState(); } break; } return super.onTouchEvent(ev); } /** * 判断移动过程的操作 * @param ev */ private void onMove(MotionEvent ev) { if(!isRemark){ //不在顶的时候移动 return; } //移动的过程中状态是不断改变的 int tempY = (int) ev.getY(); int sapce = tempY-startY; //移动的距离 int topPadding = sapce - headerHeight; //移动过程中不断设置topPadding switch (state) { case NONE: // 正常状态; if(sapce>0){ state = PULL; // 提示下拉状态 reflashViewByState(); } break; case PULL: // 提示下拉状态; topPadding(topPadding); //不断设置上边距 if(sapce > headerHeight+30 && scrollState == SCROLL_STATE_TOUCH_SCROLL){ state = RELESE; // 提示释放状态; reflashViewByState(); } break; case RELESE: // 提示释放状态; topPadding(topPadding); if(sapce<headerHeight+30){ state = PULL; reflashViewByState(); }else if(sapce <= 0){ state = NONE; isRemark = false; reflashViewByState(); } break; default: break; } } /** * 根据当前的状态,改变页面显示 */ private void reflashViewByState(){ TextView tip = (TextView) header.findViewById(R.id.tip); ImageView arrow = (ImageView) header.findViewById(R.id.arrow); ProgressBar bar = (ProgressBar) header.findViewById(R.id.progress); 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); bar.setVisibility(View.GONE); tip.setText("下拉可以刷新"); arrow.clearAnimation(); arrow.setAnimation(animation2); break; case RELESE: //释放 arrow.setVisibility(View.VISIBLE); bar.setVisibility(View.GONE); tip.setText("松开可以刷新"); arrow.clearAnimation(); arrow.setAnimation(animation); break; case REFLASHING: //刷新 topPadding(50); arrow.setVisibility(View.GONE); bar.setVisibility(View.VISIBLE); tip.setText("正在刷新"); arrow.clearAnimation(); break; default: break; } } /** * 获取完数据 */ public void reflashComplete(){ state = NONE; isRemark = false; //不在顶部 reflashViewByState(); TextView lastupdatetime = (TextView) header.findViewById(R.id.lastUpdate_time); SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); Date date = new Date(System.currentTimeMillis()); String time = format.format(date); lastupdatetime.setText(time); } /** * 回调接口 * @author user * */ public interface IReflashListener{ public void onReflash(); } public void setIReflashListener(IReflashListener listener){ this.reflashListener = listener; } }
适配器:
package com.example.listviewrefreshdemo; import java.util.ArrayList; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; /** * @author 超超boy */ public class MyAdapter extends BaseAdapter { ArrayList<ApkEntity> apk_list; LayoutInflater inflater; public MyAdapter(Context context, ArrayList<ApkEntity> apk_list) { this.apk_list = apk_list; this.inflater = LayoutInflater.from(context); } public void onDateChange(ArrayList<ApkEntity> apk_list) { this.apk_list = apk_list; this.notifyDataSetChanged(); //更新数据 } @Override public int getCount() { // TODO Auto-generated method stub return apk_list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return apk_list.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 ApkEntity entity = apk_list.get(position); ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.item_layout, null); holder.name_tv = (TextView) convertView .findViewById(R.id.item3_apkname); holder.des_tv = (TextView) convertView .findViewById(R.id.item3_apkdes); holder.info_tv = (TextView) convertView .findViewById(R.id.item3_apkinfo); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } holder.name_tv.setText(entity.getName()); holder.des_tv.setText(entity.getDes()); holder.info_tv.setText(entity.getInfo()); return convertView; } class ViewHolder { TextView name_tv; TextView des_tv; TextView info_tv; } }
activity_main.xml:
<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.listviewrefreshdemo.MainActivity" > <com.example.listviewrefreshdemo.RefreshListView android:id="@+id/listview" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@color/white" android:cacheColorHint="#00000000" android:dividerHeight="5dip" /> </RelativeLayout>
布局文件还有header.xml和item_layout.xml,就不粘贴了,想看的可以去下我的源代码,下边有下载链接。。
二、代码逻辑
1.自定义状态:PULL下拉状态,RELESE释放状态,REFLASHING刷新状态
2. AbsListView的触摸事件:
下拉的时候设置startY(开始滑动时的位置),之后就是不断的滑动过程,滑动的过程中,用自定义的onMove方法改变状态和header界面(下拉刷新,释放刷新,刷新中);
刷新:
case MotionEvent.ACTION_UP: //抬起
if (state == RELESE) { //释放
state = REFLASHING; //正在刷新
reflashViewByState();// 加载最新数据;
reflashListener.onReflash(); //在回调接口刷新 }
3.自定义的move时判断状态信息:
下拉->释放:sapce > headerHeight+30 判断距离,距离够了就改变状态:释放状态,释放更新
int sapce = tempY-startY; //移动的距离 int topPadding = sapce - headerHeight; //移动过程中不断设置topPadding switch (state) { case NONE: // 正常状态; if(sapce>0){ state = PULL; // 提示下拉状态 reflashViewByState(); } break; case PULL: // 提示下拉状态; topPadding(topPadding); //不断设置上边距 if(sapce > headerHeight+30 && scrollState == SCROLL_STATE_TOUCH_SCROLL){ state = RELESE; // 提示释放状态; reflashViewByState(); } break; case RELESE: // 提示释放状态; topPadding(topPadding); if(sapce<headerHeight+30){ state = PULL; reflashViewByState(); }else if(sapce <= 0){ state = NONE; isRemark = false; reflashViewByState(); } break;
ok。。代码很简单。。。
Demo源代码下载:http://download.csdn.net/detail/jycboy/9588195
附一篇很好的博文:Android 下拉刷新框架实现:http://blog.csdn.net/leehong2005/article/details/12567757