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>

效果图:

自定义ListView实现下拉刷新,下拉加载的功能的更多相关文章

  1. jQuery WeUI 组件下拉刷新和滚动加载的实现

    最近在做手机版使用到了下拉刷新和滚动加载,记录一下实现过程: 一.引入文件 ? 1 2 3 4 <link rel="stylesheet" href="Conte ...

  2. ListView上拉刷新和分页加载完整的Dome

    很多人工作的过程中都会碰到ListView下拉刷新和分页加载,然后大多数公司都已经把框架写好了,大家直接用就可以了,有些人一直对这个事情处于迷茫状态,为了让大家对上拉刷新和分页加载有一个比较全面的认识 ...

  3. 第三方 XListview 上拉加载、下拉刷新、分页加载和Gson解析

    注意:此Demo用的是第三方的Xlistview.jar,需要复制me文件夹到项目中,两个XML布局文件和一张图片 把下面的复制到String中 <string name="xlist ...

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

    在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/de ...

  5. SwipeRefreshLayout实现下拉刷新上滑加载

    1. 效果图 2.RefreshLayout.java package myapplication.com.myapplication; import android.content.Context; ...

  6. mui 动态加载数据出现的问题处理 (silder轮播组件 indexedList索引列表 下拉刷新不能继续加载数据)

    mui-slider 问题:动态给mui的图片轮播添加图片,轮播不滚动. 解决:最后把滚动轮播图片的mui(".mui-slider").slider({interval: 300 ...

  7. 微信小程序下拉刷新 并重新加载数据

    1.在json页面配置: { "enablePullDownRefresh": true } 2.调用刷新函数 onPullDownRefresh: function() { wx ...

  8. juery下拉刷新,div加载更多元素并添加点击事件(二)

    buffer.append("<div class='col-xs-3 "+companyId+"' style='padding-left: 10px; padd ...

  9. Vue Scroller:Vue 下拉刷新及无限加载组件

    Vue Scroller Vue Scroller is a foundational component ofVonic UI. In purpose of smooth scrolling, pu ...

  10. Vue 下拉刷新及无限加载组件

    原文  https://github.com/wangdahoo/vue-scroller 主题 Vue.js Vue Scroller Vue Scroller is a foundational ...

随机推荐

  1. Spring框架中<mvc:default-servlet-handler/>的作用

    优雅REST风格的资源URL不希望带 .html 或 .do 等后缀.由于早期的Spring MVC不能很好地处理静态资源,所以在web.xml中配置DispatcherServlet的请求映射,往往 ...

  2. vi编辑器中删除文件中所有字符

    在命令模式下,将光标移动到文档最上方(使用gg命令),然后输入dG,删除工作区内所有缓存数据. 如果想要删除某行文档以下的内容,将光标移动到文档相应行,然后输入dG即可.

  3. 全球首次!玩5G日本来了一波骚操作

    5G基站信号覆盖范围较小是5G技术应用中需要面临的问题之一,从目前的报道来看,在人口密集的城市中其理想覆盖范围只有250米左右,这也就意味着5G基站的数量和密度相比4G要成倍的增加. 可以想象,当5G ...

  4. 02-jar包操作---引用本地包--maven项目

    在idea工具中,普通项目的话,直接在jar上右键add as library就行了. 如果是maven项目 可以将包,放入lib目录下,然后在pom文件配置引用.例子: <!--引入非本地仓库 ...

  5. trigger添加及表达式

    创建触发器 点击Configuration(配置) → Hosts(主机) 点击hosts(主机)相关行的trigger 点击右上角的创建触发器(create trigger) name : 触发器名 ...

  6. tensorflow各版本下载地址

    https://pypi.org/project/tensorflow-gpu/1.13.0/#files 把13改对你想要的版本

  7. C++构造函数实例

    #include<iostream> #include <string> using namespace std; class Person { public: //无参(默认 ...

  8. Java中遍历HashMap方式

    本教程将为你展示Java中HashMap的几种典型遍历方式. 如果你使用Java8,由于该版本JDK支持lambda表达式,可以采用第5种方式来遍历. 如果你想使用泛型,可以参考方法3.如果你使用旧版 ...

  9. i3wm

    1.音量调节(alsa-utils) alsamixer: alsamixer is a graphical mixer program for the Advanced Linux Sound Ar ...

  10. head first 设计模式笔记8-模板方法模式

    模板设计模式:就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现. 优点:使用模板方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求. 缺点:如果算法骨架有修改的 ...