效果

开始用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. HTML学习----------DAY1 第一节

    什么是 HTML? HTML 是用来描述网页的一种语言. HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 不是一种编程语言,而是一种标记语言 (ma ...

  2. Intel Media SDK 性能測试

    经过測试,发如今windows 7上 i3 i5 上Intel Media SDK 1080P仅仅能解6路,720P仅仅能解8路, 不知大家有没有測试过?

  3. C++11之decltype

    使用场景 在C++中常常要用到非常长的变量名.假设已经有变量和你将使用的变量是一个类型.就可以使用decltypekeyword 来申明一样的类型变量. decltype原理      返回现有变量类 ...

  4. 控制器不存在:app\admin\controller\Document

    控制器不存在:app\admin\controller\Document 报错: 控制器不存在:app\admin\controller\Document 但是我在代码里面找了半天没出现Documen ...

  5. thinkserer TD350 系统损坏后,数据恢复及系统重做过程

    电脑配置: 联想服务器 TD350   E5-2609V4 2*8G 2*4T+R1 塔式 单电 1.系统恢复: 试过很多种方法,均无效 2.数据恢复:  重新安装系统后,直接在D盘查找 , 原C盘的 ...

  6. MD markdown入门

    1.Headings: 2.Phrase emphasis *italic text* **Bold text** 3.Listing items (在文字之前添加 + , - 或者 * ) -ite ...

  7. weex入门(一)

    emmmm其实没有接触过weex ,了解一番发现有很多坑,有很多基于weex改良后的框架,比如weexplus等等,基本不用踩多少坑.经过几天的深思熟虑我觉得还是去踩坑,毕竟踩完坑才能真正的了解嘛 w ...

  8. UVALive 6869 Repeated Substrings

    Repeated Substrings Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu Descri ...

  9. Nginx+tomcat+ssl免费证书配置

    0.说明 本文说描写叙述的方式是用nginx的443重定向到tomcat的8443,nginx的80port重定到tomcat的8080: 乱入:个人标记:caicongyang 1.nginx安装 ...

  10. SAP学习之路

    此贴记录学习SAP过程~如有错误请指出~ 6.看着网上学习SAP的貌似比較花精力.学习好的话发展前景还是不错的 5.尽管还不是非常懂.可是还是充满期待,期待着java转型abap. 能够在虚拟机上安装 ...