package com.loaderman.myrefreshlistviewdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
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; import java.text.SimpleDateFormat;
import java.util.Date; /**
*
* 实现步骤:
* 1、给ListView添加头布局
* 2、默认让ListView的头布局隐藏起来
* 负的paddingTop的值
* 如何获取头布局的高度
* 3、慢慢的将头布局拖出来
* 获取在ListView中的滑动偏移量--onTouchEvent
* 关于起点坐标的获取dispatchTouchEvent
* 4、给RefreshListView定义了三种状态
* refreshUi:根据当前的状态刷新控件的显示
* 在状态发生改变的时候来调用此方法即可
* 5、增加了动画效果
* clearAnimation的使用
* 6、处理up的事件
* STATE_PULL_TO_REFRESH的时候up
* 隐藏头布局
* STATE_RELEASE_TO_REFRESH的时候up
* 显示头布局
* 更新状态--STATE_REFRESHING
* 通知观察者去加载数据
* 7、观察者设计模式的使用
* 找出被观察者
* 定义观察者接口,接口中的方法就是观察者感兴趣的事件
* 在被观察中存储观察者的引用
* 在事件发生的时候,通知观察者
* 为什么要用接口而不使用抽象类--单继承,多实现
* 8、由TabDetailPager来通知RefreshListView数据加载完成
* setOnRefreshComplete
* 更新状态,隐藏头布局
* 9、设置时间的显示
* 存在sp中
* 10、自定义ProgressBar的效果
* 上拉加载:
* 1、添加脚布局,默认隐藏
* 2、增加了滚动监听,
* idle,显示最后一个条目的时候,显示脚布局
* 3、通知观察者加载下一页的数据
* 4、加载下一页数据的逻辑
* 将下一页数据的集合添加到上一页数据的集合红,不能new Adapter
* 5、TabDetailPager通知ListView下一页数据加载完成
* 重置isLoadingMore
* 隐藏脚布局
*/ public class RefreshListView extends ListView { public static final int STATE_PULL_TO_REFRESH = 0;
public static final int STATE_RELEASE_TO_REFRESH = 1;
public static final int STATE_REFRESHING = 2; private int mCurrentState = STATE_PULL_TO_REFRESH;//定义ListView当前的状态 private float startY;
private int headerViewHeight;
private View headerView;
private ImageView ivArrow;
private ProgressBar pb;
private TextView tvTips;
private TextView tvDate;
private RotateAnimation downAnimation;
private RotateAnimation upAnimation;
private View footerView;
private int footerViewHeight; public RefreshListView(Context context) {
this(context, null);
} public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
} public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initAnimation();
initFooterView();
} private void initAnimation() {
upAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
upAnimation.setFillAfter(true);
upAnimation.setDuration(200);
downAnimation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
downAnimation.setFillAfter(true);
downAnimation.setDuration(200); } private void initHeaderView() {
//头布局越早添加,位于越上边
headerView = View.inflate(getContext(), R.layout.layout_refresh_header, null);
ivArrow = (ImageView) headerView.findViewById(R.id.ivArrow);
pb = (ProgressBar) headerView.findViewById(R.id.pb);
tvTips = (TextView) headerView.findViewById(R.id.tvTips);
tvDate = (TextView) headerView.findViewById(R.id.tvDate); String lastUpdateTime = PrefUtils.getString(getContext(), "lastUpdateTime", "");
tvDate.setText(lastUpdateTime);
//设置一个控件的高度或者宽度的信息得找LayoutParams
//如果设置一个负的padding的值,只能在代码中设置才会其效果
//measure-layout-draw
//千万不要在Activity的onCreate方法中获取一个控件的宽度或者高度或者位置信息
//监听视图树
/*headerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() { }
});*/
//手动测量
headerView.measure(0, 0);//将测量的工作交给系统来完成,我们不参与任何的限制的意见
//获取测量之后的宽度或者高度信息
headerViewHeight = headerView.getMeasuredHeight();
headerView.setPadding(0, -headerViewHeight, 0, 0);
this.addHeaderView(headerView);
} private boolean isLoadingMore = false; private void initFooterView() {
footerView = View.inflate(getContext(), R.layout.layout_refresh_footer, null);
footerView.measure(0, 0);
footerViewHeight = footerView.getMeasuredHeight();
footerView.setPadding(0, -footerViewHeight, 0, 0);
this.addFooterView(footerView); //给ListView增加一个监听
this.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
int lastVisiblePosition = getLastVisiblePosition();
if (scrollState == SCROLL_STATE_IDLE && lastVisiblePosition == getCount() - 1 && !isLoadingMore) {
//System.out.println("到底了...");
Log.i("RefreshListView", "到底了...");
isLoadingMore = true;
//将脚布局显示出来
footerView.setPadding(0, 0, 0, 0);
//自动滑到脚布局的位置,让脚布局可以一下子就能够看得见
setSelection(getCount() - 1); notifyLoadMore();//通知观察者去加载下一页的数据 }
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { }
});
} //一旦事件到达了一个控件上,一定,最先,会调用dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
startY = ev.getY();//在这个父控件得到事件的时候,就把起点坐标初始化,这样就不会受制于子控件是否消费了事件,起点坐标就会很精确了
}
return super.dispatchTouchEvent(ev);
} //onTouchEvent的来源:
//1、自身拦截 2、子控件回传
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE: if (mCurrentState == STATE_REFRESHING) {
break;
} float moveY = ev.getY();
float dy = moveY - startY;
//什么情况下需要把头布局拖出来
int firstVisiblePosition = getFirstVisiblePosition();
//1、下拉 2、显示的第0个条目是下拉刷新头布局
if (dy > 0 && firstVisiblePosition == 0) {
int paddingTop = (int) (dy - headerViewHeight);
headerView.setPadding(0, paddingTop, 0, 0); int oldState = mCurrentState;
if (paddingTop < 0) {
//头布局有一部分没有显示出来
mCurrentState = STATE_PULL_TO_REFRESH;
} else {
mCurrentState = STATE_RELEASE_TO_REFRESH;
} //在状态发生改变的时候才需要刷新UI
if (oldState != mCurrentState) {
refreshUi();
} return true;//代表消费了事件
}
break;
case MotionEvent.ACTION_UP:
if (mCurrentState == STATE_PULL_TO_REFRESH) {
//将头布局隐藏起来
headerView.setPadding(0, -headerViewHeight, 0, 0);
} else if (mCurrentState == STATE_RELEASE_TO_REFRESH) {
//改变当前的状态,刷新控件
mCurrentState = STATE_REFRESHING;
refreshUi();
//将头布局完全显示出来
headerView.setPadding(0, 0, 0, 0);
//去重写加载网络上的数据
//tabDetailPager.getDataFromServer();
notifyRefresh();
}
break;
}
return super.onTouchEvent(ev);
} public void setOnRefreshComplete(boolean success) {
//1、更新当前的状态
mCurrentState = STATE_PULL_TO_REFRESH;
pb.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
tvTips.setText("下拉刷新");
//2、隐藏头布局
headerView.setPadding(0, -headerViewHeight, 0, 0); if (success) {
//更新tvDate的显示
setCurrentDate();
} } private void setCurrentDate() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String currentDate = sdf.format(new Date());
tvDate.setText(currentDate); PrefUtils.setString(getContext(), "lastUpdateTime", currentDate);
} public void setOnLoadMoreComplete() {
isLoadingMore = false;
//隐藏脚布局
footerView.setPadding(0, -footerViewHeight, 0, 0); } //定义观察者接口
public interface OnRefreshListener {
public void onRefresh(); public void onLoadMore();
} //保存观察者的实例对象
private OnRefreshListener listener; public void setOnRefreshListener(OnRefreshListener listener) {
this.listener = listener;
} //通知观察者
private void notifyRefresh() {
if (listener != null) {
listener.onRefresh();
}
} private void notifyLoadMore() {
if (listener != null) {
listener.onLoadMore();
}
} /*private TabDetailPager tabDetailPager; public void setTabDetailPager(TabDetailPager tabDetailPager) {
this.tabDetailPager = tabDetailPager;
}*/ private void refreshUi() {
switch (mCurrentState) {
case STATE_PULL_TO_REFRESH:
pb.setVisibility(View.INVISIBLE);//INVISIBLE会占位,GONE不会占位
ivArrow.setVisibility(View.VISIBLE);
tvTips.setText("下拉刷新");
ivArrow.startAnimation(downAnimation);
break;
case STATE_RELEASE_TO_REFRESH:
pb.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.startAnimation(upAnimation);
tvTips.setText("松开刷新");
break;
case STATE_REFRESHING:
pb.setVisibility(View.VISIBLE);
ivArrow.clearAnimation();//要控制一个控件的可见度的时候,需要先移除之前设置过的动画
ivArrow.setVisibility(View.INVISIBLE);
tvTips.setText("正在刷新");
break;
}
}
}

layout_refresh_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:gravity="center"
android:orientation="horizontal"
> <ProgressBar
android:id="@+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/shape_progress"/> <TextView
android:id="@+id/tvTips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载"
android:textColor="#F00"
android:textSize="16sp"/> </LinearLayout>

layout_refresh_header.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
> <FrameLayout
android:layout_margin="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"> <ImageView
android:id="@+id/ivArrow"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:src="@drawable/common_listview_headview_red_arrow"
android:layout_height="wrap_content"/> <ProgressBar
android:id="@+id/pb"
android:visibility="invisible"
android:indeterminateDrawable="@drawable/shape_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/> </FrameLayout> <LinearLayout
android:layout_margin="5dp"
android:gravity="center"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content"> <TextView
android:layout_width="wrap_content"
android:text="下拉刷新"
android:id="@+id/tvTips"
android:textColor="#F00"
android:textSize="16sp"
android:layout_height="wrap_content"/> <TextView
android:layout_width="wrap_content"
android:text="2016-12-17"
android:id="@+id/tvDate"
android:textColor="#ccc"
android:textSize="12sp"
android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout>

shape_progress.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="720"
android:pivotY="50%"
android:pivotX="50%"
>
<shape
android:innerRadius="15dp"
android:shape="ring"
android:thickness="3dp"
android:useLevel="false"
>
<!--<solid android:color="@android:"-->
<gradient
android:startColor="#f00"
android:centerColor="#af00"
android:endColor="#fff"
/>
</shape>
</rotate>
package com.loaderman.myrefreshlistviewdemo;

import android.content.Context;
import android.content.SharedPreferences; /**
* 关于SharedPreference的工具类
*/ public class PrefUtils {
public static String getString(Context context,String key,String defValue) {
SharedPreferences sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
String retString = sp.getString(key, defValue);
return retString;
}
public static void setString(Context context,String key ,String value) {
SharedPreferences sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
SharedPreferences.Editor edit = sp.edit();
edit.putString(key, value);
edit.commit();
}
}

代码使用:

package com.loaderman.myrefreshlistviewdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast; import java.util.ArrayList;
import java.util.Random; public class MainActivity extends AppCompatActivity implements RefreshListView.OnRefreshListener { private RefreshListView lvListNews;
private ArrayList mList;
private MyListAdapter myListAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mList = new ArrayList<>();
for (int i = 0; i < 30; i++) {
mList.add("我是天才" + i + "号");
}
lvListNews = (RefreshListView) findViewById(R.id.lvListNews);
lvListNews.setOnRefreshListener(this);
lvListNews.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { }
});
myListAdapter = new MyListAdapter();
lvListNews.setAdapter(myListAdapter);
}
//下拉刷新
@Override
public void onRefresh() {
final Random random = new Random();
mList.add(0, "我是天才" + random.nextInt(100) + "号");
Toast.makeText(MainActivity.this, "刷新了一条数据", Toast.LENGTH_SHORT).show();
//刷新完成
lvListNews.setOnRefreshComplete(true);
myListAdapter.notifyDataSetChanged();
}
//上拉加载
@Override
public void onLoadMore() {
// 添加数据
for (int i = 30; i < 35; i++) {
mList.add("我是天才" + i+ "号");
// 这里要放在里面刷新,放在外面会导致刷新的进度条卡住
myListAdapter.notifyDataSetChanged();
}
//加载完成
lvListNews.setOnLoadMoreComplete();
Toast.makeText(MainActivity.this, "加载了" + 5 + "条数据", Toast.LENGTH_SHORT).show();
}
class MyListAdapter extends BaseAdapter { @Override
public int getCount() {
return mList.size();
} @Override
public Object getItem(int position) {
return mList.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = View.inflate(MainActivity.this, R.layout.item_news_tab_detail, null);
holder = new ViewHolder();
holder.tvContent = (TextView) convertView.findViewById(R.id.tvContent);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvContent.setText(mList.get(position)+"");
return convertView;
}
} static class ViewHolder {
TextView tvContent;
} }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.loaderman.myrefreshlistviewdemo.MainActivity">
<com.loaderman.myrefreshlistviewdemo.RefreshListView
android:id="@+id/lvListNews"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>

效果图:

最新文章

  1. 堆优化的Dijkstra
  2. 利用FFmpeg生成视频缩略图 2.1.6
  3. 原创Java版的Shell
  4. java 生成随机数
  5. EntityFramework之创建数据库及基本操作(一)
  6. Comet技术浅论
  7. How can I save HICON to an .ico file
  8. jQuery插件Jeditable的使用(Struts2处理)
  9. Unable to run mksdcard SDK tool.
  10. Apache Http Server和Tomcat 之区别
  11. 自学javaee程序员之路--ssm的小项目(一)
  12. PL/SQL基础语法入门
  13. Avro序列化与反序列化
  14. python并发编程之进程池,线程池,协程
  15. C#自动识别文件编码
  16. 微软BI 之SSIS 系列 - Lookup 中的字符串比较大小写处理 Case Sensitive or Insensitive
  17. 导出toolStrip1中的图标
  18. PAT1103
  19. 2013 ACM/ICPC 杭州网络赛C题
  20. CAP理论(摘)

热门文章

  1. Mac OSX编译安装php5.6
  2. Some notes of An Insider&#39;s Guide to TOEFL iBT
  3. 09Cookie&amp;Session
  4. uestc summer training #9 牛客第三场 BFS计数
  5. fastai 核心部件
  6. Acwing-96-奇怪的汉诺塔(递推)
  7. LOJ-6282-数列分块入门6
  8. [CF1105E] Helping Hiaset
  9. MVC2新闻
  10. 一次linux站点安装经验