Android View : 自定义方形 进度条

作者: silencefun | 来源:发表于2017-08-28 17:19 被阅读202次

    腾讯的狼人杀。玩家发言在矩形的头相框,动态展示倒计时进度条,感觉很好玩,参考网上一些做法:

    最终可以实现效果:


    Paste_Image.png

    可以控制 修改颜色显示 ,是否显示小球等:
    先给出demo地址:
    链接: https://pan.baidu.com/s/1bp8NbGz 密码: 75sg

    主界面大体上就是通过handler 发送消息更新ui的思想,布局也比较简单。
    重点是绘制:

    import android.annotation.TargetApi;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.drawable.shapes.RectShape;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    /**
     * Created by Administrator on 2017/8/26.
     */
    public class SquareProgress extends View {
    
    private String TAG = "SquareProgress";
    //各个画笔的颜色
    private int maxColor = Color.YELLOW;//总进度条颜色为灰色
    private int curColor = Color.GREEN;//当前进度条颜色为蓝色
    private int dotColor = Color.RED;//进度条前端的小圆点为红色
    private float allLength;//进度条的总长度
    private int maxProgress = 60;//总的进度条长度为100(可改变)
    private int curProgress = 0;//当前进度为30(可改变)
    private Paint curPaint;//当前进度条的画笔
    private Paint maxPaint;//总进度条的画笔
    private Paint dotPaint;//进度条前端小圆点的画笔
    private int width;//整个view的宽度,(包括paddingleft和paddingright)
    private int height;//整个view的高度,(包括paddingtop和paddingbottom)
    private float maxProgressWidth;//整个进度条画笔的宽度
    private float curProgressWidth;//当前进度条画笔的宽度
    private float dotDiameter;//进度条顶端小圆点的直径
    
    private boolean canDisplayDot = true;//是否显示小圆点
    private Path curPath;//当前进度条的路径,(总的进度条的路径作为onDraw的局部变量)
    private float proWidth;//整个进度条构成矩形的宽度
    private float proHeight;//整个进度条构成矩形的高度
    private float dotCX;//小圆点的X坐标(相对view)
    private float dotCY;//小圆点的Y坐标(相对view)
    
    public SquareProgress(Context context) {
        super(context);
        initView();
    }
    
    public SquareProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
    
    public SquareProgress(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }
    
    @TargetApi(21)
    
    public SquareProgress(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView();
    }
    
    //
    private void initView() {
        canDisplayDot = true;//默认能显示小圆点
    
        curPaint = new Paint();//当前进度条的画笔设置
        curProgressWidth = dp2Px(5);//dp转px
        curPaint.setAntiAlias(true);//设置画笔抗锯齿
        curPaint.setStyle(Paint.Style.STROKE);//设置画笔(忘了)
        curPaint.setStrokeWidth(curProgressWidth);//设置画笔宽度
        curPaint.setColor(curColor);//设置画笔颜色
    
        maxProgressWidth = dp2Px(5);//总的进度条的画笔设置
        maxPaint = new Paint();
        maxPaint.setAntiAlias(true);
        maxPaint.setColor(maxColor);
        maxPaint.setStyle(Paint.Style.STROKE);
        maxPaint.setStrokeWidth(maxProgressWidth);
    
        dotPaint = new Paint();//小圆点的画笔设置
        dotDiameter = dp2Px(20);
        dotPaint.setAntiAlias(true);
        dotPaint.setStyle(Paint.Style.FILL);//因为是画圆,所以这里是这种模式
        dotPaint.setColor(dotColor);
    
    
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = measureWidth(widthMeasureSpec);//得到view的宽度
        height = measureHeight(heightMeasureSpec);//得到view的高度
        setMeasuredDimension(width, height);//将自己重新测量的宽高度应用到视图上(只设置size而不设置mode,mode是在布局中就确定了的)
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int tWidth = width - getPaddingRight() - getPaddingLeft();//得到整个view出去padding后的宽度
        int tHeight = height - getPaddingTop() - getPaddingBottom();//得到整个view除去padding后的高度
        int point1X = getPaddingLeft() + tWidth / 10;//得到第一个点的X坐标(相对于view)
        int point1Y = getPaddingTop() + tHeight / 10;
        int point2X = tWidth + getPaddingLeft() - tWidth / 10;
        int point2Y = getPaddingTop() + tHeight / 10;
        int point3X = tWidth + getPaddingLeft() - tWidth / 10;
        int point3Y = tHeight + getPaddingTop() - tHeight / 10;
        int point4X = getPaddingLeft() + tWidth / 10;
        int point4Y = tHeight + getPaddingTop() - tHeight / 10;
     //   Log.i(TAG, "onDraw: point1:" + point1X + "," + point1Y);
     //   Log.i(TAG, "onDraw: point2:" + point2X + "," + point2Y);
     //   Log.i(TAG, "onDraw: point3:" + point3X + "," + point3Y);
    //    Log.i(TAG, "onDraw: point4:" + point4X + "," + point4Y);
        proWidth = point3X - point1X;
        proHeight = point3Y - point1Y;
        Log.i(TAG, "onDraw: point5:" + proWidth + "," + proHeight);
        Path maxpath = new Path();//整个进度条的路径
        maxpath.moveTo(point1X, point1Y);
        maxpath.lineTo(point2X, point2Y);
        maxpath.lineTo(point3X, point3Y);
        maxpath.lineTo(point4X, point4Y);
        maxpath.close();
        canvas.drawPath(maxpath, maxPaint);
        allLength = 2 * (proWidth + proHeight);
        curPath = new Path();//当前进度条的路径
        curPath.moveTo(point1X, point1Y);
        float curPersent = (float) curProgress / maxProgress;//当前进度占总进度的百分比
        if (curPersent > 0) {
            if (curPersent < proWidth / allLength) {//处在第一段上面的小圆点的原点坐标和当前进度条的路径
                dotCX = point1X + allLength * curProgress / maxProgress;
                dotCY = point1Y;
                curPath.lineTo(dotCX, dotCY);
            } else if (curPersent < (proHeight + proWidth) / allLength) {
                dotCX = point2X;
                dotCY = point1Y + allLength * curProgress / maxProgress - proWidth;
                curPath.lineTo(point2X, point2Y);
                curPath.lineTo(dotCX, dotCY);
            } else if (curPersent < (2 * proWidth + proHeight) / allLength) {
                dotCX = point1X + allLength - proHeight - allLength * curProgress / maxProgress;
                dotCY = point4Y;
                curPath.lineTo(point2X, point2Y);
                curPath.lineTo(point3X, point3Y);
                curPath.lineTo(dotCX, dotCY);
            } else if (curPersent < 1) {
                dotCX = point1X;
                dotCY = point1Y + allLength - allLength * curProgress / maxProgress;
                curPath.lineTo(point2X, point2Y);
                curPath.lineTo(point3X, point3Y);
                curPath.lineTo(point4X, point4Y);
                curPath.lineTo(dotCX, dotCY);
            } else if (curPersent > 1) {
                dotCX = point1X;
                dotCY = point1Y;
                curPath.lineTo(point2X, point2Y);
                curPath.lineTo(point3X, point3Y);
                curPath.lineTo(point4X, point4Y);
                curPath.close();
            }
        } else {
            dotCX = point1X;
            dotCY = point1Y;
            curPath.lineTo(point1X, point1Y);
        }
        Log.i(TAG, "onDraw: dotC:" + dotCX + "," + dotCY);
        canvas.drawPath(curPath, curPaint);
        if (canDisplayDot) {
            canvas.drawCircle(dotCX, dotCY, dotDiameter * 0.6f, dotPaint);
        }
    
    }
    
    private int measureWidth(int widthMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(widthMeasureSpec);//得到measurespec的模式
        int size = MeasureSpec.getSize(widthMeasureSpec);//得到measurespec的大小
        int padding = getPaddingLeft() + getPaddingRight();//得到padding在宽度上的大小
        if (mode == MeasureSpec.EXACTLY)//这种模式对应于match_parent和具体的数值dp
        {
            result = size;
        } else {
            result = getSuggestedMinimumWidth();//得到屏幕能给的最大的view的最小宽度,原话:Returns the suggested minimum width that the view should use. This returns the maximum of the view's minimum width and the background's minimum width
            result += padding;//考虑padding后最大的view最小宽度
            if (mode == MeasureSpec.AT_MOST)//这种模式对应于wrap_parent
            {
                result = Math.max(result, size);
            }
        }
        return result;
    }
    
    public void setCurProgress(int curProgress) {
        this.curProgress = curProgress;
        invalidate();
    }
    
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);
        int padding = getPaddingBottom() + getPaddingTop();
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = getSuggestedMinimumHeight();
            result += padding;
            if (mode == MeasureSpec.AT_MOST) {
                result = Math.max(result, size);
            }
        }
        return result;
    }
    
    /**
     * 数据转换: dp---->px
     */
    private float dp2Px(float dp) {
        return dp * getContext().getResources().getDisplayMetrics().density;
    }
    

    参考多人的,注释也是十分到位,Activity的布局十分简单,上边可以设定进度,下边是进度条展示,下边的中间是当前进度的文本展示。

    <?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"
    android:orientation="vertical">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:orientation="vertical"
        android:padding="40dp">
    
        <EditText
            android:id="@+id/edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="number" />
    
        <Button
            android:id="@+id/button1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Confirm" />
    </LinearLayout>
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    
        <com.myapp.squareprogress.SquareProgress
            android:id="@+id/sp"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text=" " />
    
    
    </RelativeLayout>
    
    </LinearLayout>
    

    对应的 Java 代码也十分简单。

    public class MainActivity extends Activity {
    
    private EditText mEditText;
    private Button mButton;
    
    TextView tv_time;
    private SquareProgress mSquareProgress;
    Handler handler = new Handler() {
    
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            tv_time.setText(mProgress + "");
            mSquareProgress.setCurProgress(mProgress);
        }
    };
    
    private int mProgress = 60;//倒计时进度
    private Thread mytimeehead;//倒计时显示线程
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mEditText = (EditText) findViewById(R.id.edit);
        tv_time = (TextView) findViewById(R.id.tv_time);
    
        mButton = (Button) findViewById(R.id.button1);
        mSquareProgress = (SquareProgress) findViewById(R.id.sp);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String sData = mEditText.getText().toString();
                int iData = Integer.valueOf(sData);
                mSquareProgress.setCurProgress(iData);
                mProgress = iData;
            }
        });
    
        mytimeehead = new Thread() {
            @Override
            public void run() {
                while (mProgress > 0) {
    
                    mProgress = mProgress - 1;
                    //子线程给主线程发送消息更新UI
                    handler.sendEmptyMessage(0);
                    SystemClock.sleep(1000);
                }
            }
        };
        mytimeehead.start();
    }
    }
    

    private SquareProgress mSquareProgress; 声明findViewById之后,可以调用 mSquareProgress.setCurProgress(int i);来更新进度。

    }

    相关文章

      网友评论

        本文标题:Android View : 自定义方形 进度条

        本文链接:https://www.haomeiwen.com/subject/jfugdxtx.html