Android自定义实现顶部粘性下拉刷新效果
更新时间:2017年10月18日 15:19:32 作者:569932357
这篇文章主要为大家详细介绍了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>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:
- Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能(附源码)
- Android下拉刷新ListView——RTPullListView(demo)
- Android PullToRefreshLayout下拉刷新控件的终结者
- Android中使用RecyclerView实现下拉刷新和上拉加载
- Android下拉刷新上拉加载控件(适用于所有View)
- Android官方下拉刷新控件SwipeRefreshLayout使用详解
- android开发教程之实现listview下拉刷新和上拉刷新效果
- Android实现上拉加载更多以及下拉刷新功能(ListView)
- Android RecyclerView实现下拉刷新和上拉加载
- android下拉刷新ListView的介绍和实现代码
相关文章
ProgressBar、ProgessDialog-用法(详解)
下面小编就为大家带来一篇ProgressBar、ProgessDialog-用法(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧2017-06-06Android中使用 AutoCompleteTextView 实现手机号格式化附带清空历史的操作
有个小伙伴遇到了这样一个问题,就是AutoCompleteTextView实现自动填充的功能。同时要具备手机格式化的功能。接下来通过本文给大家分享使用 AutoCompleteTextView 实现手机号格式化附带清空历史的操作方法,需要的朋友参考下2017-03-03Android获取arrays.xml里的数组字段值实例详解
这篇文章主要介绍了Android获取arrays.xml里的数组字段值实例详解的相关资料,需要的朋友可以参考下2017-04-04ViewModel中StateFlow和SharedFlow单元测试使用详解
这篇文章主要为大家介绍了ViewModel中StateFlow和SharedFlow单元测试使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-01-01
最新评论