Android开发-使用自定义View实现loading效果

http://www.hello-code.com/blog/android/201510/5545.html


  • 今天给大家提供一个 Android开发中常用的下载loading效果实例。

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

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

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

    实现代码:

    1、自定义View

      1 public class DownloadPercentView extends View {
      2 
      3     public final static int STATUS_PEDDING = 1;
      4     public final static int STATUS_WAITING = 2;
      5     public final static int STATUS_DOWNLOADING = 3;
      6     public final static int STATUS_PAUSED = 4;
      7     public final static int STATUS_FINISHED = 5;
      8 
      9     // 画实心圆的画笔
     10     private Paint mCirclePaint;
     11     // 画圆环的画笔
     12     private Paint mRingPaint;
     13     // 绘制进度文字的画笔
     14     private Paint mTxtPaint;
     15     // 圆形颜色
     16     private int mCircleColor;
     17     // 圆环颜色
     18     private int mRingColor;
     19     // 半径
     20     private int mRadius;
     21     // 圆环宽度
     22     private int mStrokeWidth = 2;
     23     // 圆心x坐标
     24     private int mXCenter;
     25     // 圆心y坐标
     26     private int mYCenter;
     27     // 总进度
     28     private int mTotalProgress = 100;
     29     // 当前进度
     30     private int mProgress;
     31     //下载状态
     32     private int mStatus = 1;
     33 
     34     //默认显示的图片
     35     private Bitmap mNotBeginImg;
     36     //暂停时中间显示的图片
     37     private Bitmap mPausedImg;
     38     //等待时显示的图片
     39     private Bitmap mWatiImg;
     40     //下载完成时显示的图片
     41     private Bitmap finishedImg;
     42 
     43 
     44 
     45     public DownloadPercentView(Context context, AttributeSet attrs) {
     46         super(context, attrs);
     47         // 获取自定义的属性
     48         initAttrs(context, attrs);
     49         initVariable();
     50     }
     51 
     52     private void initAttrs(Context context, AttributeSet attrs) {
     53         TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs,
     54                 R.styleable.DownloadPercentView, 0, 0);
     55         mRadius = (int)typeArray.getDimension(R.styleable.DownloadPercentView_radius, 100);
     56         mNotBeginImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_notBeginImg)).getBitmap();
     57         mPausedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_pausedImg)).getBitmap();
     58         mWatiImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_waitImg)).getBitmap();
     59         finishedImg = ((BitmapDrawable)typeArray.getDrawable(R.styleable.DownloadPercentView_finishedImg)).getBitmap();
     60 
     61         mNotBeginImg = big(mNotBeginImg, mRadius * 2, mRadius * 2);
     62         mPausedImg = big(mPausedImg, mRadius * 2, mRadius * 2);
     63         mWatiImg = big(mWatiImg, mRadius * 2, mRadius * 2);
     64         finishedImg = big(finishedImg, mRadius * 2, mRadius * 2);
     65 
     66         mStrokeWidth = (int)typeArray.getDimension(R.styleable.DownloadPercentView_strokeWidth, 2);
     67 
     68 //        mRadius = Math.max(mNotBeginImg.getWidth()/2, mNotBeginImg.getHeight()/2) + mStrokeWidth;
     69         mCircleColor = typeArray.getColor(R.styleable.DownloadPercentView_circleColor, 0xFFFFFFFF);
     70         mRingColor = typeArray.getColor(R.styleable.DownloadPercentView_ringColor, 0xFFFFFFFF);
     71     }
     72 
     73     private void initVariable() {
     74         //初始化绘制灰色圆的画笔
     75         mCirclePaint = new Paint();
     76         mCirclePaint.setAntiAlias(true);
     77         mCirclePaint.setColor(mCircleColor);
     78         mCirclePaint.setStyle(Paint.Style.STROKE);
     79         mCirclePaint.setStrokeWidth(mStrokeWidth);
     80 
     81         //初始化绘制圆弧的画笔
     82         mRingPaint = new Paint();
     83         mRingPaint.setAntiAlias(true);
     84         mRingPaint.setColor(mRingColor);
     85         mRingPaint.setStyle(Paint.Style.STROKE);
     86         mRingPaint.setStrokeWidth(mStrokeWidth);
     87 
     88         //初始化绘制文字的画笔
     89         mTxtPaint = new Paint();
     90         mTxtPaint.setAntiAlias(true);
     91         mTxtPaint.setColor(Color.parseColor("#52ce90"));
     92         mTxtPaint.setTextAlign(Paint.Align.CENTER);
     93         mTxtPaint.setTextSize(24);
     94 
     95     }
     96 
     97     @Override
     98     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     99         int width = (int)Math.ceil(mRadius) * 2;
    100         setMeasuredDimension(width, width);
    101     }
    102     
    103     @Override
    104     protected void onDraw(Canvas canvas) {
    105         mXCenter = getWidth() / 2;
    106         mYCenter = getHeight() / 2;
    107         switch (mStatus) {
    108             case STATUS_PEDDING:
    109                 canvas.drawBitmap(mNotBeginImg, 0, 0, null);
    110                 break;
    111             case STATUS_WAITING:
    112                 canvas.drawBitmap(mWatiImg, 0, 0, null);
    113                 break;
    114             case STATUS_DOWNLOADING:
    115                 drawDownloadingView(canvas);
    116                 break;
    117             case STATUS_PAUSED:
    118                 drawPausedView(canvas);
    119                 break;
    120             case STATUS_FINISHED:
    121                 canvas.drawBitmap(finishedImg, 0, 0, null);
    122                 break;
    123         }
    124 
    125     }
    126 
    127     /**
    128      * 绘制下载中的view
    129      * @param canvas
    130      */
    131     private void drawDownloadingView(Canvas canvas) {
    132         //绘制灰色圆环
    133         canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint);
    134 
    135         //绘制进度扇形圆环
    136         RectF oval = new RectF();
    137         //设置椭圆上下左右的坐标
    138         oval.left = mXCenter - mRadius + mStrokeWidth/2;
    139         oval.top = mYCenter - mRadius + mStrokeWidth/2;
    140         oval.right = mXCenter + mRadius - mStrokeWidth/2;
    141         oval.bottom = mYCenter + mRadius - mStrokeWidth/2;
    142         canvas.drawArc(oval, -90, ((float)mProgress / mTotalProgress) * 360, false, mRingPaint);
    143 
    144         //绘制中间百分比文字
    145         String percentTxt = String.valueOf(mProgress);
    146         //计算文字垂直居中的baseline
    147         Paint.FontMetricsInt fontMetrics = mTxtPaint.getFontMetricsInt();
    148         float baseline = oval.top + (oval.bottom - oval.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
    149         canvas.drawText(percentTxt, mXCenter, baseline, mTxtPaint);
    150 
    151     }
    152 
    153     /**
    154      * 绘制暂停时的view
    155      * @param canvas
    156      */
    157     private void drawPausedView(Canvas canvas) {
    158         //绘制灰色圆环
    159         canvas.drawCircle(mXCenter, mYCenter, mRadius - mStrokeWidth/2, mCirclePaint);
    160 
    161         //绘制进度扇形圆环
    162         RectF oval = new RectF();
    163         //设置椭圆上下左右的坐标
    164         oval.left = mXCenter - mRadius + mStrokeWidth/2;
    165         oval.top = mYCenter - mRadius + mStrokeWidth/2;
    166         oval.right = mXCenter + mRadius - mStrokeWidth/2;
    167         oval.bottom = mYCenter + mRadius - mStrokeWidth/2;
    168         canvas.drawArc(oval, -90, ((float) mProgress / mTotalProgress) * 360, false, mRingPaint);
    169 
    170         //绘制中间暂停图标
    171         canvas.drawBitmap(mPausedImg, 0, 0, null);
    172     }
    173 
    174     /**
    175      * 更新进度
    176      * @param progress
    177      */
    178     public void setProgress(int progress) {
    179         mProgress = progress;
    180         postInvalidate();
    181     }
    182 
    183     /**
    184      * 设置下载状态
    185      * @param status
    186      */
    187     public void setStatus(int status) {
    188         this.mStatus = status;
    189         postInvalidate();
    190     }
    191 
    192     /**
    193      * 获取下载状态
    194      * @return
    195      */
    196     public int getStatus() {
    197         return mStatus;
    198     }
    199 
    200     public static Bitmap big(Bitmap b,float x,float y)
    201     {
    202         int w=b.getWidth();
    203         int h=b.getHeight();
    204         float sx=(float)x/w;
    205         float sy=(float)y/h;
    206         Matrix matrix = new Matrix();
    207         matrix.postScale(sx, sy); // 长和宽放大缩小的比例
    208         Bitmap resizeBmp = Bitmap.createBitmap(b, 0, 0, w,
    209                 h, matrix, true);
    210         return resizeBmp;
    211     }
    212 
    213 }

    2、自定义属性

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <resources>
     3 
     4     <declare-styleable name="DownloadPercentView">
     5         <attr name="radius" format="dimension"/>
     6         <attr name="notBeginImg" format="string"/>
     7         <attr name="waitImg" format="string"/>
     8         <attr name="pausedImg" format="string"/>
     9         <attr name="finishedImg" format="string"/>
    10         <attr name="strokeWidth" format="dimension"/>
    11         <attr name="circleColor" format="color"/>
    12         <attr name="ringColor" format="color"/>
    13     </declare-styleable>
    14     
    15 </resources>

    3、使用自定义布局

    首先在布局文件中引用:

     1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:custom="http://schemas.android.com/apk/res-auto"
     3     xmlns:tools="http://schemas.android.com/tools"
     4     android:layout_width="match_parent"
     5     android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
     6     android:paddingRight="@dimen/activity_horizontal_margin"
     7     android:paddingTop="@dimen/activity_vertical_margin"
     8     android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
     9 
    10     <com.bbk.lling.downloadpercentdemo.DownloadPercentView
    11         android:id="@+id/downloadView"
    12         android:layout_width="wrap_content"
    13         android:layout_height="wrap_content"
    14         android:layout_centerInParent="true"
    15         custom:notBeginImg="@drawable/ic_no_download"
    16         custom:waitImg="@drawable/ic_wait"
    17         custom:pausedImg="@drawable/ic_pause"
    18         custom:finishedImg="@drawable/ic_finished"
    19         custom:strokeWidth="2dp"
    20         custom:circleColor="#bdbdbd"
    21         custom:radius="18dp"
    22         custom:ringColor="#52ce90"/>
    23 
    24 </RelativeLayout>

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

     1 package com.bbk.lling.downloadpercentdemo;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.os.Handler;
     6 import android.os.Message;
     7 import android.view.View;
     8 
     9 
    10 public class MainActivity extends Activity {
    11 
    12     public final static int MSG_UPDATE = 1;
    13     public final static int MSG_FINISHED = 2;
    14 
    15     private DownloadPercentView mDownloadPercentView;
    16     private int mDownloadProgress = 0;
    17     private Handler mHandler = new InnerHandler();
    18     private boolean downloading = false;
    19 
    20     @Override
    21     protected void onCreate(Bundle savedInstanceState) {
    22         super.onCreate(savedInstanceState);
    23         setContentView(R.layout.activity_main);
    24         mDownloadPercentView = (DownloadPercentView) findViewById(R.id.downloadView);
    25         mDownloadPercentView.setOnClickListener(new View.OnClickListener() {
    26             @Override
    27             public void onClick(View v) {
    28                 if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PEDDING
    29                         || mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_PAUSED) {
    30                     downloading = true;
    31                     mDownloadPercentView.setStatus(DownloadPercentView.STATUS_DOWNLOADING);
    32                     //模拟下载
    33                     new Thread(new Runnable() {
    34                         @Override
    35                         public void run() {
    36                             while (downloading) {
    37                                 if(mDownloadProgress == 100) {
    38                                     mHandler.sendEmptyMessage(MSG_FINISHED);
    39                                     return;
    40                                 }
    41                                 mDownloadProgress += 1;
    42                                 mHandler.sendEmptyMessage(MSG_UPDATE);
    43                                 try{
    44                                     Thread.sleep(100);
    45                                 } catch (Exception e) {
    46                                 }
    47 
    48                             }
    49                         }
    50                     }).start();
    51                 } else if(mDownloadPercentView.getStatus() == DownloadPercentView.STATUS_DOWNLOADING){
    52                     downloading = false;
    53                     mDownloadPercentView.setStatus(DownloadPercentView.STATUS_PAUSED);
    54                 }
    55             }
    56         });
    57     }
    58 
    59     class InnerHandler extends Handler {
    60         @Override
    61         public void handleMessage(Message msg) {
    62             switch (msg.what) {
    63                 case MSG_FINISHED:
    64                     mDownloadPercentView.setStatus(DownloadPercentView.STATUS_FINISHED);
    65                     break;
    66                 case MSG_UPDATE:
    67                     mDownloadPercentView.setProgress(mDownloadProgress);
    68                     break;
    69             }
    70             super.handleMessage(msg);
    71         }
    72     }
    73 
    74 
    75 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值