效果

开始用Android Studio写了,还有挺多不明白这IDE用法的地方。。。。蛋疼

主要思路

  1. 添加了自定义的头布局
    2. 默认让头布局隐藏setPadding.设置 -自身的高度
    3. ListView下拉的时候, 修改paddingTop, 让头布局显示出来
    4. 触摸动态修改头布局, 根据paddingTop.  
        - paddingTop = 0 完全显示
        - paddingTop < 不完全显示 -64(自身高度)完全隐藏
        - paddingTop > 0 顶部空白

5. 松手之后根据当前的paddingTop决定是否执行刷新
        - paddingTop < 0 不完全显示, 恢复
        - paddingTop >= 0 完全显示, 执行正在刷新...

其实就是通过设置padding控制头布局和脚步局的位置

一:先写头布局和脚步局

layout_header

<?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="horizontal"> <FrameLayout
android:layout_margin="5dp"
android:layout_width="50dp"
android:layout_height="50dp" > <ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/common_listview_headview_red_arrow" /> <ProgressBar
android:id="@+id/pb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminateDrawable="@drawable/shape_progress"
android:visibility="invisible" />
</FrameLayout> <LinearLayout
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" > <TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="下拉刷新"
android:textColor="#F00"
android:textSize="18sp" /> <TextView
android:id="@+id/tv_desc_last_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:singleLine="true"
android:text="最后刷新时间: 2015-10-11 09:20:35"
android:textColor="#666"
android:textSize="14sp" />
</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:pivotX="50%"
android:pivotY="50%"
android:toDegrees="-360" > <!-- android:innerRadius="20dp" -->
<!-- android:thickness="5dp" -->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadiusRatio="2.5"
android:shape="ring"
android:thicknessRatio="10"
android:useLevel="false" >
<gradient
android:centerColor="#44FF0000"
android:endColor="#00000000"
android:startColor="#FF0000"
android:type="sweep" />
</shape> </rotate>

layout_footer

<?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="wrap_content"
android:gravity="center"
android:orientation="horizontal" > <ProgressBar
android:layout_margin="5dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminateDrawable="@drawable/shape_progress" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载更多..."
android:textColor="#F00"
android:layout_marginLeft="15dp"
android:textSize="18sp" /> </LinearLayout>

二。接下来是自定义的RefreshListView

import java.text.SimpleDateFormat;

import android.content.Context;
import android.util.AttributeSet;
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.AbsListView.OnScrollListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView; import com.itheima74.refreshlist.R; /**
* 包含下拉刷新功能的ListView
* @author
*
*/
public class RefreshListView extends ListView implements OnScrollListener{ private View mHeaderView; // 头布局
private float downY; // 按下的y坐标
private float moveY; // 移动后的y坐标
private int mHeaderViewHeight; // 头布局高度
public static final int PULL_TO_REFRESH = 0;// 下拉刷新
public static final int RELEASE_REFRESH = 1;// 释放刷新
public static final int REFRESHING = 2; // 刷新中
private int currentState = PULL_TO_REFRESH; // 当前刷新模式
private RotateAnimation rotateUpAnim; // 箭头向上动画
private RotateAnimation rotateDownAnim; // 箭头向下动画
private View mArrowView; // 箭头布局
private TextView mTitleText; // 头布局标题
private ProgressBar pb; // 进度指示器
private TextView mLastRefreshTime; // 最后刷新时间
private OnRefreshListener mListener; // 刷新监听
private View mFooterView; // 脚布局
private int mFooterViewHeight; // 脚布局高度
private boolean isLoadingMore; // 是否正在加载更多 public RefreshListView(Context context) {
super(context);
init();
} public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} /**
* 初始化头布局, 脚布局
* 滚动监听
*/
private void init() {
initHeaderView();
initAnimation(); initFooterView(); setOnScrollListener(this);
} /**
* 初始化脚布局
*/
private void initFooterView() {
mFooterView = View.inflate(getContext(), R.layout.layout_footer_list, null); mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getMeasuredHeight(); // 隐藏脚布局
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0); addFooterView(mFooterView);
} /**
* 初始化头布局的动画
*/
private void initAnimation() {
// 向上转, 围绕着自己的中心, 逆时针旋转0 -> -180.
rotateUpAnim = new RotateAnimation(0f, -180f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateUpAnim.setDuration(300);
rotateUpAnim.setFillAfter(true); // 动画停留在结束位置 // 向下转, 围绕着自己的中心, 逆时针旋转 -180 -> -360
rotateDownAnim = new RotateAnimation(-180f, -360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateDownAnim.setDuration(300);
rotateDownAnim.setFillAfter(true); // 动画停留在结束位置 } /**
* 初始化头布局
*/
private void initHeaderView() { mHeaderView = View.inflate(getContext(), R.layout.layout_header_list, null);
mArrowView = mHeaderView.findViewById(R.id.iv_arrow);
pb = (ProgressBar) mHeaderView.findViewById(R.id.pb);
mTitleText = (TextView) mHeaderView.findViewById(R.id.tv_title);
mLastRefreshTime = (TextView) mHeaderView.findViewById(R.id.tv_desc_last_refresh); // 提前手动测量宽高
mHeaderView.measure(0, 0);// 按照设置的规则测量 mHeaderViewHeight = mHeaderView.getMeasuredHeight();
System.out.println(" measuredHeight: " + mHeaderViewHeight); // 设置内边距, 可以隐藏当前控件 , -自身高度
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0); // 在设置数据适配器之前执行添加 头布局/脚布局 的方法.
addHeaderView(mHeaderView);
} @Override
public boolean onTouchEvent(MotionEvent ev) { // 判断滑动距离, 给Header设置paddingTop
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();
System.out.println("downY: " + downY); break;
case MotionEvent.ACTION_MOVE:
moveY = ev.getY();
System.out.println("moveY: " + moveY);
// 如果是正在刷新中, 就执行父类的处理
if(currentState == REFRESHING){
return super.onTouchEvent(ev);
} float offset = moveY - downY; // 移动的偏移量
// 只有 偏移量>0, 并且当前第一个可见条目索引是0, 才放大头部
if(offset > 0 && getFirstVisiblePosition() == 0){ // int paddingTop = -自身高度 + 偏移量 int paddingTop = (int) (- mHeaderViewHeight + offset);
mHeaderView.setPadding(0, paddingTop, 0, 0); if(paddingTop >= 0 && currentState != RELEASE_REFRESH){// 头布局完全显示
System.out.println("切换成释放刷新模式: " + paddingTop);
// 切换成释放刷新模式
currentState = RELEASE_REFRESH;
updateHeader(); // 根据最新的状态值更新头布局内容
}else if(paddingTop < 0 && currentState != PULL_TO_REFRESH){ // 头布局不完全显示
System.out.println("切换成下拉刷新模式: " + paddingTop);
// 切换成下拉刷新模式
currentState = PULL_TO_REFRESH;
updateHeader(); // 根据最新的状态值更新头布局内容
} return true; // 当前事件被我们处理并消费
} break;
case MotionEvent.ACTION_UP: // 根据刚刚设置状态
if(currentState == PULL_TO_REFRESH){
// - paddingTop < 0 不完全显示, 恢复
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
}else if(currentState == RELEASE_REFRESH){
// - paddingTop >= 0 完全显示, 执行正在刷新...
mHeaderView.setPadding(0, 0, 0, 0);
currentState = REFRESHING;
updateHeader();
}
break; default:
break;
} return super.onTouchEvent(ev);
} /**
* 根据状态更新头布局内容
*/
private void updateHeader() {
switch (currentState) {
case PULL_TO_REFRESH: // 切换回下拉刷新
// 做动画, 改标题
mArrowView.startAnimation(rotateDownAnim);
mTitleText.setText("下拉刷新"); break;
case RELEASE_REFRESH: // 切换成释放刷新
// 做动画, 改标题
mArrowView.startAnimation(rotateUpAnim);
mTitleText.setText("释放刷新"); break;
case REFRESHING: // 刷新中...
mArrowView.clearAnimation();
mArrowView.setVisibility(View.INVISIBLE);
pb.setVisibility(View.VISIBLE);
mTitleText.setText("正在刷新中..."); if(mListener != null){
mListener.onRefresh(); // 通知调用者, 让其到网络加载更多数据.
} break; default:
break;
}
} /**
* 刷新结束, 恢复界面效果
*/
public void onRefreshComplete() {
if(isLoadingMore){
// 加载更多
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
isLoadingMore = false;
}else {
// 下拉刷新
currentState = PULL_TO_REFRESH;
mTitleText.setText("下拉刷新"); // 切换文本
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏头布局
pb.setVisibility(View.INVISIBLE);
mArrowView.setVisibility(View.VISIBLE);
String time = getTime();
mLastRefreshTime.setText("最后刷新时间: " + time);
} } private String getTime() {
long currentTimeMillis = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(currentTimeMillis);
} public interface OnRefreshListener{ void onRefresh(); // 下拉刷新 void onLoadMore();// 加载更多
} public void setRefreshListener(OnRefreshListener mListener) {
this.mListener = mListener;
} // public static int SCROLL_STATE_IDLE = 0; // 空闲
// public static int SCROLL_STATE_TOUCH_SCROLL = 1; // 触摸滑动
// public static int SCROLL_STATE_FLING = 2; // 滑翔
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 状态更新的时候
System.out.println("scrollState: " + scrollState);
if(isLoadingMore){
return; // 已经在加载更多.返回
} // 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多
if(scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)){
isLoadingMore = true;
System.out.println("scrollState: 开始加载更多");
mFooterView.setPadding(0, 0, 0, 0); setSelection(getCount()); // 跳转到最后一条, 使其显示出加载更多. if(mListener != null){
mListener.onLoadMore();
}
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 滑动过程
} }

思路是,先设置padding让头隐藏,然后根据 onTouchEvent的动作,判断出头布局应该处于的状态。脚步局也是如此。

这里,定义了接口

public interface OnRefreshListener{

void onRefresh(); // 下拉刷新

void onLoadMore();// 加载更多

}

这些方法是为了让调用者使用

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 状态更新的时候
System.out.println("scrollState: " + scrollState);
if(isLoadingMore){
return; // 已经在加载更多.返回
} // 最新状态是空闲状态, 并且当前界面显示了所有数据的最后一条. 加载更多
if(scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)){
isLoadingMore = true;
System.out.println("scrollState: 开始加载更多");
mFooterView.setPadding(0, 0, 0, 0); setSelection(getCount()); // 跳转到最后一条, 使其显示出加载更多. if(mListener != null){
mListener.onLoadMore();
}
}
} 这里我们看到有个 if(mListener != null){ mListener.onLoadMore(); }
但是onLoadMore()方法到底在复写的呢,我们可以看下mainactivity
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.TextView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity {
private RefreshListView listview;
private ArrayList<String> listDatas;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
listDatas = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
listDatas.add("这是一条ListView数据: " + i);
}
listview = (RefreshListView) findViewById(R.id.listview);
listview.setRefreshListener(new RefreshListView.OnRefreshListener() { @Override
public void onRefresh() {
new Thread(){
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} listDatas.add(0,"我是下拉刷新出来的数据!"); runOnUiThread(new Runnable() { @Override
public void run() {
adapter.notifyDataSetChanged();
listview.onRefreshComplete();
}
});
}; }.start();
} @Override
public void onLoadMore() {
new Thread(){
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} listDatas.add("我是加载更多出来的数据!1");
listDatas.add("我是加载更多出来的数据!2");
listDatas.add("我是加载更多出来的数据!3"); runOnUiThread(new Runnable() { @Override
public void run() {
adapter.notifyDataSetChanged();
listview.onRefreshComplete();
}
});
}; }.start();
} }); adapter = new MyAdapter();
listview.setAdapter(adapter); } class MyAdapter extends BaseAdapter { @Override
public int getCount() {
return listDatas.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = new TextView(parent.getContext());
textView.setTextSize(18f);
textView.setText(listDatas.get(position)); return textView;
} @Override
public Object getItem(int position) {
return listDatas.get(position);
} @Override
public long getItemId(int position) {
return position;
} }
}

在这里,通过自定义Listview的setRefreshListener的方法,传入一个匿名类进行复写了接口要实现的方法。

这是一种回调机智。

简单的说像是,listView内部给定了一个接口,像是规定了一个规范,然后使用listview的类,如果想要实现某种功能,必须实现这个规范中的方法。

像是我们考试,老师给试卷上印好了姓名和学号填空栏目,我们使用者每个人在栏目里写上自己的姓名和学号,再交试卷

比如,我们button的点击事件

btn1.setOnClickListener(new OnClickListener() {

      @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast tst = Toast.makeText(TestButtonActivity.this, "111111111", Toast.LENGTH_SHORT);
tst.show(); }
});

老师=谷歌开发人员,写button类的

试卷=button

姓名学号栏目=接口OnClickListener中onclick方法

我们姓名学号=我们自己复写的onclick具体执行什么

自定义控件学习——下拉刷新ListView的更多相关文章

  1. 滚动到底部加载更多及下拉刷新listview的使用

    最新内容建议直接访问原文:滚动到底部加载更多及下拉刷新listview的使用 本文主要介绍可同时实现下拉刷新及滑动到底部加载更多的ListView的使用. 该ListView优点包括:a. 可自定义下 ...

  2. Android 自定义下拉刷新ListView

    package com.dwtedx.qq.view; import android.content.Context; import android.util.AttributeSet; import ...

  3. android学习---下拉刷新组建

    Google官方的下拉刷新组建 activity代码实现: /** * The SwipeRefreshLayout should be used whenever the user * can re ...

  4. 下拉刷新ListView实现原理

    (1)主要是onScroll()方法和onTouchEvent()方法,先是onTouchEvent()的ACTION_DOWN,然后是 ACTION_MOVE和onScroll()方法同时进行,最后 ...

  5. 下拉刷新Listview(8.30)

    Android-PullToRefresh 1项目托管地址: https://github.com/bavariama1/Android-PullToRefresh 2 快速开始教程:https:// ...

  6. 打造通用的Android下拉刷新组件(适用于ListView、GridView等各类View)

    前言 近期在做项目时,使用了一个开源的下拉刷新ListView组件.极其的不稳定,bug还多.稳定的组件又写得太复杂了,jar包较大.在我的一篇博客中也讲述过下拉刷新的实现,即Android打造(Li ...

  7. Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

    RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...

  8. google官方的下拉刷新+自定义上拉加载更多

    转载请标注转载:http://blog.csdn.net/oqihaogongyuan/article/details/50949118 google官方的下拉刷新+自定义上拉加载更多 现在很多app ...

  9. 安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)

    前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添 ...

随机推荐

  1. hbase启动报错

    前一段时间vmware上的ubuntu的hbase用不了了,而hadoop能正常的操作,非常的奇怪. 错误信息好像是connect fail, RPC什么的,看来跟网路有关. 想起以前曾经解决过hba ...

  2. POJ 2826 An Easy Problem!(简单数论)

    Description Have you heard the fact "The base of every normal number system is 10" ? Of co ...

  3. 通过C语言程序改动控制台的背景和前景颜色

    本文主要解说怎样通过C语言来改动dos背景和前景颜色.我们首先来看一下dos的背景颜色的属性. 打开開始菜单,点击执行,弹出执行对话框.输入cmd,回车. (打开dos控制台) 在命令提示符界面下,输 ...

  4. 在Fedora18上配置个人的Hadoop开发环境

    在Fedora18上配置个人的Hadoop开发环境 1.    背景 文章中讲述了类似于"personalcondor"的一种"personal hadoop" ...

  5. excel操作小技巧

    excel拼接sql语句时,时间格式问题 问题:若直接插入时间的单元格 :="insert into t_entity_car (create_time,name,age) value (' ...

  6. Reference Counting GC (Part two :Partial Mark & Sweep)

    目录 部分标记清除算法 前提 dec_ref_cnt()函数 new_obj()函数 scan_hatch_queue()函数 paint_gray()函数 scan_gray()函数 collect ...

  7. 错误:created a ThreadLocal with key of type ……but failed to remove it when the web application was stopped. This is very likely to create a memory leak.

    tomcat reload显示错误:SEVERE: The web application [/Interceptor] created a ThreadLocal with key of type ...

  8. HDU 5375 Gray code (简单dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5375 题面: Gray code Time Limit: 2000/1000 MS (Java/Oth ...

  9. UNIX多线程编程

    一个程序至少有一个进程.一个进程至少有一个线程.进程拥有自己独立的存储空间,而线程能够看作是轻量级的进程,共享进程内的全部资源.能够把进程看作一个工厂.线程看作工厂内的各个车间,每一个车间共享整个工厂 ...

  10. stl--vector 操作实现

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/ ...