Android自定义实现顶部粘性下拉刷新效果

本文实例为大家分享了Android实现顶部粘性下拉刷新效果的具体代码,供大家参考,具体内容如下

学习:视频地址

activity_view_mv代码

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/rl_view" android:layout_width="match_parent" android:layout_height="wrap_content" > <trunk.doi.base.ui.activity.test.TouchPullView android:id="@+id/cs_view" android:layout_width="match_parent" android:layout_height="wrap_content" app:pColor="@color/cff3e19" app:pContentDrawable="@drawable/shape_circle" app:pContentDrawableMargin="2dp" app:pDragHeight="100dp" app:pTangentAngle="110" app:pRadius="15dp" app:pTargetGravityHeight="4dp" app:pTargetWidth="200dp" /> <trunk.doi.base.ui.activity.test.TestViewBezer android:layout_width="match_parent" android:visibility="gone" android:layout_height="wrap_content" /> </RelativeLayout>

ViewMvActivity代码

import android.os.Bundle; import android.support.annotation.Nullable; import android.view.MotionEvent; import android.view.View; import android.widget.RelativeLayout; import butterknife.BindView; import trunk.doi.base.R; import trunk.doi.base.base.BaseActivity; public class ViewMvActivity extends BaseActivity { @BindView(R.id.cs_view) TouchPullView csView; @BindView(R.id.rl_view) RelativeLayout rl_view; private float mTouchStartY; private static final float TOUCH_MOVE_MAX_Y=600; @Override protected int initLayoutId() { return R.layout.activity_view_mv; } @Override protected void initView(@Nullable Bundle savedInstanceState) { rl_view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action=event.getActionMasked(); switch (action){ case MotionEvent.ACTION_DOWN: mTouchStartY=event.getY(); return true; case MotionEvent.ACTION_MOVE: float y=event.getY(); if(y>=mTouchStartY){ float moveSize= y-mTouchStartY; float progress=moveSize>=TOUCH_MOVE_MAX_Y?1:moveSize/TOUCH_MOVE_MAX_Y; csView.setProgress(progress); } return true; case MotionEvent.ACTION_UP: csView.release(); return true; default: break; } return false; } }); } @Override protected void setListener() { } @Override protected void initData() { } }

TouchPullView代码

import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.v4.view.animation.PathInterpolatorCompat; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import trunk.doi.base.R; /** * 作者:Mr.Lee on 2017-9-27 11:57 * 邮箱:569932357@qq.com */ public class TouchPullView extends View { //圆的画笔 private Paint mCirclePaint; //圆的半径 private int mCircleRadius=50; private float mCirclePointX; private float mCirclePointY; private float mProgress; //可拖拽高度 private int mDragHeigh=800; //目标宽度 private int mTargetWidth=400; //贝塞尔曲线 private Path mPath=new Path(); private Paint mPathPaint; //重心点最终高度,决定控制点的Y坐标 private int mTargetGravityHeight=10; //角度变换 0-135 private int mTangentAngle=100; private Interpolator mProgressInterpolator=new DecelerateInterpolator(); private Interpolator mTanentAngleInterpolator; private Drawable mContent=null; private int mContentMargin=0; public TouchPullView(Context context) { super(context); init(null); } public TouchPullView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(attrs); } public TouchPullView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public TouchPullView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(attrs); } /** * 初始化 */ private void init(AttributeSet attrs){ final Context context=getContext(); TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.TouchPullView,0,0); int color=array.getColor(R.styleable.TouchPullView_pColor,0x20000000); mCircleRadius=(int)array.getDimension(R.styleable.TouchPullView_pRadius,mCircleRadius); mDragHeigh=array.getDimensionPixelOffset(R.styleable.TouchPullView_pDragHeight,mDragHeigh); mTangentAngle=array.getInteger(R.styleable.TouchPullView_pTangentAngle,mTangentAngle); mTargetWidth=array.getDimensionPixelOffset(R.styleable.TouchPullView_pDragHeight,mTargetWidth); mTargetGravityHeight=array.getDimensionPixelOffset(R.styleable.TouchPullView_pTargetGravityHeight,mTargetGravityHeight); mContent=array.getDrawable(R.styleable.TouchPullView_pContentDrawable); mContentMargin=array.getDimensionPixelOffset(R.styleable.TouchPullView_pContentDrawableMargin,0); array.recycle(); Paint p=new Paint(Paint.ANTI_ALIAS_FLAG); //抗锯齿 p.setAntiAlias(true); //防抖动 p.setDither(true); //填充方式 p.setStyle(Paint.Style.FILL); p.setColor(color); mCirclePaint=p; //初始化路径部分画笔 p=new Paint(Paint.ANTI_ALIAS_FLAG); //抗锯齿 p.setAntiAlias(true); //防抖动 p.setDither(true); //填充方式 p.setStyle(Paint.Style.FILL); p.setColor(color); mPathPaint=p; //切角路径插值器 mTanentAngleInterpolator= PathInterpolatorCompat.create( (mCircleRadius*2.0f)/mDragHeigh, 90.0f/mTangentAngle ); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); updatePathLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode=MeasureSpec.getMode(widthMeasureSpec); int width=MeasureSpec.getSize(widthMeasureSpec); int heighMode=MeasureSpec.getMode(heightMeasureSpec); int heigh=MeasureSpec.getSize(heightMeasureSpec); int iHeigh=(int)((mDragHeigh*mProgress+0.5)+ 2*mCircleRadius+getPaddingTop()+getPaddingBottom()); int iWidth=2*mCircleRadius+getPaddingLeft()+getPaddingRight(); int measureWidth,measureHeigh; if(widthMode==MeasureSpec.EXACTLY){ measureWidth=width; }else if(widthMode==MeasureSpec.AT_MOST){ measureWidth=Math.min(iWidth,width); }else{ measureWidth=iWidth; } if(heighMode==MeasureSpec.EXACTLY){ measureHeigh=heigh; }else if(heighMode==MeasureSpec.AT_MOST){ measureHeigh=Math.min(iHeigh,heigh); }else{ measureHeigh=iHeigh; } setMeasuredDimension(measureWidth,measureHeigh); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int count=canvas.save(); float tranX=(getWidth()-getValueByLine(getWidth(),mTargetWidth,mProgress))/2; canvas.translate(tranX,0); canvas.drawPath(mPath,mPathPaint); //画圆 canvas.drawCircle(mCirclePointX,mCirclePointY,mCircleRadius,mCirclePaint); Drawable drawable=mContent; if(drawable!=null){ canvas.save(); //剪切矩形区域 canvas.clipRect(drawable.getBounds()); //绘制 drawable.draw(canvas); canvas.restore(); } canvas.restoreToCount(count); } /** * 设置进度 * @param progress */ public void setProgress(float progress){ Log.e("TAG","progress="+progress); mProgress=progress; //重新请求测量 requestLayout(); } private void updatePathLayout(){ final float progress=mProgressInterpolator.getInterpolation(mProgress); //获取可绘制区域高度宽度 final float w=getValueByLine(getWidth(),mTargetWidth,mProgress); final float h=getValueByLine(0,mDragHeigh,mProgress); //X对称轴的参数,圆的圆心X final float cPointX=w/2; //圆的半径 final float cRadius=mCircleRadius; //圆的圆心Y坐标 final float cPointY =h-cRadius; //控制点结束Y坐标 final float endControlY=mTargetGravityHeight; mCirclePointX=cPointX; mCirclePointY= cPointY; final Path path=mPath; //重置 path.reset(); path.moveTo(0,0); //左边部分的结束点和控制点 float lEndPointX,lEndPointY; float lControlPointX,lControlPointY; //角度转弧度 float angle=mTangentAngle*mTanentAngleInterpolator.getInterpolation(progress); double radian=Math.toRadians(angle); float x=(float) (Math.sin(radian)*cRadius); float y=(float) (Math.cos(radian)*cRadius); lEndPointX=cPointX-x; lEndPointY= cPointY +y; //控制点y坐标变化 lControlPointY=getValueByLine(0,endControlY,progress); //控制点与结束定之前的高度 float tHeigh=lEndPointY-lControlPointY; //控制点与x坐标的距离 float tWidth= (float) (tHeigh/Math.tan(radian)); lControlPointX=lEndPointX-tWidth; //左边贝塞尔曲线 path.quadTo(lControlPointX,lControlPointY,lEndPointX,lEndPointY); //连接到右边 path.lineTo(cPointX+(cPointX-lEndPointX),lEndPointY); //右边贝塞尔曲线 path.quadTo(cPointX+cPointX-lControlPointX,lControlPointY,w,0); //更新内容部分Drawable updateContentLayout(cPointX,cPointY,cRadius); } /** * 对内容部分进行测量并设置 * @param cx * @param cy * @param radius */ private void updateContentLayout(float cx,float cy,float radius){ Drawable drawable=mContent; if(drawable!=null){ int margin=mContentMargin; int l=(int)(cx-radius+margin); int r=(int)(cx+radius-margin); int t=(int)(cy-radius+margin); int b=(int)(cy+radius-margin); drawable.setBounds(l,t,r,b); } } //释放动画 private ValueAnimator valueAnimator; /** * 添加释放动作 */ public void release(){ if(valueAnimator==null){ ValueAnimator animator=ValueAnimator.ofFloat(mProgress,0f); animator.setInterpolator(new DecelerateInterpolator()); animator.setDuration(400); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Object val=animation.getAnimatedValue(); if(val instanceof Float){ setProgress((Float) val); } } }); valueAnimator=animator; }else{ valueAnimator.cancel(); valueAnimator.setFloatValues(mProgress,0f); } valueAnimator.start(); } /** * 获取当前值 * @param start * @param end * @param progress * @return */ private float getValueByLine(float start,float end ,float progress){ return start+(end-start)*progress; } }

TestViewBezer代码

import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.os.Build; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.View; /** * 作者:Mr.Lee on 2017-9-27 18:08 * 邮箱:569932357@qq.com */ public class TestViewBezer extends View { private Paint mPaint=new Paint(Paint.ANTI_ALIAS_FLAG); private Path mPath=new Path(); public TestViewBezer(Context context) { super(context); init(); } public TestViewBezer(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public TestViewBezer(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public TestViewBezer(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init(){ Paint paint=mPaint; paint.setAntiAlias(true); paint.setDither(true); mPaint.setColor(0xff000000); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10); Path path=mPath; path.moveTo(100,100); path.lineTo(400,400); path.quadTo(600,100,800,400); path.moveTo(400,800); path.cubicTo(500,600,700,1200,800,800); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath,mPaint); canvas.drawPoint(600,100,mPaint); canvas.drawPoint(500,600,mPaint); canvas.drawPoint(700,1200,mPaint); } }

attr_pull.xml代码

<declare-styleable name="TouchPullView"> <attr name="pColor" format="color" /> <attr name="pRadius" format="dimension" /> <attr name="pDragHeight" format="dimension"></attr> <attr name="pTangentAngle" format="integer" /> <attr name="pTargetWidth" format="dimension" /> <attr name="pTargetGravityHeight" format="dimension" /> <attr name="pContentDrawable" format="reference" /> <attr name="pContentDrawableMargin" format="dimension" /> </declare-styleable>

shape_circle代码

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" > <solid android:color="#fff" /> </shape>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-07-29 07:26:33

Android自定义实现顶部粘性下拉刷新效果的相关文章

Android仿京东、天猫下拉刷新效果

说到下拉刷新,相信大家都不陌生,现在基本上每个项目都会用到.我们公司的项目一直都是使用SwipeRefreshLayout,官方的Material Design风格,好用少Bug.现在下拉刷新大概有下面几种实现方式:一种是直接包在ListView或者RecyclerView的头部,有的则是像SwipeRefreshLayout一样,包在视图的最外层,个人建议使用包在最外层的做法,可拓展性比较强.下面用包在最外层的方法实现京东和天猫的下拉刷新. 1.使用框架Android-Ultra-Pull-T

Android仿百度外卖自定义下拉刷新效果_Android

现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了"百度外卖"的下拉刷新,今天的主题就是它–自定义下拉刷新动画. 看一下实现效果吧: 动画 我们先来看看Android中的动画吧: Android中的动画分为三种: Tween动画,这一类的动画提供了旋转.平移.缩放等效果. Alpha – 淡入淡出 Scale – 缩放效果 Roate – 旋转效果 Translate – 平移效果 Frame动画(帧动画),这一类动画可以创建一个D

Android仿百度外卖自定义下拉刷新效果

现如今的APP各式各样,同样也带来了各种需求,一个下拉刷新都能玩出花样了,前两天订饭的时候不经意间看到了"百度外卖"的下拉刷新,今天的主题就是它–自定义下拉刷新动画. 看一下实现效果吧: 动画 我们先来看看Android中的动画吧: Android中的动画分为三种: Tween动画,这一类的动画提供了旋转.平移.缩放等效果. Alpha – 淡入淡出 Scale – 缩放效果 Roate – 旋转效果 Translate – 平移效果 Frame动画(帧动画),这一类动画可以创建一个D

Android程序开发之Listview下拉刷新上拉(滑动分页)加载更多_Android

最近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多. 新浪微博就是使用这种方式的典型. 当用户从网络上读取微博的时候,如果一下子全部加载用户未读的微博这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容.这时候,我们就需要用到另一个功能,那就是listview的分页了,其实这个分页可以做成客户端的分页,也可以做成服务器端的分页(点击加载时,从服务器对应的加载第N页就好了!!!).通过分

Android实现RecyclerView下拉刷新效果

本文为大家分享了Android实现RecyclerView下拉刷新效果的具体代码,供大家参考,具体内容如下 思路 RealPullRefreshView继承了一个LinearLayout 里面放置了一个刷新头布局,将其margin_top设置为负的刷新头的高度的 再添加一个RecyclerView 触摸事件分发机制,当在特定条件下让RealPullRefreshView拦截触摸事件,否则的话,不拦截,让RecyclerView自己去处理触摸事件 在手指下拉时,定义好不同的状态STATE,在不同状

Android程序开发之Listview下拉刷新上拉(滑动分页)加载更多

最近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多. 新浪微博就是使用这种方式的典型. 当用户从网络上读取微博的时候,如果一下子全部加载用户未读的微博这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容.这时候,我们就需要用到另一个功能,那就是listview的分页了,其实这个分页可以做成客户端的分页,也可以做成服务器端的分页(点击加载时,从服务器对应的加载第N页就好了!!!).通过分

Android中使用RecyclerView实现下拉刷新和上拉加载_Android

推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

Android使用PullToRefresh完成ListView下拉刷新和左滑删除功能_Android

ListView下刷新刷功能相信从事Android开发的猿友们并不陌生,包括现在Google亲儿子SwipeRefreshLayout实现效果在一些APP上也能看见(不过个人不喜欢官方的刷新效果).本文就带领一些刚入门android的朋友或者一起爱分享的朋友来简单的实现ListView的下拉刷新和左滑删除效果. 一.本文主要内容: 使用PullToRefresh完成ListView下拉.上拉刷新: 扩展PullToRefresh完美的实现ListView左滑删除效果: 注意:本文中的PullTo

Android使用PullToRefresh实现上拉加载和下拉刷新效果的代码_Android

在没给大家介绍正文之前,先给大家介绍展示下运行图,如果大家感觉还不错,请继续往下阅读: 相关阅读:分享Android中pullToRefresh的使用心得 项目已同步至:https://github.com/nanchen2251/pullToRefreshDemo 简单使用详情: 1)studio可以直接在app的module设置中直接进行搜索,但是有-的必须添上,而不能用空格代替,为了更加了解这个东西,我还是推荐大家去这里看看,奉上网址: https://github.com/chrisba