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

最终可以实现效果:

bd679ff64c45

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的布局十分简单,上边可以设定进度,下边是进度条展示,下边的中间是当前进度的文本展示。

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:layout_width="match_parent"

android:layout_height="200dp"

android:orientation="vertical"

android:padding="40dp">

android:id="@+id/edit"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:inputType="number" />

android:id="@+id/button1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="Confirm" />

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1">

android:id="@+id/sp"

android:layout_width="match_parent"

android:layout_height="match_parent" />

android:id="@+id/tv_time"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:text=" " />

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

}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐