这篇文章主要围绕下拉刷新&SBJson的使用和实现下拉刷新展开,旨在为您提供一份详细的参考资料。我们将全面介绍下拉刷新&SBJson的使用的优缺点,解答实现下拉刷新的相关问题,同时也会为您带来ando
这篇文章主要围绕下拉刷新 & SBJson 的使用和实现下拉刷新展开,旨在为您提供一份详细的参考资料。我们将全面介绍下拉刷新 & SBJson 的使用的优缺点,解答实现下拉刷新的相关问题,同时也会为您带来andorid Activity 实现上拉刷新的效果,但是不用上下拉刷新控件、android ActionBar-PullToRefresh 下拉刷新的使用方法、Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能、android开发教程之实现listview下拉刷新和上拉刷新效果的实用方法。
本文目录一览:- 下拉刷新 & SBJson 的使用(实现下拉刷新)
- andorid Activity 实现上拉刷新的效果,但是不用上下拉刷新控件
- android ActionBar-PullToRefresh 下拉刷新的使用方法
- Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能
- android开发教程之实现listview下拉刷新和上拉刷新效果
下拉刷新 & SBJson 的使用(实现下拉刷新)
http://www.linuxidc.com/Linux/2013-04/83047.htm 下拉刷新
http://blog.csdn.net/jiarusun000/article/details/7712364 SBJson的简单使用
andorid Activity 实现上拉刷新的效果,但是不用上下拉刷新控件
OSC 请你来轰趴啦!1028 苏州源创会,一起寻宝 AI 时代
android ActionBar-PullToRefresh 下拉刷新的使用方法
这个最终效果, 图片来自http://a.code4app.com/android/ActionBar-PullToRefresh/524f9a156803faab3c000000
由于对于导包不是很了解,这个下拉刷新花费了我很多时间。
其实很简单,也就是两个类,首先你要下载ActionBar-PullToRefresh 点击打开链接
然后还要下载 ActionBarSherlock 点击打开链接
然后打开ActionBar-PullToRefresh 下的项目 : C:\Users\Administrator\Desktop\ActionBar-PullToRefresh-master\ActionBar-PullToRefresh-master\samples\stock
不过同时,你要先把ActionBarSherlock导入到ActionBar-PullToRefresh的根目录
那下面就好做了
[java]
/*
* Copyright 2013 Chris Banes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.co.senab.actionbarpulltorefresh.samples.stock;
import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher;
/**
* This sample shows how to use ActionBar-PullToRefresh with a
* {@link android.widget.ListView ListView}, and manually creating (and attaching) a
* {@link PullToRefreshAttacher} to the view.
*/
public class ListViewActivity extends ListActivity
implements PullToRefreshAttacher.OnRefreshListener {
private static String[] ITEMS = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
"Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega''l Pitu",
"Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler", "Abbaye de Belloc",
"Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost",
"Affidelice au Chablis", "Afuega''l Pitu", "Airag", "Airedale", "Aisy Cendre",
"Allgauer Emmentaler"};
private PullToRefreshAttacher mPullToRefreshAttacher;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Get ListView and give it an adapter to display the sample items
*/
ListView listView = getListView();
ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
ITEMS);
listView.setAdapter(adapter);
/**
* Here we create a PullToRefreshAttacher manually without an Options instance.
* PullToRefreshAttacher will manually create one using default values.
*/
mPullToRefreshAttacher = new PullToRefreshAttacher(this);
// Set the Refreshable View to be the ListView and the refresh listener to be this.
mPullToRefreshAttacher.setRefreshableView(listView, this);
}
@Override
public void onRefreshStarted(View view) {
/**
* Simulate Refresh with 4 seconds sleep
*/
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// Notify PullToRefreshAttacher that the refresh has finished
mPullToRefreshAttacher.setRefreshComplete();
}
}.execute();
}
}
这是下拉刷新的一个类
Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能
转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/9255575
最近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不 怎么理想。有些是因为功能不完整或有Bug,有些是因为使用起来太复杂,十全十美的还真没找到。因此我也是放弃了在网上找现成代码的想法,自己花功夫编写 了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享一下。相信在阅读完本篇文章之后,大家都可以在自己的项目中一分钟引入下拉刷新功能。
首先讲一下实现原理。这里我们将采取的方案是使用组合View的方式,先自定义一个布局继承自LinearLayout,然后在这个布局中加入下拉 头和ListView这两个子元素,并让这两个子元素纵向排列。初始化的时候,让下拉头向上偏移出屏幕,这样我们看到的就只有ListView了。然后对 ListView的touch事件进行监听,如果当前ListView已经滚动到顶部并且手指还在向下拉的话,那就将下拉头显示出来,松手后进行刷新操 作,并将下拉头隐藏。原理示意图如下:
那我们现在就来动手实现一下,新建一个项目起名叫PullToRefreshTest,先在项目中定义一个下拉头的布局文件pull_to_refresh.xml,代码如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/pull_to_refresh_head"
android:layout_width="fill_parent"
android:layout_height="60dip" >
<LinearLayout
android:layout_width="200dip"
android:layout_height="60dip"
android:layout_centerInParent="true"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="0dip"
android:layout_height="60dip"
android:layout_weight="3"
>
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/arrow"
/>
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_centerInParent="true"
android:visibility="gone"
/>
</RelativeLayout>
<LinearLayout
android:layout_width="0dip"
android:layout_height="60dip"
android:layout_weight="12"
android:orientation="vertical" >
<TextView
android:id="@+id/description"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_horizontal|bottom"
android:text="@string/pull_to_refresh" />
<TextView
android:id="@+id/updated_at"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_horizontal|top"
android:text="@string/updated_at" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
在这个布局中,我们包含了一个下拉指示箭头,一个下拉状态文字提示,和一个上次更新的时间。当然,还有一个隐藏的旋转进度条,只有正在刷新的时候我们才会将它显示出来。
布局中所有引用的字符串我们都放在strings.xml中,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PullToRefreshTest</string>
<string name="pull_to_refresh">下拉可以刷新</string>
<string name="release_to_refresh">释放立即刷新</string>
<string name="refreshing">正在刷新…</string>
<string name="not_updated_yet">暂未更新过</string>
<string name="updated_at">上次更新于%1$s前</string>
<string name="updated_just_now">刚刚更新</string>
<string name="time_error">时间有问题</string>
</resources>
然后新建一个RefreshableView继承自LinearLayout,代码如下所示:
public class RefreshableView extends LinearLayout implements OnTouchListener {
/**
* 下拉状态
*/
public static final int STATUS_PULL_TO_REFRESH = 0;
/**
* 释放立即刷新状态
*/
public static final int STATUS_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态
*/
public static final int STATUS_REFRESHING = 2;
/**
* 刷新完成或未刷新状态
*/
public static final int STATUS_REFRESH_FINISHED = 3;
/**
* 下拉头部回滚的速度
*/
public static final int SCROLL_SPEED = -20;
/**
* 一分钟的毫秒值,用于判断上次的更新时间
*/
public static final long ONE_MINUTE = 60 * 1000;
/**
* 一小时的毫秒值,用于判断上次的更新时间
*/
public static final long ONE_HOUR = 60 * ONE_MINUTE;
/**
* 一天的毫秒值,用于判断上次的更新时间
*/
public static final long ONE_DAY = 24 * ONE_HOUR;
/**
* 一月的毫秒值,用于判断上次的更新时间
*/
public static final long ONE_MONTH = 30 * ONE_DAY;
/**
* 一年的毫秒值,用于判断上次的更新时间
*/
public static final long ONE_YEAR = 12 * ONE_MONTH;
/**
* 上次更新时间的字符串常量,用于作为SharedPreferences的键值
*/
private static final String UPDATED_AT = "updated_at";
/**
* 下拉刷新的回调接口
*/
private PullToRefreshListener mListener;
/**
* 用于存储上次更新时间
*/
private SharedPreferences preferences;
/**
* 下拉头的View
*/
private View header;
/**
* 需要去下拉刷新的ListView
*/
private ListView listView;
/**
* 刷新时显示的进度条
*/
private ProgressBar progressBar;
/**
* 指示下拉和释放的箭头
*/
private ImageView arrow;
/**
* 指示下拉和释放的文字描述
*/
private TextView description;
/**
* 上次更新时间的文字描述
*/
private TextView updateAt;
/**
* 下拉头的布局参数
*/
private MarginLayoutParams headerLayoutParams;
/**
* 上次更新时间的毫秒值
*/
private long lastUpdateTime;
/**
* 为了防止不同界面的下拉刷新在上次更新时间上互相有冲突,使用id来做区分
*/
private int mId = -1;
/**
* 下拉头的高度
*/
private int hideHeaderHeight;
/**
* 当前处理什么状态,可选值有STATUS_PULL_TO_REFRESH, STATUS_RELEASE_TO_REFRESH,
* STATUS_REFRESHING 和 STATUS_REFRESH_FINISHED
*/
private int currentStatus = STATUS_REFRESH_FINISHED;;
/**
* 记录上一次的状态是什么,避免进行重复操作
*/
private int lastStatus = currentStatus;
/**
* 手指按下时的屏幕纵坐标
*/
private float yDown;
/**
* 在被判定为滚动之前用户手指可以移动的最大值。
*/
private int touchSlop;
/**
* 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
*/
private boolean loadOnce;
/**
* 当前是否可以下拉,只有ListView滚动到头的时候才允许下拉
*/
private boolean ableToPull;
/**
* 下拉刷新控件的构造函数,会在运行时动态添加一个下拉头的布局。
*
* @param context
* @param attrs
*/
public RefreshableView(Context context, AttributeSet attrs) {
super(context, attrs);
preferences = PreferenceManager.getDefaultSharedPreferences(context);
header = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh, null, true);
progressBar = (ProgressBar) header.findViewById(R.id.progress_bar);
arrow = (ImageView) header.findViewById(R.id.arrow);
description = (TextView) header.findViewById(R.id.description);
updateAt = (TextView) header.findViewById(R.id.updated_at);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
refreshUpdatedAtValue();
setOrientation(VERTICAL);
addView(header, 0);
}
/**
* 进行一些关键性的初始化操作,比如:将下拉头向上偏移进行隐藏,给ListView注册touch事件。
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed && !loadOnce) {
hideHeaderHeight = -header.getHeight();
headerLayoutParams = (MarginLayoutParams) header.getLayoutParams();
headerLayoutParams.topMargin = hideHeaderHeight;
listView = (ListView) getChildAt(1);
listView.setOnTouchListener(this);
loadOnce = true;
}
}
/**
* 当ListView被触摸时调用,其中处理了各种下拉刷新的具体逻辑。
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
setIsAbleToPull(event);
if (ableToPull) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
yDown = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float yMove = event.getRawY();
int distance = (int) (yMove - yDown);
// 如果手指是下滑状态,并且下拉头是完全隐藏的,就屏蔽下拉事件
if (distance <= 0 && headerLayoutParams.topMargin <= hideHeaderHeight) {
return false;
}
if (distance < touchSlop) {
return false;
}
if (currentStatus != STATUS_REFRESHING) {
if (headerLayoutParams.topMargin > 0) {
currentStatus = STATUS_RELEASE_TO_REFRESH;
} else {
currentStatus = STATUS_PULL_TO_REFRESH;
}
// 通过偏移下拉头的topMargin值,来实现下拉效果
headerLayoutParams.topMargin = (distance / 2) + hideHeaderHeight;
header.setLayoutParams(headerLayoutParams);
}
break;
case MotionEvent.ACTION_UP:
default:
if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
// 松手时如果是释放立即刷新状态,就去调用正在刷新的任务
new RefreshingTask().execute();
} else if (currentStatus == STATUS_PULL_TO_REFRESH) {
// 松手时如果是下拉状态,就去调用隐藏下拉头的任务
new HideHeaderTask().execute();
}
break;
}
// 时刻记得更新下拉头中的信息
if (currentStatus == STATUS_PULL_TO_REFRESH
|| currentStatus == STATUS_RELEASE_TO_REFRESH) {
updateHeaderView();
// 当前正处于下拉或释放状态,要让ListView失去焦点,否则被点击的那一项会一直处于选中状态
listView.setPressed(false);
listView.setFocusable(false);
listView.setFocusableInTouchMode(false);
lastStatus = currentStatus;
// 当前正处于下拉或释放状态,通过返回true屏蔽掉ListView的滚动事件
return true;
}
}
return false;
}
/**
* 给下拉刷新控件注册一个监听器。
*
* @param listener
* 监听器的实现。
* @param id
* 为了防止不同界面的下拉刷新在上次更新时间上互相有冲突, 请不同界面在注册下拉刷新监听器时一定要传入不同的id。
*/
public void setOnRefreshListener(PullToRefreshListener listener, int id) {
mListener = listener;
mId = id;
}
/**
* 当所有的刷新逻辑完成后,记录调用一下,否则你的ListView将一直处于正在刷新状态。
*/
public void finishRefreshing() {
currentStatus = STATUS_REFRESH_FINISHED;
preferences.edit().putLong(UPDATED_AT + mId, System.currentTimeMillis()).commit();
new HideHeaderTask().execute();
}
/**
* 根据当前ListView的滚动状态来设定 {@link #ableToPull}
* 的值,每次都需要在onTouch中第一个执行,这样可以判断出当前应该是滚动ListView,还是应该进行下拉。
*
* @param event
*/
private void setIsAbleToPull(MotionEvent event) {
View firstChild = listView.getChildAt(0);
if (firstChild != null) {
int firstVisiblePos = listView.getFirstVisiblePosition();
if (firstVisiblePos == 0 && firstChild.getTop() == 0) {
if (!ableToPull) {
yDown = event.getRawY();
}
// 如果首个元素的上边缘,距离父布局值为0,就说明ListView滚动到了最顶部,此时应该允许下拉刷新
ableToPull = true;
} else {
if (headerLayoutParams.topMargin != hideHeaderHeight) {
headerLayoutParams.topMargin = hideHeaderHeight;
header.setLayoutParams(headerLayoutParams);
}
ableToPull = false;
}
} else {
// 如果ListView中没有元素,也应该允许下拉刷新
ableToPull = true;
}
}
/**
* 更新下拉头中的信息。
*/
private void updateHeaderView() {
if (lastStatus != currentStatus) {
if (currentStatus == STATUS_PULL_TO_REFRESH) {
description.setText(getResources().getString(R.string.pull_to_refresh));
arrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
rotateArrow();
} else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
description.setText(getResources().getString(R.string.release_to_refresh));
arrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
rotateArrow();
} else if (currentStatus == STATUS_REFRESHING) {
description.setText(getResources().getString(R.string.refreshing));
progressBar.setVisibility(View.VISIBLE);
arrow.clearAnimation();
arrow.setVisibility(View.GONE);
}
refreshUpdatedAtValue();
}
}
/**
* 根据当前的状态来旋转箭头。
*/
private void rotateArrow() {
float pivotX = arrow.getWidth() / 2f;
float pivotY = arrow.getHeight() / 2f;
float fromDegrees = 0f;
float toDegrees = 0f;
if (currentStatus == STATUS_PULL_TO_REFRESH) {
fromDegrees = 180f;
toDegrees = 360f;
} else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
fromDegrees = 0f;
toDegrees = 180f;
}
RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
animation.setDuration(100);
animation.setFillAfter(true);
arrow.startAnimation(animation);
}
/**
* 刷新下拉头中上次更新时间的文字描述。
*/
private void refreshUpdatedAtValue() {
lastUpdateTime = preferences.getLong(UPDATED_AT + mId, -1);
long currentTime = System.currentTimeMillis();
long timePassed = currentTime - lastUpdateTime;
long timeIntoFormat;
String updateAtValue;
if (lastUpdateTime == -1) {
updateAtValue = getResources().getString(R.string.not_updated_yet);
} else if (timePassed < 0) {
updateAtValue = getResources().getString(R.string.time_error);
} else if (timePassed < ONE_MINUTE) {
updateAtValue = getResources().getString(R.string.updated_just_now);
} else if (timePassed < ONE_HOUR) {
timeIntoFormat = timePassed / ONE_MINUTE;
String value = timeIntoFormat + "分钟";
updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
} else if (timePassed < ONE_DAY) {
timeIntoFormat = timePassed / ONE_HOUR;
String value = timeIntoFormat + "小时";
updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
} else if (timePassed < ONE_MONTH) {
timeIntoFormat = timePassed / ONE_DAY;
String value = timeIntoFormat + "天";
updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
} else if (timePassed < ONE_YEAR) {
timeIntoFormat = timePassed / ONE_MONTH;
String value = timeIntoFormat + "个月";
updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
} else {
timeIntoFormat = timePassed / ONE_YEAR;
String value = timeIntoFormat + "年";
updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
}
updateAt.setText(updateAtValue);
}
/**
* 正在刷新的任务,在此任务中会去回调注册进来的下拉刷新监听器。
*
* @author guolin
*/
class RefreshingTask extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... params) {
int topMargin = headerLayoutParams.topMargin;
while (true) {
topMargin = topMargin + SCROLL_SPEED;
if (topMargin <= 0) {
topMargin = 0;
break;
}
publishProgress(topMargin);
sleep(10);
}
currentStatus = STATUS_REFRESHING;
publishProgress(0);
if (mListener != null) {
mListener.onRefresh();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... topMargin) {
updateHeaderView();
headerLayoutParams.topMargin = topMargin[0];
header.setLayoutParams(headerLayoutParams);
}
}
/**
* 隐藏下拉头的任务,当未进行下拉刷新或下拉刷新完成后,此任务将会使下拉头重新隐藏。
*
* @author guolin
*/
class HideHeaderTask extends AsyncTask<Void, Integer, Integer> {
@Override
protected Integer doInBackground(Void... params) {
int topMargin = headerLayoutParams.topMargin;
while (true) {
topMargin = topMargin + SCROLL_SPEED;
if (topMargin <= hideHeaderHeight) {
topMargin = hideHeaderHeight;
break;
}
publishProgress(topMargin);
sleep(10);
}
return topMargin;
}
@Override
protected void onProgressUpdate(Integer... topMargin) {
headerLayoutParams.topMargin = topMargin[0];
header.setLayoutParams(headerLayoutParams);
}
@Override
protected void onPostExecute(Integer topMargin) {
headerLayoutParams.topMargin = topMargin;
header.setLayoutParams(headerLayoutParams);
currentStatus = STATUS_REFRESH_FINISHED;
}
}
/**
* 使当前线程睡眠指定的毫秒数。
*
* @param time
* 指定当前线程睡眠多久,以毫秒为单位
*/
private void sleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 下拉刷新的监听器,使用下拉刷新的地方应该注册此监听器来获取刷新回调。
*
* @author guolin
*/
public interface PullToRefreshListener {
/**
* 刷新时会去回调此方法,在方法内编写具体的刷新逻辑。注意此方法是在子线程中调用的, 你可以不必另开线程来进行耗时操作。
*/
void onRefresh();
}
}
这个类是整个下拉刷新功能中最重要的一个类,注释已经写得比较详细了,我再简单解释一下。首先在RefreshableView的构造函数中动态添 加了刚刚定义的pull_to_refresh这个布局作为下拉头,然后在onLayout方法中将下拉头向上偏移出了屏幕,再给ListView注册了 touch事件。之后每当手指在ListView上滑动时,onTouch方法就会执行。在onTouch方法中的第一行就调用了 setIsAbleToPull方法来判断ListView是否滚动到了最顶部,只有滚动到了最顶部才会执行后面的代码,否则就视为正常的 ListView滚动,不做任何处理。当ListView滚动到了最顶部时,如果手指还在向下拖动,就会改变下拉头的偏移值,让下拉头显示出来,下拉的距 离设定为手指移动距离的1/2,这样才会有拉力的感觉。如果下拉的距离足够大,在松手的时候就会执行刷新操作,如果距离不够大,就仅仅重新隐藏下拉头。
具体的刷新操作会在RefreshingTask中进行,其中在doInBackground方法中回调了PullToRefreshListener接口的onRefresh方法,这也是大家在使用RefreshableView时必须要去实现的一个接口,因为具体刷新的逻辑就应该写在onRefresh方法中,后面会演示使用的方法。
另外每次在下拉的时候都还会调用updateHeaderView方法来改变下拉头中的数据,比如箭头方向的旋转,下拉文字描述的改变等。更加深入的理解请大家仔细去阅读RefreshableView中的代码。现在我们已经把下拉刷新的所有功能都完成了,接下来就要看一看如何在项目中引入下拉刷新了。打开或新建activity_main.xml作为程序主界面的布局,加入如下代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.pulltorefreshtest.RefreshableView
android:id="@+id/refreshable_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="@+id/list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ListView>
</com.example.pulltorefreshtest.RefreshableView>
</RelativeLayout>
可以看到,我们在自定义的RefreshableView中加入了一个ListView,这就意味着给这个ListView加入了下拉刷新的功能,就是这么简单!
然后我们再来看一下程序的主Activity,打开或新建MainActivity,加入如下代码:
public class MainActivity extends Activity {
RefreshableView refreshableView;
ListView listView;
ArrayAdapter<String> adapter;
String[] items = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
refreshableView = (RefreshableView) findViewById(R.id.refreshable_view);
listView = (ListView) findViewById(R.id.list_view);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
listView.setAdapter(adapter);
refreshableView.setOnRefreshListener(new PullToRefreshListener() {
@Override
public void onRefresh() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
refreshableView.finishRefreshing();
}
}, 0);
}
}
可以看到,我们通过调用RefreshableView的setOnRefreshListener方法注册了一个监听器,当ListView正在刷新时 就会回调监听器的onRefresh方法,刷新的具体逻辑就在这里处理。而且这个方法已经自动开启了线程,可以直接在onRefresh方法中进行耗时操 作,比如向服务器请求最新数据等,在这里我就简单让线程睡眠3秒钟。另外在onRefresh方法的最后,一定要调用RefreshableView中的 finishRefreshing方法,这个方法是用来通知RefreshableView刷新结束了,不然我们的ListView将一直处于正在刷新的 状态。
不知道大家有没有注意到,setOnRefreshListener这个方法其实是有两个参数的,我们刚刚也是传入了一个不起眼的0。那这第二个参数是用 来做什么的呢?由于RefreshableView比较智能,它会自动帮我们记录上次刷新完成的时间,然后下拉的时候会在下拉头中显示距上次刷新已过了多 久。这是一个非常好用的功能,让我们不用再自己手动去记录和计算时间了,但是却存在一个问题。如果当前我们的项目中有三个地方都使用到了下拉刷新的功能, 现在在一处进行了刷新,其它两处的时间也都会跟着改变!因为刷新完成的时间是记录在配置文件中的,由于在一处刷新更改了配置文件,导致在其它两处读取到的 配置文件时间已经是更改过的了。那解决方案是什么?就是每个用到下拉刷新的地方,给setOnRefreshListener方法的第二个参数中传入不同 的id就行了。这样各处的上次刷新完成时间都是单独记录的,相互之间就不会再有影响。
好了,全部的代码都在这里了,让我们来运行一下,看看效果吧。
效果看起来还是非常不错的。我们最后再来总结一下,在项目中引入ListView下拉刷新功能只需三步:
1. 在Activity的布局文件中加入自定义的RefreshableView,并让ListView包含在其中。
2. 在Activity中调用RefreshableView的setOnRefreshListener方法注册回调接口。
3. 在onRefresh方法的最后,记得调用RefreshableView的finishRefreshing方法,通知刷新结束。
从此以后,在项目的任何地方,一分钟引入下拉刷新功能妥妥的。
好了,今天的讲解到此结束,有疑问的朋友请在下面留言。
源码下载,请点击这里
android开发教程之实现listview下拉刷新和上拉刷新效果
public class PullToLoadListView extends ListView implements OnScrollListener {
private static final String TAG = PullToLoadListView.class.getSimpleName();
private static final int STATE_NON = 0;
private static final int STATE_PULL_TO_REFRESH = 1;
private static final int STATE_RELEASE_TO_REFRESH = 2;
private static final int STATE_REFRESHING = 3;
private int state;
private int firstVisibleItem;
private int lastVisisibleItem;
private float prevY = 0;
private View headerView;
private View footerView;
// header widgets
private ProgressBar headerProgressBar;
private ImageView headerImageArrow;
private TextView headerText;
private RotateAnimation headerArrowAnim;
private RotateAnimation headerArrowReverseAnim;
// footer widgets
private ProgressBar footerProgressBar;
private TextView footerText;
private boolean headerIsHanding = false;
private boolean footerIsHanding = false;
private int headerHeight;
private int footerHeight;
private ResetAnimation resetAnim;
private OnLoadingListener onLoadingListener;
private OnScrollListener onScrollListener;
public PullToLoadListView(Context context) {
super(context);
init(context);
}
public PullToLoadListView(Context context,AttributeSet attrs) {
super(context,attrs);
init(context);
}
private void init(Context context) {
state = STATE_NON;
firstVisibleItem = 0;
lastVisisibleItem = 0;
LayoutInflater inflater = LayoutInflater.from(context);
headerView = inflater.inflate(R.layout.view_pull_header,null);
footerView = inflater.inflate(R.layout.view_pull_footer,null);
headerProgressBar = (ProgressBar) headerView.findViewById(R.id.progressbar);
headerImageArrow = (ImageView) headerView.findViewById(R.id.arrow);
headerText = (TextView) headerView.findViewById(R.id.text);
headerArrowAnim = new RotateAnimation(0,-180,Animation.RELATIVE_TO_SELF,0.5f,0.5f);
headerArrowAnim.setDuration(300);
headerArrowAnim.setFillAfter(true);
headerArrowReverseAnim = new RotateAnimation(-180,0.5f);
headerArrowReverseAnim.setDuration(300);
headerArrowReverseAnim.setFillAfter(true);
footerProgressBar = (ProgressBar) footerView.findViewById(R.id.progressbar);
footerText = (TextView) footerView.findViewById(R.id.text);
measureView(headerView);
measureView(footerView);
headerHeight = headerView.getMeasuredHeight();
footerHeight = footerView.getMeasuredHeight();
headerView.setPadding(0,-1 * headerView.getMeasuredHeight(),0);
footerView.setPadding(0,-1 * footerView.getMeasuredHeight(),0);
headerView.invalidate();
footerView.invalidate();
addHeaderView(headerView,null,false);
addFooterView(footerView,false);
super.setonScrollListener(this);
}
private void measureView(View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if(lp == null) {
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0,lp.width);
int childHeightSpec;
if(lp.height > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);
}
view.measure(childWidthSpec,childHeightSpec);
}
private void resetHeader() {
// headerView.setPadding(0,-1 * headerHeight,0);
resetAnim = new ResetAnimation(headerView,headerHeight,headerView.getPaddingTop());
resetAnim.start();
}
private void resetFooter() {
resetAnim = new ResetAnimation(footerView,footerHeight,footerView.getPaddingTop());
resetAnim.start();
}
private void changeHeaderViewByState(int state) {
if(this.state == state) {
return ;
}
int prevstate = this.state;
this.state = state;
switch(state) {
case STATE_NON:
headerProgressBar.setVisibility(View.INVISIBLE);
headerImageArrow.setVisibility(View.VISIBLE);
headerImageArrow.clearanimation();
headerText.setText("Pull Down To Refresh");
break;
case STATE_PULL_TO_REFRESH:
headerProgressBar.setVisibility(View.INVISIBLE);
headerImageArrow.setVisibility(View.VISIBLE);
headerText.setText("Pull Down To Refresh");
if(prevstate == STATE_RELEASE_TO_REFRESH) {
headerImageArrow.startAnimation(headerArrowReverseAnim);
} else {
headerImageArrow.clearanimation();
}
break;
case STATE_RELEASE_TO_REFRESH:
headerProgressBar.setVisibility(View.INVISIBLE);
headerImageArrow.setVisibility(View.VISIBLE);
headerImageArrow.startAnimation(headerArrowAnim);
headerText.setText("Release To Refresh");
break;
case STATE_REFRESHING:
headerProgressBar.setVisibility(View.VISIBLE);
headerImageArrow.setVisibility(View.INVISIBLE);
headerImageArrow.clearanimation();
headerText.setText("Refreshing");
break;
default:
break;
}
}
private void changeFooterViewByState(int state) {
if(this.state == state) {
return ;
}
this.state = state;
switch(state) {
case STATE_NON:
footerProgressBar.setVisibility(View.INVISIBLE);
footerText.setText("Pull Up To Refresh");
break;
case STATE_PULL_TO_REFRESH:
footerProgressBar.setVisibility(View.INVISIBLE);
footerText.setText("Pull Up To Refresh");
break;
case STATE_RELEASE_TO_REFRESH:
footerProgressBar.setVisibility(View.INVISIBLE);
footerText.setText("Release To Refresh");
break;
case STATE_REFRESHING:
footerProgressBar.setVisibility(View.VISIBLE);
footerText.setText("Refreshing");
break;
default:
break;
}
}
@Override
public void setonScrollListener(OnScrollListener l) {
this.onScrollListener = l;
}
public void setonLoadingListener(OnLoadingListener onLoadingListener) {
this.onLoadingListener = onLoadingListener;
}
public void loadCompleted() {
if(headerIsHanding) {
changeHeaderViewByState(STATE_NON);
resetHeader();
headerIsHanding = false;
}
if(footerIsHanding) {
changeFooterViewByState(STATE_NON);
resetFooter();
footerIsHanding = false;
}
}
private void handleMoveHeaderEvent(MotionEvent ev) {
headerIsHanding = true;
float tempY = ev.getRawY();
float vector = tempY - prevY;
vector /= 2;
prevY = tempY;
if(vector > 0) {
int newPadding = (int) (headerView.getPaddingTop() + vector);
newPadding = Math.min(newPadding,headerHeight / 2);
headerView.setPadding(0,newPadding,0);
if(state != STATE_REFRESHING) {
if(newPadding > 0) {
changeHeaderViewByState(STATE_RELEASE_TO_REFRESH);
} else {
changeHeaderViewByState(STATE_PULL_TO_REFRESH);
}
}
} else {
if(state == STATE_RELEASE_TO_REFRESH || state == STATE_PULL_TO_REFRESH) {
int newPadding = (int) (headerView.getPaddingTop() + vector);
newPadding = Math.max(newPadding,-1 * headerHeight);
headerView.setPadding(0,0);
if(newPadding <= -1 * headerHeight) {
changeHeaderViewByState(STATE_NON);
headerIsHanding = false;
} else if(newPadding <= 0) {
changeHeaderViewByState(STATE_PULL_TO_REFRESH);
} else {
}
}
}
}
private void handleMoveFooterEvent(MotionEvent ev) {
footerIsHanding = true;
float tempY = ev.getRawY();
float vector = tempY - prevY;
vector /= 2;
prevY = tempY;
if(vector < 0) {
int newPadding = (int) (footerView.getPaddingTop() - vector);
if(newPadding > 0) {
newPadding = 0;
}
footerView.setPadding(0,0);
if(state != STATE_REFRESHING) {
if(newPadding < 0) {
changeFooterViewByState(STATE_PULL_TO_REFRESH);
} else {
changeFooterViewByState(STATE_RELEASE_TO_REFRESH);
}
}
} else {
int newPadding = (int) (footerView.getPaddingTop() - vector);
newPadding = Math.min(newPadding,footerHeight);
footerView.setPadding(0,0);
if(newPadding <= -1 * footerHeight) {
changeFooterViewByState(STATE_NON);
footerIsHanding = false;
} else if(newPadding < 0) {
changeFooterViewByState(STATE_PULL_TO_REFRESH);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch(ev.getAction()) {
case MotionEvent.ACTION_DOWN:
prevY = ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if(state == STATE_RELEASE_TO_REFRESH) {
if(headerIsHanding) {
changeHeaderViewByState(STATE_REFRESHING);
if(onLoadingListener != null) {
onLoadingListener.onLoadNew();
}
}
if(footerIsHanding) {
changeFooterViewByState(STATE_REFRESHING);
if(onLoadingListener != null) {
onLoadingListener.onLoadMore();
}
}
} else if(state == STATE_PULL_TO_REFRESH) {
if(headerIsHanding) {
changeHeaderViewByState(STATE_NON);
resetHeader();
headerIsHanding = false;
}
if(footerIsHanding) {
changeFooterViewByState(STATE_NON);
resetFooter();
footerIsHanding = false;
}
} else if(state == STATE_NON) {
headerIsHanding = false;
footerIsHanding = false;
} else {
// state == STATE_REFRESHING
// ignore
}
break;
case MotionEvent.ACTION_MOVE:
if(resetAnim == null || !resetAnim.run) {
if(state != STATE_REFRESHING) {
Adapter adapter = getAdapter();
if(adapter == null) {
handleMoveHeaderEvent(ev);
} else {
final int count = adapter.getCount();
if(count <= 0) {
handleMoveHeaderEvent(ev);
} else {
float tempY = ev.getRawY();
float vector = tempY - prevY;
if(firstVisibleItem == 0 && lastVisisibleItem == count - 1) {
if(headerIsHanding) {
handleMoveHeaderEvent(ev);
} else if(footerIsHanding) {
handleMoveFooterEvent(ev);
} else {
if(vector > 0) {
handleMoveHeaderEvent(ev);
} else if(vector < 0) {
handleMoveFooterEvent(ev);
} else {
// ignore vector == 0
}
}
} else if(firstVisibleItem == 0 && vector > 0) {
handleMoveHeaderEvent(ev);
} else if(lastVisisibleItem == count - 1 && vector < 0) {
handleMoveFooterEvent(ev);
} else {
// ignore
}
}
}
}
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
@Override
public void onScrollStateChanged(AbsListView view,int scrollState) {
if(onScrollListener != null) {
onScrollListener.onScrollStateChanged(view,scrollState);
}
}
@Override
public void onScroll(AbsListView view,int firstVisibleItem,
int visibleItemCount,int totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
this.lastVisisibleItem = firstVisibleItem + visibleItemCount - 1;
if(onScrollListener != null) {
onScrollListener.onScroll(view,firstVisibleItem,visibleItemCount,totalItemCount);
}
}
static class ResetAnimation extends Thread {
static final int DURATION = 600;
static final int INTERVAL = 5;
View view;
int orignalHeight;
int paddingTop;
boolean run = false;
ResetAnimation(View view,int orignalHeight,int paddingTop) {
this.view = view;
this.orignalHeight = orignalHeight;
this.paddingTop = paddingTop;
}
public void run() {
run = true;
int total = orignalHeight * 2 + paddingTop;
int timetotal = DURATION / INTERVAL;
int piece = total / timetotal;
int time = 0;
final View view = this.view;
final int paddingTop = this.paddingTop;
do {
final int nextPaddingTop = paddingTop - time * piece;
view.post(new Runnable() {
public void run() {
view.setPadding(0,nextPaddingTop,0);
view.postInvalidate();
}
});
try {
sleep(INTERVAL);
} catch (InterruptedException e) {
e.printstacktrace();
}
time ++;
} while(time < timetotal);
run = false;
}
}
public interface OnLoadingListener {
public void onLoadNew();
public void onLoadMore();
}
}
关于下拉刷新 & SBJson 的使用和实现下拉刷新的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于andorid Activity 实现上拉刷新的效果,但是不用上下拉刷新控件、android ActionBar-PullToRefresh 下拉刷新的使用方法、Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能、android开发教程之实现listview下拉刷新和上拉刷新效果的相关信息,请在本站寻找。
本文标签: