如何写一个Android通用刷新控件
发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要介绍如何写一个Android通用刷新控件,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!思路:写一个继承RelativeLayout的RefreshLayout添加头
千家信息网最后更新 2025年01月19日如何写一个Android通用刷新控件
这篇文章主要介绍如何写一个Android通用刷新控件,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
思路:
写一个继承RelativeLayout的RefreshLayout
添加头尾控件作为刷新控件
通过事件分发来进行刷新操作
通过动画来控制控件移动
目的:让他的所有子控件都可以使用,哪怕是一个TextView
public class RefreshLayout extends RelativeLayout { /** * 滑动控件时拉去的速度比例 */ private final int V_REFRESH = 2; /** * 是否是刷新过程 * true 是 * false 不是 * 为false的时候才可以进行刷新 */ private boolean mIsRefreshDuring; /** * 可以进下拉刷新 */ private boolean mCanDownPull; /** * 可以进行上拉刷新 */ private boolean mCanUpPull; /** * 判断触摸后是否是初次移动 */ private boolean mIsFirstMove; /** * y轴呢平移的距离 */ private int mDistanceY; /** * 刷新接口对象 */ private OnRefresh mOnRefresh; /** * 用于控制事件拦截的变量 */ private boolean mCanIntercept; private int mTouchSlop; private int mDistance; private LayoutParams mHeaderParams; private View mHeaderView; private View mFootView; private int mHeaderMaxHeight; private int mStartY; private LayoutParams mFootParams; private int mFootMaxHeight; private PullCallBack mCallBack; private View mChildView; private ObjectAnimator mAnimator; public RefreshLayout(Context context) { super(context); initData(); } public RefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); initData(); } public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initData(); } /** * 必须让头尾控件实现的接口 */ public interface HeadAndFootCallBack { //设置属性 void setAttribute(); //开始刷新 void startPull(); //停止刷新 void stopPull(); } /** * 必须让被拖动的控件子类实现 */ public interface PullCallBack { boolean canDownPull(); boolean canUpPull(); } private void initData() { //不调用该方法不能进行绘制 setWillNotDraw(false); } /** * 下拉刷新完成后必须使用该方法 */ public void downPullFinish() { mAnimator.setFloatValues(mChildView.getTranslationY(), 0); mAnimator.start(); ((HeadAndFootCallBack) mHeaderView).stopPull(); } /** * 上拉完成后必须调用该方法 */ public void upPullFinish() { mAnimator.setFloatValues(mChildView.getTranslationY(), 0); mAnimator.start(); ((HeadAndFootCallBack) mFootView).stopPull(); } /** * 自动下拉刷新 */ public void autoDownPullForHead() { postDelayed(new Runnable() { @Override public void run() { mCanDownPull = true; mCanUpPull = false; mAnimator.setFloatValues(10, mHeaderMaxHeight); mAnimator.start(); ((HeadAndFootCallBack) mHeaderView).startPull(); mOnRefresh.onDownPullRefresh(); } }, 500); } /** * 自动下拉刷新 */ public void autoUpPullForHead() { postDelayed(new Runnable() { @Override public void run() { mCanDownPull = false; mCanUpPull = true; mAnimator.setFloatValues(0, mFootMaxHeight); mAnimator.start(); ((HeadAndFootCallBack) mFootView).startPull(); mOnRefresh.onUpPullRefresh(); } }, 500); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mCanIntercept; } @Override public boolean onTouchEvent(MotionEvent event) { return true; } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.e("shen", "mIsRefreshDuring=" + mIsRefreshDuring); if (mIsRefreshDuring)/*如果正在进行刷新将不会获取MotionEvent*/ { return super.dispatchTouchEvent(event); } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mStartY = (int) event.getY(); initPull(); break; case MotionEvent.ACTION_MOVE: if (event.getPointerCount() == 1) { int moveY = (int) event.getY(); mDistanceY = (moveY - mStartY) / V_REFRESH; if (!mIsFirstMove && mDistanceY != 0 && mDistanceY < mTouchSlop) { mCanDownPull = mDistanceY > 0; mCanUpPull = !mCanDownPull; mIsFirstMove = true; } if (mCanDownPull && mCallBack.canDownPull()) { upDataForDownPull();//下拉刷新 mChildView.setEnabled(false); mCanIntercept = true; } if (mCanUpPull && mCallBack.canUpPull()) { upDataForUpPull();//上拉加载 mChildView.setEnabled(false); mCanIntercept = true; } mStartY = moveY; } break; case MotionEvent.ACTION_UP: mIsRefreshDuring = true; mIsFirstMove = false; if (mHeaderParams.height >= mHeaderMaxHeight)/*可以下拉刷新*/ { ((HeadAndFootCallBack) mHeaderView).startPull(); mOnRefresh.onDownPullRefresh(); } else if (mFootParams.height >= mFootMaxHeight)/*可以上拉刷新*/ { ((HeadAndFootCallBack) mFootView).startPull(); mOnRefresh.onUpPullRefresh(); } else if (mHeaderParams.height > 0 && mHeaderParams.height < mHeaderMaxHeight)/*不能进行下拉刷新,收回*/ { releaseForDownFinished(); } else if (mFootParams.height > 0 && mFootParams.height < mFootMaxHeight)/*不能进行下拉刷新,收回*/ { releaseForUpFinished(); } else { mIsRefreshDuring = false; mCanIntercept = false; } break; } super.dispatchTouchEvent(event); return true; } /** * 每次进行触摸都需要进行初始化 */ private void initPull() { mCanDownPull = false; mCanUpPull = false; } /** * 不需要进行上拉刷新 */ private void releaseForUpFinished() { mAnimator.setFloatValues(mChildView.getTranslationY(), 0); mAnimator.start(); } /** * 不需要进行下拉刷新 */ private void releaseForDownFinished() { mAnimator.setFloatValues(mChildView.getTranslationY(), 0); mAnimator.start(); } /** * 上拉时处理手势 */ private void upDataForUpPull() { if (mDistanceY != 0) { mFootParams.height -= mDistanceY; if (mFootParams.height <= 0) { mFootParams.height = 0; } if (mFootParams.height >= mFootMaxHeight) { mFootParams.height = mFootMaxHeight; } mChildView.setTranslationY(-mFootParams.height); mFootView.requestLayout(); } } /** * 下拉时处理手势 */ private void upDataForDownPull() { if (mDistanceY != 0) { mHeaderParams.height += mDistanceY; if (mHeaderParams.height >= mHeaderMaxHeight) { //*** mHeaderParams.height = mHeaderMaxHeight; } if (mHeaderParams.height <= 0) { //最小 mHeaderParams.height = 0; } mChildView.setTranslationY(mHeaderParams.height); mHeaderView.requestLayout(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override protected void onFinishInflate() { super.onFinishInflate(); //加载头 mHeaderView = getChildAt(0); if (!(mHeaderView instanceof HeadAndFootCallBack)) { new IllegalStateException("HeaderView必须实现HeadAndFootCallBack接口"); } ((HeadAndFootCallBack) mHeaderView).setAttribute(); mHeaderParams = (LayoutParams) mHeaderView.getLayoutParams(); mHeaderParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); //加载尾 mFootView = getChildAt(2); if (!(mFootView instanceof HeadAndFootCallBack)) { new IllegalStateException("FootView必须实现HeadAndFootCallBack接口"); } ((HeadAndFootCallBack) mFootView).setAttribute(); mFootParams = (LayoutParams) mFootView.getLayoutParams(); mFootParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); mChildView = getChildAt(1); if (!(mChildView instanceof HeadAndFootCallBack)) { new IllegalStateException("ChildView必须实现PullCallBack接口"); } mCallBack = (PullCallBack) getChildAt(1); //设置动画 mAnimator = ObjectAnimator.ofFloat(mChildView, "translationY", 0); mAnimator.setInterpolator(new DecelerateInterpolator()); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int translationY = (int) mChildView.getTranslationY(); if (mCanUpPull) { //从移动到的位置往下滑 mFootParams.height = Math.abs(translationY); mFootView.requestLayout(); } else if (mCanDownPull) { mHeaderParams.height = Math.abs(translationY); mHeaderView.requestLayout(); } Log.e("shen", "translationY=" + translationY); Log.e("shen", "mHeaderParams.height=" + mHeaderParams.height); if (translationY == 0) { mChildView.setEnabled(true); mDistanceY = 0; //重置 mIsRefreshDuring = false; //重置 mCanIntercept = false; } else { mIsRefreshDuring = true; } } }); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); mDistance = mTouchSlop * 5; //设置下拉头初始属性 mHeaderMaxHeight = mHeaderParams.height; mHeaderParams.height = 0; mHeaderView.requestLayout(); //设置上拉尾初始属性 mFootMaxHeight = mFootParams.height; mFootParams.height = 0; mFootView.requestLayout(); } /** * 下拉/上拉事件监听 */ public interface OnRefresh { /** * 下拉刷新 */ void onDownPullRefresh(); /** * 上拉加载 */ void onUpPullRefresh(); } public void setOnRefresh(OnRefresh onRefresh) { mOnRefresh = onRefresh; } }
给他添加三个控件,头尾就是刷新头、尾,第二个就是正常显示的控件。必须让头尾实现HeadAndFootCallBack接口,来设置属性,通知开始刷新、结束刷新
难点: 现在来说下开发时遇到的难点
由于判断在dispatchTouchEvent中,导致如果该控件以及子控件都不消费该事件的话,就会造成事件不会发送到它,因为如果不消费DOWN事件的话,之后所有的事件都不会在进行接收。解决方式,让该控件onTouchEvent方法消返回true,当子控件不进行事件消费的话,就会返回由该控件消费,不会造成因DOWN事件不消费而无法接收到事件,导致dispatchTouchEvent也不消费事件
动画,动画就是我的伤痛,最近在学习估值器
以上是"如何写一个Android通用刷新控件"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!
控件
下拉
事件
接口
消费
动画
头尾
属性
方法
就是
移动
内容
手势
篇文章
难点
拉加
处理
控制
最小
三个
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
如何访问内网服务器电脑
电脑装服务器有啥用
学网络技术短时间
方舟服务器管理器更新失败
数据库密码锁了怎么办
重庆垫江生鲜信息软件开发
网络技术和应用题库
车牌识别系统车牌数据库更新
服务器连接密码不成功
druid数据库自定义密码
阿里云的服务器是哪里买的
湖北网络技术
重庆数据库招聘
国家的网络安全宣传
网络安全信息安全的手抄报怎么做
北京创新财富网络技术
易学会网络技术
天津市软件开发
大华存储服务器默认地址
小学生网络安全存在问题
海康视频管理服务器器功率
DBA数据库管理员的职业
杭州币猫网络技术官网
网络安全工坊
巨开心网络技术有限公司
西山居网络技术
怎么像服务器发送数据
广西大学网络安全学院官网
九月互联网科技
软件开发售后服务文字