如何实现系统自带下拉刷动画效果?

刚开始看确实感觉很不习惯,看久了,就觉得还不错!所以就想引用到自己的项目中;

接下来就找到源码看看他是如何运用的,首先找到SwipeRefreshLayout,因为这个类是Android自带的下拉刷新实现类,相应的动画也肯定在里面有用到。

可以找到里面用到了两个类MaterialProgressDrawable 和 CircleImageView,这两个类就是我们所看到的加载动画的实现类!

但是这两个类外界不可调用,为什么不可调用呢?打开源码

/**
 *
 * @hide
 */
class CircleImageView extends ImageView {

    private static final int KEY_SHADOW_COLOR = 0x1E000000;
    private static final int FILL_SHADOW_COLOR = 0x3D000000;
    // PX
    private static final float X_OFFSET = 0f;
    private static final float Y_OFFSET = 1.75f;
    private static final float SHADOW_RADIUS = 3.5f;
    private static final int SHADOW_ELEVATION = 4;

    private Animation.AnimationListener mListener;
    private int mShadowRadius;

    public CircleImageView(Context context, int color, final float radius) {
    //....
    }
    //....
}
/**
 * Fancy progress indicator for Material theme.
 *
 * @hide
 */
class MaterialProgressDrawable extends Drawable implements Animatable {
    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
    private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator();
    private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator();
    private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator();

    @Retention(RetentionPolicy.CLASS)
    @IntDef({LARGE, DEFAULT})
    public @interface ProgressDrawableSize {}
    // Maps to ProgressBar.Large style
    static final int LARGE = 0;
    // Maps to ProgressBar default style
    static final int DEFAULT = 1;
    //...
}

两个类都声明为保护的,所以我们无法直接调用,不过居然我们有源码,我们就能复制一份出来!

接下来,我们就来使用他们

这个相信都难不到大家,因为系统已经有使用的例子了。

/**
 * Created by moon.zhong on 2015/4/20.
 */
public class LoadingView extends RelativeLayout {

    private CircleImageView mCircleView;
    private MaterialProgressDrawable mProgress;
    private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA;
    private static final int DEFAULT_CIRCLE_TARGET = 64;
    private static final int CIRCLE_DIAMETER_Big = 45;
    private int mCircleWidth;
    private int mCircleHeight;
    private int mCurrentTargetOffsetTop;
    protected int mOriginalOffsetTop;

    private float mTotalDragDistance = -1;

    private final float REFRESH_SCALE = 1.20F;

    private int mMediumAnimationDuration;
    private Animation mScaleAnimation;

    private Animation mScaleDownAnimation;

    protected int mFrom;

    private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;

    private DecelerateInterpolator mDecelerateInterpolator;

    private boolean mRefresh = false;

    public LoadingView(Context context) {
        super(context);
        initView(getContext());
    }

    public LoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(getContext());
    }

    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(getContext());
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView(getContext());
    }

    private void initView(Context context) {
        createProgressView();
        final DisplayMetrics metrics = getResources().getDisplayMetrics();
        mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER_Big * metrics.density);
        mTotalDragDistance = DEFAULT_CIRCLE_TARGET * metrics.density;
        mMediumAnimationDuration = getResources().getInteger(
                android.R.integer.config_mediumAnimTime);
        mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
        setVisibility(VISIBLE);
    }

    /*创建动画View*/
    private void createProgressView() {
        /*创建圆形的ImageView*/
        mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT, CIRCLE_DIAMETER_Big/2);
        /*创建加载动画Drawable*/
        mProgress = new MaterialProgressDrawable(getContext(), this);
        /*设置颜色*/
        mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);
        mCircleView.setImageDrawable(mProgress);
        mCircleView.setVisibility(View.VISIBLE);
        mProgress.setAlpha(256);
        /*设置加载时的颜色值*/
        mProgress.setColorSchemeColors(0xFC9B4C8B, 0xFC9B7D7C , 0xFC439B7B ,0xFC2798DD ,0xFC2F27DD ,0xFCC745DD ,0xC1FFF238 );
        addView(mCircleView);
    }

    /*这个是动态改变动画的效果*/
    public void setScale(float scale) {
        mProgress.showArrow(true);
        float targetScale = scale;
        mProgress.setArrowScale(Math.min(1.0f, targetScale));
        mProgress.setProgressRotation((targetScale));
        mProgress.setStartEndTrim(0, Math.min(.8f, 0.8f * targetScale));
    }

    /*这里暂时没用到*/
    public void onRefresh(float scale) {

        mRefresh = true;
        startScaleUpAnimation(null);
        animateOffsetToCorrectPosition(mCurrentTargetOffsetTop, mRefreshListener);
    }

    /*这里暂时没用到*/
    public void stopRefresh() {
        mRefresh = false;
        startScaleDownAnimation(mRefreshListener);
    }

    /*开始执行动画效果*/
    public void startAnimation() {

        mProgress.start();
    }

    /*结束知心动画效果*/
    public void stopAnimation() {

        mProgress.stop();
    }

    private Animation.AnimationListener mRefreshListener = new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            if (mRefresh) {

                mProgress.start();

            } else {
                mProgress.stop();
                mCircleView.setVisibility(View.GONE);
                ViewCompat.setScaleX(mCircleView, 0);
                ViewCompat.setScaleY(mCircleView, 0);
            }
            mCurrentTargetOffsetTop = mCircleView.getTop();
        }
    };

    private void startScaleUpAnimation(Animation.AnimationListener listener) {
        mCircleView.setVisibility(View.VISIBLE);
        mScaleAnimation = new Animation() {
            @Override
            public void applyTransformation(float interpolatedTime, Transformation t) {
                setAnimationProgress(interpolatedTime);
            }
        };
        if (listener != null) {
            mScaleAnimation.setAnimationListener(listener);
        }
        mScaleAnimation.setDuration(mMediumAnimationDuration);
        mCircleView.clearAnimation();
        mCircleView.startAnimation(mScaleAnimation);
    }

    private void startScaleDownAnimation(Animation.AnimationListener listener) {
        mScaleDownAnimation = new Animation() {
            @Override
            public void applyTransformation(float interpolatedTime, Transformation t) {
                setAnimationProgress(1 - interpolatedTime);
            }
        };
        mScaleDownAnimation.setDuration(150);
        mCircleView.setAnimationListener(listener);
        mCircleView.clearAnimation();
        mCircleView.startAnimation(mScaleDownAnimation);
    }

    private void animateOffsetToCorrectPosition(int from, Animation.AnimationListener listener) {
        mFrom = from;
        mAnimateToCorrectPosition.reset();
        mAnimateToCorrectPosition.setDuration(200);
        mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator);
        if (listener != null) {
            mAnimateToCorrectPosition.setAnimationListener(listener);
        }
        mCircleView.clearAnimation();
        mCircleView.startAnimation(mAnimateToCorrectPosition);
    }

    /*当view设为可见的时候,动画开始执行,反之动画停止*/
    @Override
    public void setVisibility(int visibility) {
        if (visibility == GONE || visibility == INVISIBLE) {

            mProgress.stop();
        } else {
            mProgress.start();
            mProgress.showArrow(true);
        }
        super.setVisibility(visibility);
    }

    private final Animation mAnimateToCorrectPosition = new Animation() {
        @Override
        public void applyTransformation(float interpolatedTime, Transformation t) {
            int targetTop = 0;
            int endTarget = 0;
            endTarget = (int) (mOriginalOffsetTop + mTotalDragDistance);
            targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
            int offset = targetTop - mCircleView.getTop();
            setTargetOffsetTopAndBottom(offset);
        }
    };

    /**
     * @param progress
     */
    private void setAnimationProgress(float progress) {
        ViewCompat.setScaleX(mCircleView, progress);
        ViewCompat.setScaleY(mCircleView, progress);
    }

    private void setTargetOffsetTopAndBottom(int offset) {
        mCircleView.bringToFront();
        mCircleView.offsetTopAndBottom(offset);
        mCurrentTargetOffsetTop = mCircleView.getTop();
        invalidate();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        final int width = getMeasuredWidth();
        int circleWidth = mCircleView.getMeasuredWidth();
        int circleHeight = mCircleView.getMeasuredHeight();
        mCircleView.layout((width / 2 - circleWidth / 2), 0,
                (width / 2 + circleWidth / 2), circleHeight);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleWidth, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(mCircleHeight, MeasureSpec.EXACTLY));

    }
}

xml 引用

xml调用
<eyeclip.myapplication.LoadingView
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_centerInParent="true"
        >
</eyeclip.myapplication.LoadingView>

最后的效果就是:

demo 下载

时间: 2024-08-03 16:55:03

如何实现系统自带下拉刷动画效果?的相关文章

带下拉子菜单的导航菜单

一.带下拉子菜单的导航菜单 下拉菜单在一些企业网站应用尤为广泛,它存在使用方便,占用空间小等特点.之前纵向导航教程中已使用过二级导航,今天制作下横向导航菜单的二级菜单,方法和纵向一样,只不过由纵向改变为横向而已,下面我们以上一章第二节用图片美化的横向导航中的实例进行修改. 先在html代码增加二级菜单的代码: <div id=”menu”><ul><li><a id=”current” href=”#”>首页</a></li><

安卓自带下拉刷新SwipeRefreshLayout添加上拉刷新功能

在项目里面要用到刷新库,以前都是使用第三方的,不过看到官方出了  SwipeRefreshLayout之后就用SwipeRefreshLayout,但是不知道什么原因官方SwipeRefreshLayout只提供下拉刷新功能,很多时候我们需要上拉刷新功能,所以下载v4源码修改SwipeRefreshLayout,与之相关联的文件有两个分别是SwipeProgressBar,BakedBezierInterpolator把这三个文件拷贝到项目里面,修改一下包名就可以了.如何实现上拉刷新功能,其

导出带下拉框的Excel(数据超出长度解决方案)

  注意:此文档中标注的行号和列号都是下标,从0开始 //下载模板 protected void btnDownLoad_ServerClick(object sender,EventArgs e) { //生成Excel模板 CreateExcelTemp(); //设置想要打开的模板路径  string path = this.MapPath("~/BaseInfo/Temp/GABProdectInfoMaintain.xls"); //将此模板打开或重新保存 ExcelOutE

Xamarin.Form 下拉刷新动画

好像园子里对 Xamarin 感兴趣的人很少啊... 来, 先给各位爷们逗个笑, 本山大爷本色出演: 照例, 上源码: https://github.com/gruan01/ListViewExtend 目前只有 WP 的效果, Android 还在研究, IOS 的还没计划. ------------------------------------------------------ Xamarin.Form 的 ListView 只支持下拉刷新 (这里有用法), 上拉 加载更多 没有对应的事

PresentViewController切换界面(一些系统自带的页面切换动画)

视图切换,没有NavigationController的情况下,一般会使用presentViewController来切换视图并携带切换时的动画, 其中切换方法如下: – presentViewController:animated:completion: 弹出,出现一个新视图 可以带动画效果,完成后可以做相应的执行函数经常为nil– dismissViewControllerAnimated:completion:退出一个新视图 可以带动画效果,完成后可以做相应的执行函数经常为nil 切换动画

android 滚动栏下拉反弹的效果(相似微信朋友圈)

微信朋友圈上面的图片封面,QQ空间说说上面的图片封面都有下拉反弹的效果,这些都是使用滚动栏实现的.下拉,当松开时候.反弹至原来的位置.下拉时候能看到背景图片.那么这里简介一下这样的效果的实现. 本文源代码下载:点击 1.效果图 这部手机显示的分辨率有限,非常老的手机调试. 2.具有反弹效果BounceScrollView package com.org.scroll; import android.content.Context; import android.graphics.Rect; im

快速下拉刷新动画

-(void)setupTableview{ //添加下拉的动画图片 //设置下拉刷新回调 [self.tableView addGifHeaderWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; //设置普通状态的动画图片 NSMutableArray *idleImages = [NSMutableArray array]; for (NSUInteger i = 1; i<=60; ++i) { // U

下拉框选择效果的实现原理

导航栏与下拉框的效果 实现的效果是在导航栏中间出现下拉框选择的效果,当选择某一个时,则上面的字也相应进行修改(此实例代码可以看Coding.net的源代码),这边有把它单独提取出来进行测试,源代码下载:接下来将简单介绍一下此实现的方式及主要代码: 1:因为我们是跟导航栏进行结合,所以这边用到的NavigationController,我们把这个页面的效果放在viewController中,弹出来的下拉列表这边是以表格的形式存在,每个则是表格的一行,行里面包括图标跟文字: 首先看一下AppDele

JQuery打造下拉框联动效果

做联动效果,若是用纯JavaScript来做,往往须要辅助页面保存须要刷新的结果集,然后渲染到原页面.考虑将须要动态刷新的内容自己主动拼接到前一个下拉框之后,当前一个下拉框onchange后,同级的后面的下拉框所有清除,然后又一次拼接刷新的内容.用JQuery实现比較easy,代码以省市联动效果为例实现. JSP页面代码例如以下: <li id="base"> <p>生源地:</p> <label> <select id="