前言

最近一直在研究自定义view,正好项目中有一个根据下载进度来实现球体进度的需求,所以自己写了个进度球,代码非常简单。先看下效果:

android 查看大图并支持放大缩小 android 图片放大缩小 实现_ide

效果还是非常不错的。

准备知识

要实现上面的效果我们只要掌握两个知识点就好了,一个是Handler机制,用于发消息刷新我们的进度球,一个是clipDrawable。网上关于Handler的教程很多,这里重点介绍一下clipDrawable,进度球的实现全靠clipDrawable。clipDrawable如下图所示:ClipDrawable和InsertDrawable一样继承DrawableWrapper,DrawableWrapper继承Drawable。


android 查看大图并支持放大缩小 android 图片放大缩小 实现_自定义_02


ClipDrawable是可以进行裁剪操作的drawable,提供了函数 setLevel(@IntRange(from=0,to=10000)intlevel)来设置裁剪的大小。level越大图片越大。level=0时图片完全不显示,level=10000时图片完全显示。

ClipDrawable clipDrawable = (ClipDrawable) getContext().getResources().getDrawable(R.drawable.bottom_top_clip_gradient_color);//获取图片 clipDrawable.setLevel(100);//进行裁剪

一般还要设置裁剪的方向,垂直裁剪还是水平裁剪,我们这个进度球用的是垂直从下向上裁剪。思路:知道了ClipDrawable的用法,进度球就好实现了。只需要一个球形的图片,从下往上裁剪,通过设置setLevel从0到10000,就可以实现进度球从0到进度100的效果了。

实现

1、定义BallProgress,BallProgress继承View,重写onDraw()方法,用于实现进度球的view。

/** * Created time 15:02. * * @author huhanjun * @since 2018/12/26 */public class BallProgress extends View { private float mProgress = 0.0f; //取值位 0 - 1.0 private boolean selected = true; public BallProgress(Context context) { super(context); } public BallProgress(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public BallProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mProgressPaint = new Paint();//初始化,定义画笔。 mProgressPaint.setAntiAlias(true);//设置抗锯齿 } public float getProgress() { return mProgress; } public void setProgress(float progress) {//设置进度,通过进度的大小实现裁剪的大小 mProgress = progress; invalidate(); } private Paint mProgressPaint; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap dst = getRectangleBitmap();//获取bitmap setLayerType(LAYER_TYPE_HARDWARE, null); //开启硬件离屏缓存 canvas.drawBitmap(dst, 0, 0, mProgressPaint); } private Bitmap getRectangleBitmap() { int width = getWidth(); int height = getHeight(); Bitmap dstBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); ClipDrawable clipDrawable = null; clipDrawable = (ClipDrawable) getContext().getResources().getDrawable(R.drawable.bottom_top_clip_single_color);//获取球形的背景图片,用于裁剪,就是上面看到的进度球中的图片 clipDrawable.setBounds(new Rect(0, 0, width, height));//设置边界 clipDrawable.setLevel((int) (10000 * mProgress));//设置进度, Canvas canvas = new Canvas(dstBitmap);//设置画布 clipDrawable.draw(canvas);//绘制 return dstBitmap;//将bitmap返回 }}

有了自定义的BallProgress,就可以在布局中使用了,定义的xml文件如下:

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

上面布局中的ImageView是悬浮球的边界。在MainActivity中来定时的改变进度球的大小。代码如下:

public class MainActivity extends AppCompatActivity { private final int PROGRESS_MESSAGE = 0; private float progress = 0.0f; private BallProgress mBallProgress; private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case PROGRESS_MESSAGE: progress = (progress > 0.9f) ? 0.9f : progress; mBallProgress.setProgress(progress);//接收消息,改变进度球的进度 break; } return true; } }); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initAction(); } private void initView() { mBallProgress = findViewById(R.id.progress); } //发消息 private void initAction() { new Thread(new Runnable() { @Override public void run() { while (true) { progress += 0.02f; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } mHandler.sendEmptyMessage(PROGRESS_MESSAGE);//每隔100毫秒发送一次消息,对进度球进度进行更新。 } } }).start(); }}

上面代码在inAction()中开一个线程,每隔100毫秒发送消息,在handler中处理更新,在handler使用中并没有直接重写hanldeMessage方法,而是传入Handler.Callback并在callback中实现handleMessage方法,这样可以防止内存泄漏。实际应用中,可以是跟业务相关的具体进度。

总结

自定义进度球,用的是继承view,并且通过自定义画笔,重写onDraw()方法来实现,一般自定义view都会重写onDraw()方法,一般进度条都是ClipDrawable来实现的。