慕课网app下拉刷新图标填充效果的实现

之前看到一种下拉刷新的效果,与以往的下拉效果都不一样,大多数下拉刷新都是一个圆形进度条在旋转,而这个下拉刷新则是一个不断填充的效果。本以为这是个自定义View,后来反编译慕课网的app后提取资源的时候看到好多的图片,那大概慕课网app内部的实现应该是帧动画达到这种效果。而当我看到这种效果的时候,由于前段时间在学自定义控件,所以本能的反应则是自定义的。首先我们看下慕课网的效果。如下图

        

而我的也实现了一个这个图标填充的简单版。如下图

    

整个实现使用图形的混合模式+贝塞尔曲线,贝塞尔曲线的绘制参考自爱哥的博客  贝塞尔曲线内容

资源文件就只有下面这个图标,该图标提取自慕课网app,然后对内部的火焰进行透明处理过


既然是自定义View,那就要继承View,实现onDraw,onMeasure等方法,这里将控件的宽度高度直接设置为图片的宽度和高度加上内边距,并且去实现相应的逻辑去判断MeasureSpec的模式是哪个从而进行处理。

先贴代码

  1. package cn.edu.zafu.view;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.Bitmap.Config;  
  6. import android.graphics.BitmapFactory;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Path;  
  11. import android.graphics.PorterDuff;  
  12. import android.graphics.PorterDuffXfermode;  
  13. import android.util.AttributeSet;  
  14. import android.view.View;  
  15.   
  16. /** 
  17.  * @author lizhangqu 
  18.  *  
  19.  *         2015-3-5 
  20.  */  
  21. public class CustomView extends View {  
  22.     private PorterDuffXfermode porterDuffXfermode;// Xfermode  
  23.     private Paint paint;// 画笔  
  24.     private Bitmap bitmap;// 源图片  
  25.     private int width, height;// 控件宽高  
  26.     private Path path;// 画贝塞尔曲线需要用到  
  27.     private Canvas mCanvas;// 在该画布上绘制目标图片  
  28.     private Bitmap bg;// 目标图片  
  29.   
  30.     private float controlX, controlY;// 贝塞尔曲线控制点,使用三阶贝塞尔曲线曲线,需要两个控制点,两个控制点都在该变量基础上生成  
  31.     private float waveY;// 上升的高度  
  32.   
  33.     private boolean isIncrease;// 用于控制控制点水平移动  
  34.   
  35.     private boolean isReflesh = true;// 是否刷新并产生填充效果,默认为true  
  36.   
  37.     /** 
  38.      * @return 是否刷新 
  39.      */  
  40.     public boolean isReflesh() {  
  41.         return isReflesh;  
  42.     }  
  43.   
  44.     /** 
  45.      * 提供接口设置刷新 
  46.      *  
  47.      * @param isReflesh 
  48.      */  
  49.     public void setReflesh(boolean isReflesh) {  
  50.         this.isReflesh = isReflesh;  
  51.         //重绘  
  52.         postInvalidate();  
  53.     }  
  54.   
  55.     /** 
  56.      * @param context 
  57.      */  
  58.     public CustomView(Context context) {  
  59.         this(context, null);  
  60.     }  
  61.   
  62.     /** 
  63.      * @param context 
  64.      * @param attrs 
  65.      */  
  66.     public CustomView(Context context, AttributeSet attrs) {  
  67.         this(context, attrs, 0);  
  68.     }  
  69.   
  70.     /** 
  71.      * @param context 
  72.      * @param attrs 
  73.      * @param defStyle 
  74.      */  
  75.     public CustomView(Context context, AttributeSet attrs, int defStyle) {  
  76.         super(context, attrs, defStyle);  
  77.         init();  
  78.     }  
  79.   
  80.     /** 
  81.      * 初始化变量 
  82.      */  
  83.     private void init() {  
  84.         // 初始化画笔  
  85.         paint = new Paint();  
  86.         paint.setAntiAlias(true);  
  87.         paint.setDither(true);  
  88.         paint.setStyle(Paint.Style.FILL);  
  89.         paint.setColor(Color.parseColor("#ffc9394a"));  
  90.         // 获得资源文件  
  91.         bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mooc);  
  92.         // 设置宽高为图片的宽高  
  93.         width = bitmap.getWidth();  
  94.         height = bitmap.getHeight();  
  95.   
  96.         // 初始状态值  
  97.         waveY = 7 / 8F * height;  
  98.         controlY = 17 / 16F * height;  
  99.   
  100.         // 初始化Xfermode  
  101.         porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);  
  102.         // 初始化path  
  103.         path = new Path();  
  104.         // 初始化画布  
  105.         mCanvas = new Canvas();  
  106.         // 创建bitmap  
  107.         bg = Bitmap.createBitmap(width, height, Config.ARGB_8888);  
  108.         // 将新建的bitmap注入画布  
  109.         mCanvas.setBitmap(bg);  
  110.   
  111.     }  
  112.   
  113.     @Override  
  114.     protected void onDraw(Canvas canvas) {  
  115.         // 画目标图,存在bg上  
  116.         drawTargetBitmap();  
  117.         // 将目标图绘制在当前画布上,起点为左边距,上边距的交点  
  118.         canvas.drawBitmap(bg, getPaddingLeft(), getPaddingTop(), null);  
  119.         if (isReflesh) {  
  120.             // 重绘,使用boolean变量isReflesh进行控制,并对外提供访问的接口,默认为true且刷新  
  121.             invalidate();  
  122.         }  
  123.     }  
  124.   
  125.     private void drawTargetBitmap() {  
  126.         // 重置path  
  127.         path.reset();  
  128.         // 擦除像素  
  129.         bg.eraseColor(Color.parseColor("#00ffffff"));  
  130.   
  131.         // 当控制点的x坐标大于或等于终点x坐标时更改标识值  
  132.         if (controlX >= width + 1 / 2 * width) {  
  133.             isIncrease = false;  
  134.         }  
  135.         // 当控制点的x坐标小于或等于起点x坐标时更改标识值  
  136.         else if (controlX <= -1 / 2 * width) {  
  137.             isIncrease = true;  
  138.         }  
  139.   
  140.         // 根据标识值判断当前的控制点x坐标是该加还是减  
  141.         controlX = isIncrease ? controlX + 10 : controlX - 10;  
  142.         if (controlY >= 0) {  
  143.             // 波浪上移  
  144.             controlY -= 1;  
  145.             waveY -= 1;  
  146.         } else {  
  147.             // 超出则重置位置  
  148.             waveY = 7 / 8F * height;  
  149.             controlY = 17 / 16F * height;  
  150.         }  
  151.   
  152.         // 贝塞尔曲线的生成  
  153.         path.moveTo(0, waveY);  
  154.         // 两个控制点通过controlX,controlY生成  
  155.         path.cubicTo(controlX / 2, waveY - (controlY - waveY),  
  156.                 (controlX + width) / 2, controlY, width, waveY);  
  157.         // 与下下边界闭合  
  158.         path.lineTo(width, height);  
  159.         path.lineTo(0, height);  
  160.         // 进行闭合  
  161.         path.close();  
  162.   
  163.         // 以上画贝塞尔曲线代码参考自爱哥博客  
  164.         // http://blog.csdn.net/aigestudio/article/details/41960507  
  165.   
  166.         mCanvas.drawBitmap(bitmap, 00, paint);// 画慕课网logo  
  167.         paint.setXfermode(porterDuffXfermode);// 设置Xfermode  
  168.         mCanvas.drawPath(path, paint);// 画三阶贝塞尔曲线  
  169.         paint.setXfermode(null);// 重置Xfermode  
  170.     }  
  171.   
  172.     @Override  
  173.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  174.         // 获得宽高测量模式和大小  
  175.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  176.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  177.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  178.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  179.         // 保存测量结果  
  180.         int width, height;  
  181.   
  182.         if (widthMode == MeasureSpec.EXACTLY) {  
  183.             // 宽度  
  184.             width = widthSize;  
  185.         } else {  
  186.             // 宽度加左右内边距  
  187.             width = this.width + getPaddingLeft() + getPaddingRight();  
  188.             ;  
  189.             if (widthMode == MeasureSpec.AT_MOST) {  
  190.                 // 取小的那个  
  191.                 width = Math.min(width, widthSize);  
  192.             }  
  193.   
  194.         }  
  195.   
  196.         if (heightMode == MeasureSpec.EXACTLY) {  
  197.             // 高度  
  198.             height = heightSize;  
  199.         } else {  
  200.             // 高度加左右内边距  
  201.             height = this.height + getPaddingTop() + getPaddingBottom();  
  202.             ;  
  203.             if (heightMode == MeasureSpec.AT_MOST) {  
  204.                 // 取小的那个  
  205.                 height = Math.min(height, heightSize);  
  206.             }  
  207.   
  208.         }  
  209.         // 设置高度宽度为logo宽度和高度,实际开发中应该判断MeasureSpec的模式,进行对应的逻辑处理,这里做了简单的判断测量  
  210.         setMeasuredDimension(width, height);  
  211.   
  212.     }  
  213.   
  214. }  


控件的使用
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/ll"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.     <cn.edu.zafu.view.CustomView   
  7.         android:id="@+id/cv"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:padding="20dp"  
  11.         android:layout_centerInParent="true"  
  12.         android:background="#0000ff"  
  13.         />  
  14. </RelativeLayout>  

如果要停止其不断填充的效果,通过函数setReflesh设置isReflesh变量为false即可。

整个实现过程还是相对简单的,基本上注释都讲的很清楚了,这里也不再重复了,文章中涉及到的两个知识点(图形的混合模式和贝塞尔曲线)的相关内容参考下面两篇文章

图形混合模式 http://blog.csdn.net/aigestudio/article/details/41316141

贝塞尔曲线  http://blog.csdn.net/aigestudio/article/details/41960507
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值