美文网首页Android...首页投稿(暂停使用,暂停投稿)
十分钟学会定制 Android 酷炫下拉刷新。

十分钟学会定制 Android 酷炫下拉刷新。

作者: Anonymous___ | 来源:发表于2016-10-23 16:43 被阅读0次

    这次UI又脑洞大开,嫌弃我们项目中的 SwipeRefreshLayout 的下拉效果没特色,整了一个带主题色彩的下拉刷新,效果图如下:

    想到又要手撸一个下拉刷新,心里顿时上万条草泥马奔腾。做为一只傲娇程序猿,我们怎么能干出这种吃力不讨好的事呢(逻辑判断、Touch事件分发、状态变化写起来很麻烦,还特么可能有一大堆八阿哥)。吃口屎冷静了一下,先分析吧~

    要实现这个下拉刷新,主要的难点有:

    1、下拉刷新功能的实现

    2、刷新过程中的 UI 动效

    3、 Demo 写出来了,要是一个一个页面去移植这个下拉刷新,那又得花不少时间,怎样更快捷的方式替换掉项目中几十上百个页面。

    解决思路:

    难点一:说的下拉刷新的功能实现,很多小伙伴都会说PullToRefreshListview、SwipeRefreshLayout呀,度娘一搜一大把。对,没错,我也是直接在 SwipeRefreshLayout 这个类上修改了刷新 Ui 效果。至于为什么不用PullToRefreshListview,因为我们项目中还有很多页面并不是列表页,但是也需要刷新。

    难点二:刷新过程中的 UI 动效,这个后文我会带着大家分析代码实现。

    难点三:全局搜索替换,把“android.support.v4.widget.SwipeRefreshLayout”替换成你的控件名就好,注意接口回调以及方法名和SwipeRefreshLayout保持一致即可。

    好了,问题都已经分析完了,接下来就撸代码吧~

    难点一解决:我的解决方案是直接修改了SwipeRefreshLayout,但是由于公司的源码不方便拿出来讲解,这里我在 Github 上随便搜了一个和SwipeRefreshLayout 功能一样的 Github Demo 来作讲解,最终实现效果不好有任何影响,希望大家不要介怀。

    使用起来很简单

    在 需要刷新的 View 父节点用 RefreshLayout 包裹

    <com.rongyi.diamond.pulltorefresh.RefreshLayout

    android:id="@+id/refreshLayout"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    <TextView

        android:layout_width="match_parent"

        android:layout_height="50dp" 

        android:background="#FFDAB9"

        android:gravity="center"

        android:text="Target"

        android:textSize="30sp"/>

    </com.rongyi.diamond.pulltorefresh.RefreshLayout>

    然后在 Java代码中设置自定义的下拉刷新头即可。

    RefreshLayout refreshLayout=(RefreshLayout)findViewById(R.id.refreshLayout);

    ShopView shopView=new ShopView(this)

    refreshLayout.setRefreshHeader(shopView);

    其中RefreshLayout就是GitHub 上面搜索的和SwipeRefreshLayout相似的类,ShopView 就是我自己写的自定义下拉刷新样式。

    RefreshLayout 功能:拦截Touch 事件根据判断子 View 是否滑动到顶部来判断是否要显示刷新头,就是一个类似于RefreshLayout的容器。具体实现这里就不做重点讲解了,有兴趣的小伙伴自己自己下载下来读一遍,源码我会在文章结尾处贴出来。

    ShopView:自定义的请求头,难点二中会详细讲解ShopView的实现。

    难点二解决: 

    第一步:新建一个 ShopView 类,继承RelativeLayout(继承 ViewGroup 也行,我嫌弃麻烦),然后在构造方面里面 View.inflate一个布局到当前 View。

    第二步:让布局里面的元素在合适的时候动起来

    首先我们来分析刷新头。动画中的基本元素有:1.购物袋上的手提绳、2.“直击全国专柜特卖现场”文字图片、3.购物袋中喷出的商品、4.购物袋、

    观察 UI 效果图我们发现,购物袋需要在刷新的时候抖动、购物袋上的手提绳需要在下拉的过程中改变效果、刷新的时候购物袋中会喷出商品、刷新头完整出现之后继续下拉会出现“直击全国专柜特卖现场”。

    由于这些元素都包含在刷新头里面,但是上面的4种动画元素是需要根据不同的状态来展示的,因此,我们需要一个接口来监听RefreshLayout中的状态变化,这里原作者已经提供了状态回调,我们直接用就行了。

    public interface RefreshHeader{

    /**

    * 松手,头部隐藏后会回调这个方法

    */

    voidreset();

    /**

    * 下拉出头部的一瞬间调用

    */

    voidpull();

    /**

    * 正在刷新的时候调用

    */

    voidrefreshing();

    /**

    * 头部滚动的时候持续调用

    *

    * @paramcurrentPostarget当前偏移高度

    * @paramlastPostarget上一次的偏移高度

    * @paramrefreshPos可以松手刷新的高度

    * @paramisTouch手指是否按下状态(通过scroll自动滚动时需要判断)

    * @paramstate当前状态

    */

    voidonPositionChange(floatcurrentPos,floatlastPos,floatrefreshPos,booleanisTouch,RefreshLayout.Statestate);

    /**

    * 刷新成功的时候调用

    */

    voidcomplete();

    }

    直接让我们的 ShopView 实现这个接口,在相应的状态回调里面展示相应的动画即可。

    接下来就只需要把这四个动画撸出来就完成我们的定制下拉刷新了~

    动画一:手提绳的变化

    首先,在onPositionChange()条目下拉的时候改变手提绳的效果。经过观察我们发现手提袋就是一个上下翻滚的效果,这里我们直接用一个二阶贝塞尔曲线,起始点不动,控制点 x 轴在起始点正中间,y 轴根据onPositionChange()变化而变化即可。实现核心代码如下:

    @Override

    public void onPositionChange(floatcurrentPos,floatlastPos,floatrefreshPos,booleanisTouch,RefreshLayout.Statestate){

    mBezierLine.setControlY(currentPos);

    }

    这里直接把手提袋封装了一个 View,setControlY方法实际上就是改变了控制点的 Y 轴,重新绘制了 View

    public void setControlY(floaty){

    if(isRefresh){

    return;

    }

    if(y+min

    control.y=y+min;

    }else if(y+min>max&&y+min<2*(max-min)){

    control.y=2*max-min-y;

    }else{

    control.y=min;

    }

    invalidate();

    }

    绘制代码如下:

    protected voidonDraw(Canvascanvas){

    super.onDraw(canvas);

    // 绘制贝塞尔曲线

    mPaint.setColor(Color.WHITE);

    mPaint.setStrokeWidth(2);

    Pathpath=newPath();

    path.moveTo(start.x,start.y);

    path.quadTo(control.x,control.y,end.x,end.y);

    canvas.drawPath(path,mPaint);

    }

    动画二:当条目下拉过程中超过 刷新头的高度时,改变“直击全国专柜特卖现场”的setTranslationY即可

    @Override

    public void onPositionChange(floatcurrentPos,floatlastPos,floatrefreshPos,booleanisTouch,RefreshLayout.Statestate){

    if(currentPos>mHeight){

    inttranslationY=(int) (currentPos-mHeight);

    intdp20=Utils.dp2px(getContext(),20);

    if(translationY>dp20){

    translationY=dp20;

    }

    mIvTrans.setTranslationY(dp20-translationY);

    }else{

    mIvTrans.setTranslationY(Utils.dp2px(getContext(),20));

    }

    }

    动画三:在刷新的回调中开启喷出商品的动画,在刷新结束的时候关闭即可

    @Override

    public voidrefreshing(){

    star();

    }

    Handlerhandler=newHandler(){

    @Override

    public void handleMessage(Messagemsg){

    super.handleMessage(msg);

    addHeart();

    handler.sendEmptyMessageDelayed(0,250);

    }

    };

    public void star(){

    handler.sendEmptyMessage(0);

    }

    说简单点就是通过 handle 重复发送延时消息添加一个 shop,然后再展示一段动画,最后在动画结束的时候 remove 掉就好。addHeart()方面里面代码量较多,具体实现代码就不贴出来了。

    动画四:在刷新的回调中开启购物袋抖动效果,在刷新结束的时候关闭即可。这里只是做了一个简单的 Y轴缩放0.95的过程

    ScaleAnimation scaleAnimation=new ScaleAnimation(1,1.0f,0.95f,1.0f,

    Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);

    scaleAnimation.setDuration(800);

    scaleAnimation.setRepeatCount(100);//设置缩放次数

    rongYi.startAnimation(scaleAnimation);

    难点三解决:

    此时,我们的下拉刷新 Demo 已经完成了,测试没问题之后就可以移植到项目中去了,傲娇的程序员肯定不会选择一个一个页面去修改控件,那是代码搬运工干的蠢事,我们直接全局搜索替换android.support.v4.widget.SwipeRefreshLayout就完工了,不会全局替换代码的小伙伴自己去问一下度娘吧~

    到这里,本次UI 提出的更换下拉刷新效果的需求已经完成,再给大家回顾一次整个流程。首先拿到一个需求先不要慌,冷静下来好好分析,分析完了之后也不要急着敲代码,自己把整个思路再捋一遍,画个草图。最后,实际开发的过程中,能有现成的轮子可以用,就尽量利用,比如说这次的“难点一”,就直接用了现有的轮子。当然,时间充沛的前提下,自己造轮子也是可以的,这样对自身的提高会比较有帮助。

    我的梦想是“没有bug”,感谢阅读。

    源码在这里

    相关文章

      网友评论

        本文标题:十分钟学会定制 Android 酷炫下拉刷新。

        本文链接:https://www.haomeiwen.com/subject/gthkuttx.html