APP实用开发——定制自己的下拉刷新头
来源:互联网 发布:不同编程语言 做什么 编辑:程序博客网 时间:2024/06/08 05:56
- 功能介绍
下拉刷新,几乎是每个 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 的参考,以下是具体实现。
@Overridepublic 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,更新最后刷新时间。
@Overridepublic 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,显示文字,如果是下拉刷新,显示“下拉刷新”,如果是释放刷新,显示“下拉”。
@Overridepublic void onUIRefreshBegin(PtrFrameLayout frame) { mShouldShowLastUpdate = false; hideRotateView(); mProgressBar.setVisibility(VISIBLE); mTitleTextView.setVisibility(VISIBLE); mTitleTextView.setText(R.string.cube_ptr_refreshing); tryUpdateLastUpdateTime(); mLastUpdateTimeUpdater.stop();}
开始刷新,隐藏箭头 View,显示忙碌进度条,显示文字,显示“加载中…”,更新最后刷新时间。
@Overridepublic 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,隐藏忙碌进度条,显示文字,显示“更新完成”,写入最后刷新时间。
@Overridepublic 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
Demo
- APP实用开发——定制自己的下拉刷新头
- 使用 Ultra Pull To Refresh 定制自己的下拉刷新头部
- APP开发之Viewpage+Fragment+下拉刷新
- android开发游记:SpringView 下拉刷新的高效解决方案,定制你自己风格的拖拽页面
- 微信小程序开发——下拉刷新的实现
- 利用MJRefresh定制自己的刷新动画
- EGORefreshTableHeaderView下拉刷新(二)——启动APP触发下拉刷新
- IOS开发——TabelView下拉刷新
- app定制开发的好处
- iOS开发——封装自己的下拉菜单
- Android开发 - 下拉刷新和分段头悬停列表
- Android开发 - 下拉刷新和分段头悬停列表
- SwipeRefreshLayout谷歌自己的下拉 刷新
- Google自己的下拉刷新组件SwipeRefreshLayout
- 自己实现一个下拉刷新的ListView
- Google自己的下拉刷新组件SwipeRefreshLayout
- Google自己的下拉刷新组件SwipeRefreshLayout
- Google自己的下拉刷新组件SwipeRefreshLayout
- 一些笔记。。
- AngularJS开发指南06:表达式
- 连续与可导
- Semaphore,CountDownLatch,CyclicBarrier
- 视觉SLAM漫谈
- APP实用开发——定制自己的下拉刷新头
- 嵌入式面试常问问题
- AngularJS开发指南07:表单
- 内存、外存、主存、辅存和寄存
- 架构的目的是驾驭
- Phpcms V9网站从本地上传到服务器需要修改的地方小结
- 自用screen配置文件.screenrc
- 水池数目
- 数组元素的三种表示方式