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

视频地址
http://www.imooc.com/learn/830

这里写图片描述

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>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值