一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态。之前在公司的项目中写过一个,今天抽空来整理一下。

  一般下载都会有这么几种状态:未开始、等待、正在下载、下载结束,当然有时候会有下载出错的状态。等待状态是指用户点击开始下载,但是线程池中没有空闲的线程来处理该次下载,所以状态为等待。

效果图:

  这里我只是演示了一下下载和暂停的状态,其他状态没有演示,在代码中设置就可以了。

实现代码:

1、自定义View

 public class DownloadPercentView extends View {

     public final static int STATUS_PEDDING = 1;
     public final static int STATUS_WAITING = 2;
     public final static int STATUS_DOWNLOADING = 3;
     public final static int STATUS_PAUSED = 4;
     public final static int STATUS_FINISHED = 5;

     // 画实心圆的画笔
     private Paint mCirclePaint;
     // 画圆环的画笔
     private Paint mRingPaint;
     // 绘制进度文字的画笔
     private Paint mTxtPaint;
     // 圆形颜色
     private int mCircleColor;
     // 圆环颜色
     private int mRingColor;
     // 半径
     private int mRadius;
     // 圆环宽度
     private int mStrokeWidth = 2;
     // 圆心x坐标
     private int mXCenter;
     // 圆心y坐标
     private int mYCenter;
     // 总进度
     private int mTotalProgress = 100;
     // 当前进度
     private int mProgress;
     //下载状态
     private int mStatus = 1;

     //默认显示的图片
     private Bitmap mNotBeginImg;
     //暂停时中间显示的图片
     private Bitmap mPausedImg;
     //等待时显示的图片
     private Bitmap mWatiImg;
     //下载完成时显示的图片
     private Bitmap finishedImg;

     public DownloadPercentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         // 获取自定义的属性
         initAttrs(context, attrs);
         initVariable();
     }

     private void initAttrs(Context context, AttributeSet attrs) {
         TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs,
                 R.styleable.DownloadPercentView, 0, 0);
         mRadius = (int)typeArray.getDimension(R.styleable.DownloadPercentView_radius, 100);
         mNotBeginImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_notBeginImg)).getBitmap();
         mPausedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_pausedImg)).getBitmap();
         mWatiImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_waitImg)).getBitmap();
         finishedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_finishedImg)).getBitmap();

         mNotBeginImg = big(mNotBeginImg, mRadius * 2, mRadius * 2);
         mPausedImg = big(mPausedImg, mRadius * 2, mRadius * 2);
         mWatiImg = big(mWatiImg, mRadius * 2, mRadius * 2);
         finishedImg = big(finishedImg, mRadius * 2, mRadius * 2);

         mStrokeWidth = (int)typeArray.getDimension(R.styleable.DownloadPercentView_strokeWidth, 2);

 //        mRadius = Math.max(mNotBeginImg.getWidth()/2, mNotBeginImg.getHeight()/2) + mStrokeWidth;
         mCircleColor = typeArray.getColor(R.styleable.DownloadPercentView_circleColor, 0xFFFFFFFF);
         mRingColor = typeArray.getColor(R.styleable.DownloadPercentView_ringColor, 0xFFFFFFFF);
     }

     private void initVariable() {
         //初始化绘制灰色圆的画笔
         mCirclePaint = new Paint();
         mCirclePaint.setAntiAlias(true);
         mCirclePaint.setColor(mCircleColor);
         mCirclePaint.setStyle(Paint.Style.STROKE);
         mCirclePaint.setStrokeWidth(mStrokeWidth);

         //初始化绘制圆弧的画笔
         mRingPaint = new Paint();
         mRingPaint.setAntiAlias(true);
         mRingPaint.setColor(mRingColor);
         mRingPaint.setStyle(Paint.Style.STROKE);
         mRingPaint.setStrokeWidth(mStrokeWidth);

         //初始化绘制文字的画笔
         mTxtPaint = new Paint();
         mTxtPaint.setAntiAlias(true);
         mTxtPaint.setColor(Color.parseColor("#52ce90"));
         mTxtPaint.setTextAlign(Paint.Align.CENTER);
         mTxtPaint.setTextSize(24);

     }

     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = (int)Math.ceil(mRadius) * 2;
         setMeasuredDimension(width, width);
     }

     @Override
     protected void onDraw(Canvas canvas) {
         mXCenter = getWidth() / 2;
         mYCenter = getHeight() / 2;
         switch (mStatus) {
             case STATUS_PEDDING:
                 canvas.drawBitmap(mNotBeginImg, 0, 0, null);
                 break;
             case STATUS_WAITING:
                 canvas.drawBitmap(mWatiImg, 0, 0, null);
                 break;
             case STATUS_DOWNLOADING:
                 drawDownloadingView(canvas);
                 break;
             case STATUS_PAUSED:
                 drawPausedView(canvas);
                 break;
             case STATUS_FINISHED:
                 canvas.drawBitmap(finishedImg, 0, 0, null);
                 break;
         }

     }

     /**
      * 绘制下载中的view
      * @param canvas
      */
     private void drawDownloadingView(Canvas canvas) {
         //绘制灰色圆环
         canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint);

         //绘制进度扇形圆环
         RectF oval = new RectF();
         //设置椭圆上下左右的坐标
         oval.left = mXCenter - mRadius + mStrokeWidth/2;
         oval.top = mYCenter - mRadius + mStrokeWidth/2;
         oval.right = mXCenter + mRadius - mStrokeWidth/2;
         oval.bottom = mYCenter + mRadius - mStrokeWidth/2;
         canvas.drawArc(oval, -90, ((float)mProgress / mTotalProgress) * 360, false, mRingPaint);

         //绘制中间百分比文字
         String percentTxt = String.valueOf(mProgress);
         //计算文字垂直居中的baseline
         Paint.FontMetricsInt fontMetrics = mTxtPaint.getFontMetricsInt();
         float baseline = oval.top + (oval.bottom - oval.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
         canvas.drawText(percentTxt, mXCenter, baseline, mTxtPaint);

     }

     /**
      * 绘制暂停时的view
      * @param canvas
      */
     private void drawPausedView(Canvas canvas) {
         //绘制灰色圆环
         canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint);

         //绘制进度扇形圆环
         RectF oval = new RectF();
         //设置椭圆上下左右的坐标
         oval.left = mXCenter - mRadius + mStrokeWidth/2;
         oval.top = mYCenter - mRadius + mStrokeWidth/2;
         oval.right = mXCenter + mRadius - mStrokeWidth/2;
         oval.bottom = mYCenter + mRadius - mStrokeWidth/2;
         canvas.drawArc(oval, -90, ((float) mProgress / mTotalProgress) * 360, false, mRingPaint);

         //绘制中间暂停图标
         canvas.drawBitmap(mPausedImg, 0, 0, null);
     }

     /**
      * 更新进度
      * @param progress
      */
     public void setProgress(int progress) {
         mProgress = progress;
         postInvalidate();
     }

     /**
      * 设置下载状态
      * @param status
      */
     public void setStatus(int status) {
         this.mStatus = status;
         postInvalidate();
     }

     /**
      * 获取下载状态
      * @return
      */
     public int getStatus() {
         return mStatus;
     }

     public static Bitmap big(Bitmap b,float x,float y)
     {
         int w=b.getWidth();
         int h=b.getHeight();
         float sx=(float)x/w;
         float sy=(float)y/h;
         Matrix matrix = new Matrix();
         matrix.postScale(sx, sy); // 长和宽放大缩小的比例
         Bitmap resizeBmp = Bitmap.createBitmap(b, 0, 0, w,
                 h, matrix, true);
         return resizeBmp;
     }

 }

2、自定义属性

 <?xml version="1.0" encoding="utf-8"?>
 <resources>

     <declare-styleable name="DownloadPercentView">
         <attr name="radius" format="dimension"/>
         <attr name="notBeginImg" format="string"/>
         <attr name="waitImg" format="string"/>
         <attr name="pausedImg" format="string"/>
         <attr name="finishedImg" format="string"/>
         <attr name="strokeWidth" format="dimension"/>
         <attr name="circleColor" format="color"/>
         <attr name="ringColor" format="color"/>
     </declare-styleable>

 </resources>

3、使用自定义布局

  首先在布局文件中引用:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:custom="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin"
     android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

     <com.bbk.lling.downloadpercentdemo.DownloadPercentView
         android:id="@+id/downloadView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerInParent="true"
         custom:notBeginImg="@drawable/ic_no_download"
         custom:waitImg="@drawable/ic_wait"
         custom:pausedImg="@drawable/ic_pause"
         custom:finishedImg="@drawable/ic_finished"
         custom:strokeWidth="2dp"
         custom:circleColor="#bdbdbd"
         custom:radius="18dp"
         custom:ringColor="#52ce90"/>

 </RelativeLayout>

  然后我这里在Activity使用一个线程来模拟下载过程来演示:

 package com.bbk.lling.downloadpercentdemo;

 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.view.View;

 public class MainActivity extends Activity {

     public final static int MSG_UPDATE = 1;
     public final static int MSG_FINISHED = 2;

     private DownloadPercentView mDownloadPercentView;
     private int mDownloadProgress = 0;
     private Handler mHandler = new InnerHandler();
     private boolean downloading = false;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         mDownloadPercentView = (DownloadPercentView) findViewById(R.id.downloadView);
         mDownloadPercentView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PEDDING
                         || mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PAUSED) {
                     downloading = true;
                     mDownloadPercentView.setStatus(DownloadPercentView.STATUS_DOWNLOADING);
                     //模拟下载
                     new Thread(new Runnable() {
                         @Override
                         public void run() {
                             while (downloading) {
                                 if(mDownloadProgress == 100) {
                                     mHandler.sendEmptyMessage(MSG_FINISHED);
                                     return;
                                 }
                                 mDownloadProgress += 1;
                                 mHandler.sendEmptyMessage(MSG_UPDATE);
                                 try{
                                     Thread.sleep(100);
                                 } catch (Exception e) {
                                 }

                             }
                         }
                     }).start();
                 } else if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_DOWNLOADING){
                     downloading = false;
                     mDownloadPercentView.setStatus(DownloadPercentView.STATUS_PAUSED);
                 }
             }
         });
     }

     class InnerHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_FINISHED:
                     mDownloadPercentView.setStatus(DownloadPercentView.STATUS_FINISHED);
                     break;
                 case MSG_UPDATE:
                     mDownloadPercentView.setProgress(mDownloadProgress);
                     break;
             }
             super.handleMessage(msg);
         }
     }

 }

源码下载:https://github.com/liuling07/DownloadPercentDemo

Android中使用自定义View实现下载进度的显示的更多相关文章

  1. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  2. Android中制作自定义dialog对话框的实例

    http://www.jb51.net/article/83319.htm   这篇文章主要介绍了Android中制作自定义dialog对话框的实例分享,安卓自带的Dialog显然不够用,因而我们要继 ...

  3. iOS开发小技巧--获取自定义的BarButtonItem中的自定义View的方法(customView)

    如果BarButtonItem是通过[[UIBarButtonItem alloc] initWithCustomView:(nonnull UIView *)]方法设置的.某些情况下需要修改BarB ...

  4. iOS 在UITableViewCell中加入自定义view时view的frame设定注意

    由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...

  5. 从一个简洁的进度刻度绘制中了解自定义View的思路流程

    先看效果(原谅我的渣像素),进度的刻度.宽度.颜色可以随意设定: [项目github地址: https://github.com/zhangke3016/CircleLoading] 实现起来并不难, ...

  6. Android中使用AsyncTask实现文件下载以及进度更新提示

    Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和 ...

  7. Android 中使用自定义字体的方法

    1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . <?xml version="1.0 ...

  8. Android 初阶自定义 View 字符头像

    自己很少做自定义 View ,只有最开始的时候跟着郭神写了一个小 Demo ,后来随着见识的越来越多,特别是在开源社区看到很多优秀的漂亮的控件,都是羡慕的要死,但是拉下来的代码还是看不明白,而且当时因 ...

  9. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

随机推荐

  1. django 其他地址访问不了问题

    启动的时候 使用 python manage.py runserver 0.0.0.0:8000  即可接收所有IP访问

  2. iOS - QRCode 二维码

    1.QRCode 在 iOS7 以前,在 iOS 中实现二维码和条形码扫描,我们所知的有,两大开源组件 ZBar 与 ZXing. 这两大组件我们都有用过,这里总结下各自的缺点: 1.ZBar 在扫描 ...

  3. SQL Server如何编辑超过前200行的数据

    从SQL Server 2008开始,微软为了提高查询效率等原因,右键点击表时弹出菜单中默认没有"显示所有行",而以"选择前1000行"替代.这有时会为我们带来 ...

  4. 10个开源的PHP网站内容管理系统

    1. DEDE -这是一款国内开源的cms,作者是一个个人,能做出如此功能的cms,是相当不错的.2007版功能十分强大,希望能改善之前数据量一大,更新静态页就很慢的缺点.因为开源,有较多的玩家和拥护 ...

  5. angularjs中ng-selected使用方法

    ng-selected只能应用在option标签上,就像ng-submit只能应用在form标签上一样. ng-selected指令为select设置了指定的选中值,HTML规范不允许浏览器保存类似s ...

  6. Eclipse 为jar包加入 Java Source和Javadoc(如何向Eclipse中导入源码和doc)

    : 当我们在MyEclipse中加入Struct,只是引入了jar包,这时使用jar包里面的类,是无法看到源码的,看到的只是这样 是反编译后的一些东西 加入源码 那么如果看到源码呢,就需要导入了 找到 ...

  7. Python基础——0前言

    python虽然这几年才兴起,但是已经是一门“老”语言了. python的诞生历史也很有趣.Python的创始人为Guido van Rossum(龟叔).1989年圣诞节期间,在阿姆斯特丹,Guid ...

  8. consul &amp; registrator &amp; consul-template 使用

    consul & registrator & consul-template 使用 参考这里的文章: https://www.jianshu.com/p/a4c04a3eeb57 do ...

  9. 20165206 2017-2018-2 《Java程序设计》第五周学习总结

    20165206 2017-2018-2 <Java程序设计>第五周学习总结 教材学习内容总结 内部类:支持在一个类中定义另一个类的类. 外嵌类:包含内部类的类,称为内部类的外嵌类. 匿名 ...

  10. 作业二/Git的安装以及使用

    作业要求来自https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2097 GitHub地址 https://github.com/20160 ...