下拉刷新XListView的简单分析
依照这篇博文里的思路分析和理解的
先要理解Scroller,看过的博文:
http://ipjmc.iteye.com/blog/1615828
http://blog.csdn.net/wangjinyu501/article/details/32339379
还要理解View的touch时间传递:
http://www.codekk.com/open-source-project-analysis/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8BView%20%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92
在实现中遇到的问题:
1、下拉时,下拉区域不会尾随下拉而变化,仅仅显示当中一部分。
图:
解决:採用设置下拉区域的paddind,实现尾随滚动效果。终于图:
2、当下拉超过极限高度后向上滑动时。listview会尾随滑动。
解决方法是通过在onTouchEvent推断这一情况推断这一情况,具体在代码里。
代码:
下拉区域布局文件
<?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:orientation="vertical" > <RelativeLayout
android:id="@+id/xlistview_header_content"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_marginBottom="2dp"
android:gravity="center_horizontal" > <TextView
android:id="@+id/xlistview_header_hint_textview"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="正在载入"
android:textColor="@android:color/black"
android:textSize="14sp" /> <ImageView
android:id="@+id/xlistview_header_image"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/xlistview_header_hint_textview"
android:src="@drawable/indicator_arrow" /> <ProgressBar
android:id="@+id/xlistview_header_progressbar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/xlistview_header_hint_textview"
android:visibility="invisible" />
</RelativeLayout> </LinearLayout>
下拉区域
package com.example.test; import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView; public class XListViewHeader extends LinearLayout { private static final String HINT_NORMAL = "下拉刷新";
private static final String HINT_READY = "松开刷新数据";
private static final String HINT_LOADING = "正在载入..."; // 正常状态,下拉未超过head高度
public final static int STATE_NORMAL = 0;
// 准备刷新状态,也就是箭头方向发生改变之后的状态,可是没有刷新
public final static int STATE_READY = 1;
// 刷新状态。箭头变成了progressBar,正在刷新
public final static int STATE_REFRESHING = 2;
// 布局容器,也就是根布局
private LinearLayout mContentLayout;
// 箭头图片
private ImageView mImageView;
// 刷新状态显示
private ProgressBar mProgressBar;
// 说明文本
private TextView mHintTextView;
// 记录当前的状态
private int mState = -1;
// 用于改变箭头的方向的动画
private Animation mRotateUpAnim;
private Animation mRotateDownAnim;
// 动画持续时间
private final int ROTATE_ANIM_DURATION = 180; private int headHeight;
private Context context; public XListViewHeader(Context context) {
super(context);
this.context = context;
init();
} private void init() {
LinearLayout.LayoutParams lp = new LayoutParams(
LayoutParams.MATCH_PARENT, 0);// 初始化高度为0
mContentLayout = (LinearLayout) LayoutInflater.from(context).inflate(
R.layout.xlistview_header, null);
mContentLayout.setLayoutParams(lp);
addView(mContentLayout); mImageView = (ImageView) mContentLayout
.findViewById(R.id.xlistview_header_image);// 箭头图片
mHintTextView = (TextView) mContentLayout
.findViewById(R.id.xlistview_header_hint_textview);// 提示文本
mProgressBar = (ProgressBar) mContentLayout
.findViewById(R.id.xlistview_header_progressbar);// 进度条
mRotateUpAnim = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);// 箭头向上旋转的动画
mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);// 动画持续时间
mRotateUpAnim.setFillAfter(true);// 动画终止时停留在最后,也就是保留动画以后的状态
mRotateDownAnim = new RotateAnimation(-180, 0,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
mRotateDownAnim.setFillAfter(true);
setState(STATE_NORMAL);// 初始化设置为正常模式
} public void setState(int state) {
if (state == mState) {
return;
}
if (state == STATE_REFRESHING) {// 设置为正在刷新状态时,清楚全部动画,箭头隐藏, 进度条显示
mImageView.clearAnimation();
mImageView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
} else {
mImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
switch (state) {
case STATE_NORMAL:
if (mState == STATE_READY) {// 由准备状态变为正常状态。开启向下动画
mImageView.startAnimation(mRotateDownAnim);
} else {
mImageView.clearAnimation();
}
mHintTextView.setText(HINT_NORMAL);
break;
case STATE_READY:
if (mState == STATE_NORMAL) {
mImageView.startAnimation(mRotateUpAnim);
}
mHintTextView.setText(HINT_READY);
break;
case STATE_REFRESHING:
mHintTextView.setText(HINT_LOADING);
break;
}
mState = state;
} @SuppressLint("NewApi")
public void setVisitHeight(int height) {
if (height < 0) {
height = 0;
}
LinearLayout.LayoutParams lp = (LayoutParams) mContentLayout
.getLayoutParams();
lp.height = height;
mContentLayout.setLayoutParams(lp);
mContentLayout.setPadding(mContentLayout.getPaddingLeft(), height
- headHeight, mContentLayout.getPaddingRight(),
mContentLayout.getPaddingBottom());// 设置padding是为了下拉时,head尾随着下拉。更好看
} public int getVisitHeight() {
return mContentLayout.getHeight();
} public void show() {
mContentLayout.setVisibility(View.VISIBLE);
} public void hide() {
mContentLayout.setVisibility(View.INVISIBLE);
} public int getHeadHeight() {
return headHeight;
} public void setHeadHeight(int headHeight) {
this.headHeight = headHeight;
} }
listview
package com.example.test; import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller; public class XListView extends ListView {
private Context context;
// 滑动时长
private final static int SCROLL_DURATION = 400;
// 滑动比例
private final static float OFFSET_RADIO = 2f;
// 记录按下点的y坐标
private float lastY;
// 用来回滚
private Scroller scroller;
private IXListViewListener mListViewListener;
private XListViewHeader headerView;
private RelativeLayout headerViewContent;
// header的高度
private int headerHeight;
// 是否可以刷新
private boolean enableRefresh = true;
// 是否正在刷新
private boolean isRefreashing = false;
// 记录当前手势是向上还是向下
private int TOUCH_UP = 0, TOUCH_DOWN = 1;
private int mTouch; public XListView(Context context) {
super(context);
this.context = context;
init();
} private void init() {
scroller = new Scroller(context, new DecelerateInterpolator());
headerView = new XListViewHeader(context);
headerViewContent = (RelativeLayout) headerView
.findViewById(R.id.xlistview_header_content);
// 获得head的高度
headerView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
headerHeight = headerViewContent.getHeight();
headerView.setHeadHeight(headerHeight);
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
addHeaderView(headerView);
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float t = ev.getRawY() - lastY;
lastY = ev.getRawY();
if (t > 0) {
mTouch = TOUCH_DOWN;
} else {
mTouch = TOUCH_UP;
}
// 当前是第一个item,且手势是向下,就显示下拉条,更新高度
if (getFirstVisiblePosition() == 0
&& (headerView.getVisitHeight() > 0 || t > 0)) {
updateHeaderViewHeight(t / OFFSET_RADIO);
}
if (!isRefreashing && mTouch == TOUCH_UP
&& headerView.getVisitHeight() > 0) {
return true;// 当下拉高度达到header高度时候,松开就可以刷新。若此刻向上滑,listview会尾随滑动,return
// true 代表消费这个事件,listview禁止滚动
}
break;
case MotionEvent.ACTION_UP:
if (getFirstVisiblePosition() == 0) {
if (enableRefresh && headerView.getVisitHeight() > headerHeight) {
isRefreashing = true;
headerView.setState(headerView.STATE_REFRESHING);
if (mListViewListener != null) {
mListViewListener.onRefresh();//刷新事件
}
}
}
resetHeaderHeight();
break;
}
return super.onTouchEvent(ev);
} public void updateHeaderViewHeight(float f) {
headerView.setVisitHeight((int) f + headerView.getVisitHeight());
// 未处于刷新状态,更新箭头
if (enableRefresh && !isRefreashing) {
if (headerView.getVisitHeight() > headerHeight) {
headerView.setState(XListViewHeader.STATE_READY);
}else{
headerView.setState(XListViewHeader.STATE_NORMAL);
}
}
} // 下拉条动态消失
public void resetHeaderHeight() {
int height = headerView.getVisitHeight();
int endheight = 0;
if (isRefreashing) {
endheight = headerHeight;
}
// y轴方向由 height 到 endheight 。第三个參数是增量,假设不是刷新则高度变为0,假设是,高度变为head原始高度
scroller.startScroll(0, height, 0, endheight - height, SCROLL_DURATION);
invalidate();
} public void computeScroll() {
if (scroller.computeScrollOffset()) {
// 利用scroller 。设置高度。重复重绘
headerView.setVisitHeight(scroller.getCurrY());
postInvalidate();
}
super.computeScroll();
} public void stopRefresh() {
if (isRefreashing == true) {
isRefreashing = false;
resetHeaderHeight();
}
} public void setIxListener(IXListViewListener listener) {
this.mListViewListener = listener;
} interface IXListViewListener {
public void onRefresh();// 刷新事件的回调函数
}
}
mainactivity
package com.example.test; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ArrayAdapter; import com.example.test.XListView.IXListViewListener; public class MainActivity extends Activity {
private XListView xListView;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
xListView.stopRefresh();
}
}; protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
xListView = new XListView(this);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_expandable_list_item_1);
xListView.setAdapter(adapter);
for (int i = 0; i < 10; i++) {
adapter.add("text" + i);
}
setContentView(xListView);
xListView.setIxListener(new IXListViewListener() {
public void onRefresh() {
new Thread() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}.start();
}
});
}
}
csdn博文编辑不能撤销么。写的东西都没了
下拉刷新XListView的简单分析的更多相关文章
- mui实现分页上拉加载更多 下拉刷新数据的简单实现 移动端下拉上拉
空下来把mui上拉加载更多,下拉刷新数据做了一个简单的实现,希望可以帮助到需要的朋友 demo项目的结构 <!DOCTYPE html> <html> <head> ...
- IOS UIWebView 下拉刷新功能的简单实现
1.运行效果图 2.swift 代码的实现 import UIKit class RefreshWebViewController: UIViewController,UIScrollViewDele ...
- 手把手教你轻松实现listview下拉刷新
很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview...也不看原理, ...
- iOS下拉刷新和上拉刷新
在iOS开发中,我们经常要用到下拉刷新和上拉刷新来加载新的数据,当前这也适合分页.iOS原生就带有该方法,下面就iOS自带的下拉刷新方法来简单操作. 上拉刷新 1.在TableView里,一打开软件, ...
- Android之XListView下拉刷新,更新网络美女图
一.简介: 下拉刷新是一种特定的手动刷新交互,和其他的同类操作不同的地方在于它采用了更加直觉的下拉操作,所以它的交互足够清晰明显. 下拉刷新主要用在类似ListView这样的控件,设计下拉刷新有三 ...
- XListView下拉刷新和上拉加载更多详解
转载本专栏每一篇博客请注明转载出处地址,尊重原创.博客链接地址:小杨的博客 http://blog.csdn.net/qq_32059827/article/details/53167655 市面上有 ...
- Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件
前言: 忙完了结婚乐APP的开发,终于可以花一定的时间放在博客上了.好了,废话不多说,今天我们要带来的效果是苹果版本的QQ下拉刷新.首先看一下目标效果以及demo效果: 因为此效果实现的步骤 ...
- 下拉刷新和UITableView的section headerView冲突的原因分析与解决方案
UITableView:下拉刷新和上拉加载更多 [转载请注明出处] 本文将说明具有多个section的UITableView在使用下拉刷新机制时会遇到的问题及其解决方案. 工程地址在帖子最下方,只需要 ...
- 简单的下拉刷新以及优化--SwipeRefreshLayout
代码工程简要说明:以一个SwipeRefreshLayout包裹ListView,SwipeRefreshLayout接管ListView的下拉事件,若ListView被用户触发下拉动作后,Swipe ...
随机推荐
- spring IOC 模拟实现
IOC即inverse of control 控制反转 以前对象之间的引用是通过new来调用实现,有了Spring IOC,我们可以把对象之间的引用交给他来管理,这样就把控制权交给了Spring,所以 ...
- java 多线程 27 :多线程组件之CountDownLatch
前言 在多线程环境下,JDK给开发者提供了许多的组件供用户使用(主要在java.util.concurrent下),使得用户不需要再去关心在具体场景下要如何写出同时兼顾线程安全性与高效率的代码.之前讲 ...
- python导外部包
win7+python2.7下安装spark 最近在学习Spark,由于我的Python是在windows下安装的,好多库也已经配置好,所以需要将spark安装在windows环境下,下面是安装过程: ...
- 纯CSS3打造非常炫的加载动画
纯css3打造的一款非常炫的加载图.用在需要一定时间加载的地方非常合适.先上效果图: 点击这里在线预览 代码非常简单.没有用任何javascript代码.纯css3实现. html代码: <di ...
- JBoss Wildfly (1) —— 7.2.0.Final编译
JBoss Wildfly (1) -- 7.2.0.Final编译 wildfly版本: 7.2.0.Final-testsuite-fix jdk版本: jdk1.7.0_79 maven版本: ...
- linux软件管理(六)
[教程主题]:linux软件管理 [1]软件包管理 在系统管理中,软件包的管理是最重要的,是系统管理的基础的基础, 只有我们学会软件包的管理才能谈得上其它的应用. RPM RPM是软件管理程序,提供软 ...
- ajax 多个参数问题,如何既能表单序列化获取,又能加参数,加全部代码
$.param({'address':address,'delivity':delivity,'payment':payment}) + '&' + $('#card_form').ser ...
- 【合集】TiDB 源码阅读系列文章
[合集]TiDB 源码阅读系列文章 (一)序 (二)初识 TiDB 源码 (三)SQL 的一生 (四)INSERT 语句概览 (五)TiDB SQL Parser 的实现 (六)Select 语句概览 ...
- php curl_multi系列函数实现多线程抓取网页
最近几天在做一个多搜索引擎关键字排名查询工具,用于及时方便的了解关键词在各大搜索引擎的排名. 在抓取360搜索的时候,发现360搜索每页只支持显示10个搜索结果,如果想获取100个搜索结果数据,就得搜 ...
- svn 怎么直接同步指定服务器的某个文件夹
背景:首先在svn代码库了,我直接从服务器上拖了一个文件夹下来,但是没有显示svn相关信息(比如:左下角的勾勾).这个时候因为直接拖动下来的时候没有生成对应的.svn文件,所以导致. 解决办法: 选中 ...