- 功能介绍
下拉刷新,几乎是每个 Android 应用都会需要的功能。 android-Ultra-Pull-To-Refresh (以下简称 UltraPTR )便是一个强大的 Andriod 下拉刷新框架。
主要特点:
(1).继承于 ViewGroup, Content 可以包含任何 View。
(2).简洁完善的 Header 抽象,方便进行拓展,构建符合需求的头部。
GitHub
2. 总体设计
UltraPTR 总体设计比较简单清晰。
首先抽象出了两个接口,功能接口和 UI 接口。
PtrHandler 代表下拉刷新的功能接口,包含刷新功能回调方法以及判断是否可以下拉的方法。用户实现此接口来进行数据刷新工作。
PtrUIHandler 代表下拉刷新的 UI 接口,包含准备下拉,下拉中,下拉完成,重置以及下拉过程中的位置变化等回调方法。通常情况下, Header 需要实现此接口,来处理下拉刷新过程中头部 UI 的变化。
整个项目围绕核心类 PtrFrameLayout。 PtrFrameLayout 代表了一个下拉刷新的自定义控件。
PtrFrameLayout 继承自 ViewGroup,有且只能有两个子 View,头部 Header 和内容 Content。通常情况下 Header 会实现 PtrUIHandler 接口, Content 可以为任意的 View。
和所有的自定义控件一样, PtrFrameLayout 通过重写 onFinishInflate, onMeasure, onLayout 来确定控件大小和位置。通过重写 dispatchTouchEvent 来确定控件的下拉行为。
依赖
compile 'in.srain.cube:ultra-ptr:1.0.11'
定制
您可以添加PtrUIHandler到PtrFrameLayout以实现任何您想要的UI效果。
public interface PtrUIHandler {
/ * *
*当内容视图已达到顶部并且刷新已完成时,将重置视图。
*
* @param frame
* / public void onUIReset( PtrFrameLayout frame);
/ * *
*准备加载
*
* @param frame
* / public void onUIRefreshPrepare( PtrFrameLayout frame);
/ * *
*执行刷新UI
* / public void onUIRefreshBegin( PtrFrameLayout frame);
/ * *
*刷新后执行UI
* / public void onUIRefreshComplete( PtrFrameLayout frame);
public void onUIPositionChange(PtrFrameLayout frame,boolean isUnderTouch,byte status,int oldPosition,int currentPosition,float oldPercent,float currentPercent);
}}
栗子讲解
PtrClassicDefaultHeader 实现了 PtrUIHandler 接口。
经典样式的 Header 实现,可以作为我们实现自定义 Header 的参考,以下是具体实现。
@Override
public void onUIReset(PtrFrameLayout frame) {
resetView();
mShouldShowLastUpdate = true;
tryUpdateLastUpdateTime();
}
private void resetView() {
hideRotateView();
mProgressBar.setVisibility(INVISIBLE);
}
private void hideRotateView() {
mRotateView.clearAnimation();
mRotateView.setVisibility(INVISIBLE);
}
重置 View,隐藏忙碌进度条,隐藏箭头 View,更新最后刷新时间。
@Override
public void onUIRefreshPrepare(PtrFrameLayout frame) {
mShouldShowLastUpdate = true;
tryUpdateLastUpdateTime();
mLastUpdateTimeUpdater.start();
mProgressBar.setVisibility(INVISIBLE);
mRotateView.setVisibility(VISIBLE);
mTitleTextView.setVisibility(VISIBLE);
if (frame.isPullToRefresh()) {
mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh));
} else {
mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down));
}
}
准备刷新,隐藏忙碌进度条,显示箭头 View,显示文字,如果是下拉刷新,显示“下拉刷新”,如果是释放刷新,显示“下拉”。
@Override
public void onUIRefreshBegin(PtrFrameLayout frame) {
mShouldShowLastUpdate = false;
hideRotateView();
mProgressBar.setVisibility(VISIBLE);
mTitleTextView.setVisibility(VISIBLE);
mTitleTextView.setText(R.string.cube_ptr_refreshing);
tryUpdateLastUpdateTime();
mLastUpdateTimeUpdater.stop();
}
开始刷新,隐藏箭头 View,显示忙碌进度条,显示文字,显示“加载中…”,更新最后刷新时间。
@Override
public void onUIRefreshComplete(PtrFrameLayout frame) {
hideRotateView();
mProgressBar.setVisibility(INVISIBLE);
mTitleTextView.setVisibility(VISIBLE);
mTitleTextView.setText(getResources().getString(R.string.cube_ptr_refresh_complete));
// update last update time
SharedPreferences sharedPreferences = getContext().getSharedPreferences(KEY_SharedPreferences, 0);
if (!TextUtils.isEmpty(mLastUpdateTimeKey)) {
mLastUpdateTime = new Date().getTime();
sharedPreferences.edit().putLong(mLastUpdateTimeKey, mLastUpdateTime).commit();
}
}
刷新结束,隐藏箭头 View,隐藏忙碌进度条,显示文字,显示“更新完成”,写入最后刷新时间。
@Override
public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int lastPos, int currentPos, float oldPercent, float currentPercent) {
final int mOffsetToRefresh = frame.getOffsetToRefresh();
if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
crossRotateLineFromBottomUnderTouch(frame);
if (mRotateView != null) {
mRotateView.clearAnimation();
mRotateView.startAnimation(mReverseFlipAnimation);
}
}
} else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
crossRotateLineFromTopUnderTouch(frame);
if (mRotateView != null) {
mRotateView.clearAnimation();
mRotateView.startAnimation(mFlipAnimation);
}
}
}
}
下拉过程中位置变化回调。
在拖动情况下,当下拉距离从 小于刷新高度到大于刷新高度 时,箭头 View 从向下,变成向上,同时改变文字显示。
当下拉距离从 大于刷新高度到小于刷新高度 时,箭头 View 从向上,变为向下,同时改变文字显示。
回调
ptrFrame.setPtrHandler(new PtrHandler() {
@Override
public void onRefreshBegin(PtrFrameLayout frame) {
frame.postDelayed(new Runnable() {
@Override
public void run() {
ptrFrame.refreshComplete();
}
}, 1800);
}
@Override
public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);
}
});
案例2
首先我们要自己定义需要的头部布局
<?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="60dp"
android:orientation="horizontal"
android:background="@color/grby">
<LinearLayout
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="0.5">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_windmill"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@mipmap/header"/>
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_head_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="30dp"
android:gravity="bottom"
android:text="下拉刷新"
android:textColor="#ffffff"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="25dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_head_time"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="本次刷新时间 : 2015-10-23 12:00"
android:textColor="#ffffff"
android:textSize="10dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
public class WindmillHeader extends FrameLayout implements PtrUIHandler {
private LayoutInflater inflater;
// 下拉刷新视图(头部视图)
private ViewGroup headView;
// 下拉刷新文字
private TextView tvHeadTitle;
// 下拉图标
private ImageView ivWindmill;
// 下拉刷新时间
private TextView tvHeadTime;
/** 保存上一次的刷新时间. */
private String lastRefreshTime = null;
//动画
private WindmillDrawable drawable;
public WindmillHeader(Context context) {
this(context, null);
}
public WindmillHeader(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WindmillHeader(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化
*
* @param context
*/
private void init(Context context) {
inflater = LayoutInflater.from(context);
/**
* 头部
*/
headView = (ViewGroup) inflater.inflate(R.layout.windmill_header, this, true);
ivWindmill = (ImageView) headView.findViewById(R.id.iv_windmill);
tvHeadTitle = (TextView) headView.findViewById(R.id.tv_head_title);
tvHeadTime= (TextView) findViewById(R.id.tv_head_time);
drawable = new WindmillDrawable(context, ivWindmill);
ivWindmill.setImageDrawable(drawable);
}
@Override
public void onUIReset(PtrFrameLayout ptrFrameLayout) {
tvHeadTitle.setText("下拉刷新");
drawable.stop();
}
@Override
public void onUIRefreshPrepare(PtrFrameLayout ptrFrameLayout) {
tvHeadTitle.setText("下拉刷新");
if(lastRefreshTime==null){
lastRefreshTime = TimeUtils.getCurrentTimeInString();
tvHeadTime.setText("当前刷新时间:" + lastRefreshTime);
}else{
tvHeadTime.setText("上次刷新时间:" + lastRefreshTime);
}
}
@Override
public void onUIRefreshBegin(PtrFrameLayout ptrFrameLayout) {
tvHeadTitle.setText("正在刷新");
tvHeadTime.setText("本次刷新时间:" + lastRefreshTime);
drawable.start();
}
@Override
public void onUIRefreshComplete(PtrFrameLayout ptrFrameLayout) {
ivWindmill.clearAnimation();
tvHeadTitle.setText("刷新完成");
tvHeadTime.setText("本次刷新时间:" + lastRefreshTime);
}
@Override
public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int lastPos, int currentPos, float oldPercent, float currentPercent) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
drawable.postRotation(currentPos - lastPos);
invalidate();
}
final int mOffsetToRefresh = frame.getOffsetToRefresh();
if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
tvHeadTitle.setText("下拉刷新");
if(lastRefreshTime==null){
lastRefreshTime = TimeUtils.getCurrentTimeInString();
tvHeadTime.setText("当前刷新时间:" + lastRefreshTime);
}else{
tvHeadTime.setText("上次刷新时间:" + lastRefreshTime);
}
}
} else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
tvHeadTitle.setText("松开刷新");
tvHeadTime.setText("上次刷新时间:" + lastRefreshTime);
lastRefreshTime = TimeUtils.getCurrentTimeInString();
}
}
}
}
private WindmillHeader header;
private void initView() {
ptr= (PtrFrameLayout) findViewById(R.id.ptr_main);
//创建自定义头部UI
header = new WindmillHeader(this);
/* 设置刷新头部view */
ptr.setHeaderView(header);
//设置回调
ptr.addPtrUIHandler(header);
ptr.disableWhenHorizontalMove(true);
ptr.setPtrHandler(new PtrDefaultHandler() {
@Override
public void onRefreshBegin(final PtrFrameLayout frame) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// ptr.refreshComplete();
getdata();
}
}, 3000);
}
@Override
public boolean checkCanDoRefresh(PtrFrameLayout frame,
View content, View header) {
return PtrDefaultHandler.checkContentCanBePulledDown(frame,
content, header);
}
});
/* 延时100秒,自动刷新 */
ptr.postDelayed(new Runnable() {
@Override
public void run() {
ptr.autoRefresh(false);
}
}, 100);
}
案例3
public class WindmillHeader extends FrameLayout implements PtrUIHandler {
private LayoutInflater inflater;
// 下拉刷新视图(头部视图)
private ViewGroup headView;
// 下拉刷新文字
private TextView tvHeadTitle;
// 下拉图标
private ImageView ivWindmill;
private WindmillDrawable drawable;
public WindmillHeader(Context context) {
this(context, null);
}
public WindmillHeader(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WindmillHeader(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化
*
* @param context
*/
private void init(Context context) {
inflater = LayoutInflater.from(context);
/**
* 头部
*/
headView = (ViewGroup) inflater.inflate(R.layout.windmill_header, this, true);
ivWindmill = (ImageView) headView.findViewById(R.id.iv_windmill);
tvHeadTitle = (TextView) headView.findViewById(R.id.tv_head_title);
drawable = new WindmillDrawable(context, ivWindmill);
ivWindmill.setImageDrawable(drawable);
}
@Override
public void onUIReset(PtrFrameLayout ptrFrameLayout) {
tvHeadTitle.setText("下拉刷新");
drawable.stop();
}
@Override
public void onUIRefreshPrepare(PtrFrameLayout ptrFrameLayout) {
tvHeadTitle.setText("下拉刷新");
}
@Override
public void onUIRefreshBegin(PtrFrameLayout ptrFrameLayout) {
tvHeadTitle.setText("正在刷新");
drawable.start();
}
@Override
public void onUIRefreshComplete(PtrFrameLayout ptrFrameLayout) {
ivWindmill.clearAnimation();
tvHeadTitle.setText("刷新完成");
}
@Override
public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, int lastPos, int currentPos, float oldPercent, float currentPercent) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
drawable.postRotation(currentPos - lastPos);
invalidate();
}
final int mOffsetToRefresh = frame.getOffsetToRefresh();
if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
tvHeadTitle.setText("下拉刷新");
}
} else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {
if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
tvHeadTitle.setText("松开刷新");
}
}
}
}
public class WindmillDrawable extends Drawable implements Animatable {
private static final String TAG = WindmillDrawable.class.getSimpleName();
private Resources resources;
private Bitmap windmill;
private Matrix matrix;
private View parent;
private Animation animation;
private boolean isFirstDraw = true;
private boolean isAnimating;
public WindmillDrawable(Context context, View parent) {
resources = context.getResources();
windmill = BitmapFactory.decodeResource(resources, R.drawable.windmill);
matrix = new Matrix();
this.parent = parent;
animation = new RotateAnimation(360, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(800);
animation.setRepeatMode(Animation.RESTART);
animation.setRepeatCount(Animation.INFINITE);
animation.setFillAfter(true);
}
@Override
public void draw(Canvas canvas) {
if (isFirstDraw) {
isFirstDraw = false;
matrix.setTranslate((getBounds().width() - windmill.getWidth()) / 2, (getBounds().height() - windmill.getHeight()) / 2);
}
Paint p = new Paint();
canvas.drawBitmap(windmill, matrix, p);
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
@Override
public int getOpacity() {
return 0;
}
public void postRotation(int degree) {
matrix.postRotate(degree, getBounds().exactCenterX(), getBounds().exactCenterY());
invalidateSelf();
}
@Override
public void start() {
parent.startAnimation(animation);
isAnimating = true;
}
@Override
public void stop() {
parent.clearAnimation();
isAnimating = false;
}
@Override
public boolean isRunning() {
return isAnimating;
}
*/
public class ContentGridViewFragment extends Fragment {
private GridView gvMain;
private BaseAdapter adapter;
private PtrClassicFrameLayout ptr;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_content_grid_view, container, false);
initView(v);
return v;
}
private void initView(View v) {
gvMain = (GridView) v.findViewById(R.id.gv_main);
ptr = (PtrClassicFrameLayout) v.findViewById(R.id.ptr_main);
ptr.setPtrHandler(new PtrDefaultHandler() {
@Override
public void onRefreshBegin(PtrFrameLayout ptrFrameLayout) {
getData();
ptr.refreshComplete();
}
});
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
ptr.autoRefresh();
}
}, 100);
}
private void getData() {
adapter = new GradViewAdapter(getActivity(), Constants.SMALL_IMAGE_URLS);
gvMain.setAdapter(adapter);
}
}
Contact GitHub API Training Shop Blog About