效果

开始用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. 【Codeforces Round #420 (Div. 2) B】Okabe and Banana Trees

    [题目链接]:http://codeforces.com/contest/821/problem/B [题意] 当(x,y)这个坐标中,x和y都为整数的时候; 这个坐标上会有x+y根香蕉; 然后给你一 ...

  2. eclipse maven install 时控制台乱码问题解决

    pom.xml文件中加入: <properties> <argLine>-Dfile.encoding=UTF-8</argLine> <project.bu ...

  3. [Poi] Customize Babel to Build a React App with Poi

    Developing React with Poi is as easy as adding the babel-preset-react-appto a .babelrc and installin ...

  4. Codeforces Round #105 (Div. 2) 148C Terse princess(脑洞)

    C. Terse princess time limit per test 1 second memory limit per test 256 megabytes input standard in ...

  5. bzoj1066【SCOI2007】蜥蜴

    1066: [SCOI2007]蜥蜴 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 2512  Solved: 1238 [Submit][Statu ...

  6. scikit-learn:3.2. Grid Search: Searching for estimator parameters

    參考:http://scikit-learn.org/stable/modules/grid_search.html GridSearchCV通过(蛮力)搜索參数空间(參数的全部可能组合).寻找最好的 ...

  7. idea里新建maven项目时,在new module页面,一直显示loading archetype list...

    不知道什么时候开始,在idea里新建maven项目时,在new module页面,一直显示loading archetype list....,导致一直没办法新建.后来我以为是防火墙问题,各种设置还是 ...

  8. POJ2411 状态压缩dp

    POJ2411 http://poj.org/problem?id=2411

  9. SQL SERVER 2014无法启动T-SQL调试的解决方法(亲自实践)

    将 Windows 登录帐户添加为 sysadmin 已经具有 sysadmin 特权的用户必须执行以下命令: sp_addsrvrolemember 'Domain\Name', 'sysadmin ...

  10. CentOS6.x操作系统自带的 DM Multipath(DMMP)多路径软件配置说明。

    CentOS系统下的多路径软件是操作系统自带的 DM Multipath(DMMP)工具.------------------------------------------------------- ...