自定义万能的progressView

效果预览

这里写图片描述

控件由来

这两天有个哥们给我商量他们公司语音播放进度条的做法。如图这里写图片描述
看到这个图片我首先想到的是自定义view,怎么自定义view呢?

第一个想法

数一下一共有多少根竖条,然后找到每一根竖条直接高度的比例,通过自定义view宽高的比例算数每个竖条和中间横条的位置。这个办法肯定是可以的,但是感觉有点麻烦而且不通用,万一哪天设计的妹子把这个进度图换了我还得重新找规律重新通过复杂的计算来做。

第二种想法

其实这种进度的显示就是两个图层。第一个图层显示灰色全部画下来,第二个要根据进度画出不发蓝色图层就可以了。而且这种方法放之四海都可以用(只要让设计MM给你切两个大小一致颜色不同的图片就ok了)


编码

通过上边的比较我们决定用第二种方法来实现
我们先来分析下我们需要哪些属性:
  • 我们要两个大小相等颜色不同的图片
  • 当前进度和总进度

分析了下好像就学这四个属性就够了于是我们先定attr

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyProgress">
        <attr name="bg_drawable" format="reference"></attr>
        <attr name="progress_drawable" format="reference"></attr>
        <attr name="progress" format="integer"></attr>
        <attr name="max_progress" format="integer"></attr>
        <attr name="orientation" format="enum">
            <enum name="left" value="0"></enum>
            <enum name="top" value="1"></enum>
            <enum name="right" value="2"></enum>
            <enum name="bottom" value="3"></enum>
        </attr>
    </declare-styleable>
</resources>

好像多了一个属性orientation,既然是是自定义要放之四海皆准我们就要考虑到他进度方向有可能是从左向右,有可能是从下往上,所以这里我定义了4个方向

再看看我的自定义view的java代码
  • 定义上边需要的5个属性变量并在构造函数里获取到赋值
public MyProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.MyProgress,defStyleAttr,0);
        //背景图片
        res_bg= typedArray.getResourceId(R.styleable.MyProgress_bg_drawable,0);
        //进度图片
        res_progress=typedArray.getResourceId(R.styleable.MyProgress_progress_drawable,0);
        /方向
        orientation=typedArray.getInteger(R.styleable.MyProgress_orientation, 0);
        //最大进度
        max=typedArray.getInteger(R.styleable.MyProgress_max_progress,100);
        //进度值并转化为百分比
        progressP=typedArray.getInteger(R.styleable.MyProgress_progress,100)*1.0f/max;
        typedArray.recycle();
        init();
    }
  • 既然自定义view就要重写onMeasure确定它的宽高,这个宽高我们自然是用图片自己打宽高了,所以我们在init()里获取图片的bitmap和宽高,再onMeasure里设置成图片的宽高
private void init(){
        //进度图的bitmap
        if(res_progress!=0){
            mBlueBitmap= BitmapFactory.decodeResource(getResources(),res_progress);
        }else{
            mBlueBitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.blue);
        }
        //背景图的bitmap
        if(res_bg!=0){
            mWhiteBitmap= BitmapFactory.decodeResource(getResources(),res_bg);
        }else {
            mWhiteBitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.white);
        }
        //图片宽
        width=mBlueBitmap.getWidth();
        //图片高
        height=mBlueBitmap.getHeight();
    }
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(width, height);
    }
  • 接下下来就是在onDraw()里画图了。其实就两步:1、画背景图 2、按进度比例截取进度图的部分图像,并画在画不上
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mWhiteBitmap, 0, 0, null);
         ImageCrop(canvas, mBlueBitmap, progressP);
    }
  • 那我们重点来看看这个截取图片的代码ImageCrop(),代码也很简单就是通过progress的进度值再结合定义的方向算出来对应的x,y坐标和对应截取图片的大小区域
 public  void ImageCrop(Canvas canvas,Bitmap bitmap,float progress) {
        int w = bitmap.getWidth(); // 得到图片的宽,高
        int h = bitmap.getHeight();
        int x=0;
        int y=0;
        if(orientation==0){
            w=(int) (w*progress);
        }
        if(orientation==1){
            h=(int)(h*progress);
        }
        if(orientation==2){
            x=w-(int)(w*progress);
            w=(int)(w*progress);
        }
        if(orientation==3){
            y=h-(int)(h*progress);
            h=(int)(h*progress);
        }
        //下面这句是关键
        Bitmap tempBitmap=Bitmap.createBitmap(bitmap, x, y, w, h, null, false);
        canvas.drawBitmap(tempBitmap,x,y,null);
    }
  • 到这里这个自定义view已经可以用了,但是我们通常的进度是通过代码控制的 所以我们加个方法来控制进度值progress
public void setProgress(int progress){
        this.progressP=progress*1.0f/max;
        invalidate();
    }
  • 接下来就是使用了,在xml里定义我们的view,在activity里new个线程调用调用setProgress
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity"
    android:background="#000000">

    <com.leisure.progressview.MyProgress
        android:layout_margin="10dp"
        android:id="@+id/left_progress"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:orientation="left"
        app:bg_drawable="@mipmap/btn_photo_select_normal"
        app:progress_drawable="@mipmap/btn_photo_select_push"
        app:progress="50"
        app:max_progress="100"/>
    <com.leisure.progressview.MyProgress
        android:layout_margin="10dp"
        android:id="@+id/top_progress"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:orientation="top"
        app:bg_drawable="@mipmap/btn_file_browser_up_level_normal"
        app:progress_drawable="@mipmap/btn_file_browser_up_level_pressed"
        app:progress="50"
        app:max_progress="100"/>
    <com.leisure.progressview.MyProgress
        android:layout_margin="10dp"
        android:id="@+id/right_progress"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:orientation="right"
        app:bg_drawable="@mipmap/enterprise_normal"
        app:progress_drawable="@mipmap/enterprise_press"
        app:progress="50"
        app:max_progress="100"/>
    <com.leisure.progressview.MyProgress
        android:layout_margin="10dp"
        android:id="@+id/bottom_progress"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:orientation="bottom"
        app:progress="50"
        app:max_progress="100"/>

</LinearLayout>
package com.leisure.progressview;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    MyProgress mProgressLeft,mProgressTop,mProgressRight,mProgressBottom;
    Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mProgressLeft.setProgress(msg.what);
            mProgressTop.setProgress(msg.what);
            mProgressRight.setProgress(msg.what);
            mProgressBottom.setProgress(msg.what);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgressLeft= (MyProgress) findViewById(R.id.left_progress);
        mProgressTop= (MyProgress) findViewById(R.id.top_progress);
        mProgressRight= (MyProgress) findViewById(R.id.right_progress);
        mProgressBottom= (MyProgress) findViewById(R.id.bottom_progress);
        new Thread(){
            @Override
            public void run() {
                super.run();
                for(int i=1;i<=100;i++){
                    mHandler.sendEmptyMessage(i);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

    }
}

总结

自己总是特别懒,不愿意多动脑,但是比较乐于助人,如果是自己的事可能就不会写这个控件了,但是朋友给机会讨论 我就写个试试吧 感谢这个朋友给的机会 就刚学android的时候写过博客 ,好久没有写了希望有人能看懂

github地址:https://github.com/leisurelife/ProgressView

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值