Android开发经验谈Android进阶之路Android开发

Android View : 自定义方形 进度条

2017-08-28  本文已影响202人  silencefun

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

最终可以实现效果:


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);来更新进度。

}

上一篇下一篇

猜你喜欢

热点阅读