细心拷贝代码,就可以运行。
易君的开发工具为:Android Studio
功能介绍:
1、自定义控件实现小米秒表表盘
2、获取当前系统的网络时间
3、可以多次计时(计时列表)【RecyclerView BaseQuickAdapter】
4、有【开始】、【计时】、【清零】 三个按钮
看效果图:
如下图,初始效果 和 清零后的效果一样
计时效果:
源码截图:
首先需要集成导入:RecyclerView BaseQuickAdapter
1、在App的build.gradle中添加:
implementation ‘com.android.support:recyclerview-v7:28.0.0’
implementation ‘com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.38’
implementation ‘com.google.android.gms:play-services-maps:16.1.0’
2、在项目 build.gradle 中添加:maven { url ‘https://jitpack.io’ }
源码拷贝
用到的颜色
<color name="Hei">#000000</color>
<color name="Bai">#ffffff</color>
在style 中添加主题 AppTheme01,将背景设为黑色
<style name="AppTheme01">
<item name="colorPrimary">@color/Hei</item> <!--导航栏颜色-->
<item name="colorPrimaryDark">@color/Hei</item> <!--状态栏颜色-->
<item name="android:windowBackground">@color/Hei</item> <!--背景颜色-->
</style>
activity_main.xml 布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/RelativeLayout"
android:layout_width="match_parent"
android:layout_height="370dp">
<com.example.erp_lxkun_jak.xiaomiclock.xiaomiClock
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:id="@+id/Time_TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="00:00:00"
android:textColor="@color/Bai"
android:textSize="60sp" />
<com.example.erp_lxkun_jak.xiaomiclock.xiaomiClock02
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/Time_TextView01"
android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/Time_TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/Time_TextView01"
android:layout_centerHorizontal="true"
android:text="000"
android:textColor="@color/Bai"
android:textSize="40sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/Time_TextView01"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:layout_toEndOf="@+id/Time_TextView"
android:layout_toRightOf="@+id/Time_TextView"
android:text="小时"
android:textColor="@color/Bai"
android:textSize="12sp" />
</RelativeLayout>
<LinearLayout
android:id="@+id/LinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp">
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="1"
android:background="@drawable/button_bg"
android:gravity="center"
android:text="暂停"
android:textColor="@color/Bai"
android:textSize="20sp" />
<TextView
android:id="@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:background="@drawable/button_bg"
android:gravity="center"
android:text="计时"
android:textColor="@color/Bai"
android:textSize="20sp" />
<TextView
android:id="@+id/TextView03"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="1"
android:background="@drawable/button_bg"
android:gravity="center"
android:text="清零"
android:textColor="@color/Bai"
android:textSize="20sp" />
</LinearLayout>
<TextView
android:id="@+id/System_Time_TextView"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_below="@+id/RelativeLayout"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/button_bg"
android:gravity="center"
android:text="0000/00/00 00:00:00"
android:textColor="@color/Bai"
android:textSize="18sp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/RecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/LinearLayout"
android:layout_below="@+id/System_Time_TextView"
android:padding="10dp"
android:background="@drawable/button_bg"
android:layout_margin="10dp" />
</RelativeLayout>
RecyclerView 的 item_time.xml 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/button_bg"
android:gravity="center"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/item_TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:text="1"
android:textColor="@color/Bai"
android:textSize="18sp" />
<TextView
android:id="@+id/item_TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:gravity="right"
android:text="000:00:00:00"
android:textColor="@color/Bai"
android:textSize="18sp" />
</LinearLayout>
按钮的背景 button_bg.xml 布局文件
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:layout_width="match_parent"/>
<size android:layout_height="match_parent"/>
<!-- 默认背景颜色 -->
<solid android:color="@color/Hei" />
<!--边框-->
<stroke android:width="0.5dp"
android:color="@color/Bai" />
<!--圆角-->
<corners android:radius="5dp" />
</shape>
【BaseActivity.java】(记得在 AndroidManifest.xml 中先声明)
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//主题设置
setTheme(R.style.AppTheme01);
}
}
Log的封装的调试类【L.java】
public class L {
//开关
public static final boolean DEBUG = true;
//TAG
public static final String TAG = "Smartbutler"; // Smart butler: 智能滤镜
public static void e(String text){
if(DEBUG){
Log.e(TAG,text);
}
}
}
数据类【Static.java】
public class Static {
public static boolean cartoon = false; //画布旋转的默认值
public static boolean agree_flag = false; //秒表的默认值
}
时间记录 BaseQuickAdapter 的实体类【TimeData.java】
public class TimeData {
private String Type;
private String Time;
public String getType() {
return Type;
}
public void setType(String type) {
Type = type;
}
public String getTime() {
return Time;
}
public void setTime(String time) {
Time = time;
}
}
时间记录列表的 BaseQuickAdapter【TimeAdapter.java】
public class TimeAdapter extends BaseQuickAdapter<TimeData, BaseViewHolder> {
public TimeAdapter(int layoutResId, List data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, TimeData item) {
helper.setText(R.id.item_TextView01,item.getType());
helper.setText(R.id.item_TextView02,item.getTime());
}
}
仿小米秒表 大 表盘自定义控件【xiaomiClock.java】
public class xiaomiClock extends View {
private Paint textPaint, paint;
private Path mTriangle;
private Timer mTimer;
private float agree = 1;
private Shader mshader;
private PathEffect mEffect;
public xiaomiClock(Context context) {
super(context);
}
public xiaomiClock(Context context, AttributeSet attrs) {
super(context, attrs);
//mEffect = new DashPathEffect(new float[]{1,2,5,10,50,20}, 0); // float[]{ 虚线的厚度, 虚线的间距,虚线的厚度, 虚线的间距 ......}
mEffect = new DashPathEffect(new float[]{5,15}, 0); // float[]{ 虚线的厚度, 虚线的间距,虚线的厚度, 虚线的间距 ......}
mshader = new SweepGradient(500, 500, Color.parseColor("#3b3b3b"), Color.parseColor("#ffffff")); //渐变遮罩样式
textPaint = new Paint();
textPaint.setStrokeWidth(16);
textPaint.setColor(Color.WHITE);
textPaint.setStrokeCap(Paint.Cap.ROUND);
textPaint.setAntiAlias(true);
paint = new Paint();
paint.setAntiAlias(true);
mTriangle = new Path();
mTriangle.moveTo(960, 500);// 此点为多边形的顶点
//下面两个x 相等,表示底边的位置
mTriangle.lineTo(1000, 525); // y:底边宽的其中一个顶点
mTriangle.lineTo(1000, 475); //y:底边宽的其中一个顶点
mTriangle.close();
setmTimer();
System.out.println("度数" + ((float) 6.0 / 10));
}
@Override
protected void onDraw(Canvas canvas) {
//绘画手表盘
drawbeauty(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//设置宽高
setMeasuredDimension(1000,1000); //画布大小
}
public void drawbeauty(Canvas canvas) {
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
//设置缓存层,因为下面要实用xfermode,使用xfemode必须使用缓存层,否则会出现黑色背景
int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
//初始化画笔,因为上下两层做画需要的画笔属性不一样,所以只能每次重新设置一次
paint.setStyle(Paint.Style.STROKE); //设置画笔为不填充模式
paint.setPathEffect(mEffect); //设置笔画样式,这里设置的是虚线样式
paint.setStrokeWidth(50); //设置笔画宽度
canvas.drawCircle(500, 500, 420, paint); //画一个纯色表盘,虚线,空心圆形 【表盘位置和大小】
//设置画笔属性,SRC_IN属性,让第二个图案只能花在第一个图案上面,也就是只能画在上面所说那个纯色表盘里面
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//把画笔虚线属性去掉,因为我要的是一个实心圆形,然后让这个实心但是颜色不一样圆形画在上面所说表盘上面,因为设置了xfermode,所以显示的一样会是虚线圆形表盘,但是颜色会变成你现在的颜色
paint.setPathEffect(null);
//设置画笔shader属性,这里设置的是SweepGradient模式,可以让颜色过渡泾渭分明,以圆形为中心开始变化
paint.setShader(mshader);
paint.setStyle(Paint.Style.FILL);
canvas.save(); //保存画布
//旋转画布,然后你就会发现时钟表盘开始动了
if(Static.cartoon){
if(Static.agree_flag){
agree = 1;
Static.agree_flag = false;
}
canvas.rotate(agree, 500, 500); //画布旋转的中心点
}else{
agree = 1;
}
canvas.drawRect(10, 10, 1000, 1100, paint); //渐变矩形绘制
canvas.drawPath(mTriangle, textPaint); //绘制小三角形
canvas.restore();
//最后将画笔去除Xfermode
paint.setXfermode(null);
canvas.restoreToCount(layerId);
}
private void setmTimer() {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
L.e("agree = " + agree); //数值越大,旋转速度越快
agree = agree + 0.022f;
if (agree > 360)
agree = 1;
postInvalidate();
}
}, 1000, 3); //延时/周期
}
}
仿小米秒表 小 表盘自定义控件【xiaomiClock02.java】
public class xiaomiClock02 extends View {
private Paint textPaint, paint;
private Path mTriangle;
private Timer mTimer;
private float agree = 1;
private Shader mshader;
private PathEffect mEffect;
public xiaomiClock02(Context context) {
super(context);
}
public xiaomiClock02(Context context, AttributeSet attrs) {
super(context, attrs);
mEffect = new DashPathEffect(new float[]{5,0}, 0);
mshader = new SweepGradient(110, 110, Color.parseColor("#3b3b3b"), Color.parseColor("#ffffff"));
textPaint = new Paint();
textPaint.setStrokeWidth(16);
textPaint.setColor(Color.WHITE);
textPaint.setStrokeCap(Paint.Cap.ROUND);
textPaint.setAntiAlias(true);
paint = new Paint();
paint.setAntiAlias(true);
mTriangle = new Path();
mTriangle.moveTo(190, 110);
mTriangle.lineTo(110, 112);
mTriangle.lineTo(110, 108);
mTriangle.close();
setmTimer();
System.out.println("度数" + ((float) 6.0 / 10));
}
@Override
protected void onDraw(Canvas canvas) {
drawbeauty(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(220,220);
}
public void drawbeauty(Canvas canvas) {
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
paint.setStyle(Paint.Style.STROKE); //设置画笔为不填充模式
paint.setPathEffect(mEffect); //设置笔画样式,这里设置的是虚线样式
paint.setStrokeWidth(2); //设置笔画宽度
canvas.drawCircle(110, 110, 100, paint); //画一个纯色表盘,虚线,空心圆形 【表盘位置和大小】
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
paint.setPathEffect(null);
paint.setShader(mshader);
paint.setStyle(Paint.Style.FILL);
canvas.save(); //保存画布
if(Static.cartoon){
if(Static.agree_flag){
agree = 1;
Static.agree_flag = false;
}
canvas.rotate(agree, 110, 110); //画布旋转的中心点
}else{
agree = 1;
}
canvas.drawRect(1, 1, 220, 220, paint); //渐变矩形绘制
canvas.drawPath(mTriangle, textPaint); //绘制小三角形
canvas.restore();
paint.setXfermode(null);
canvas.restoreToCount(layerId);
}
private void setmTimer() {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
L.e("agree = " + agree); //数值越大,旋转速度越快
//agree++;
agree = agree + 0.2f + 1f;
if (agree > 360)
agree = 1;
postInvalidate();
}
}, 1000, 3); //延时/周期
}
}
主页面【MainActivity.java】
public class MainActivity extends BaseActivity implements View.OnClickListener {
private TextView mTextView;
private TextView Time_TextView;
//三个按钮
private TextView TextView01;
private TextView TextView02;
private TextView TextView03;
private RecyclerView Recycler_View;
private List<TimeData> Data;
private BaseQuickAdapter Timedapter;
private int number = 1; //用来统计计时的次数
//变化的时间数据 HH:mm:ss
private int H = 0, m = 0, n = 0, s = 0;
private String num_m, num_n, num_s;
private boolean type = true; //用于避免连续点击开始时出错
Thread TimeThread = new Thread(new DataThread()); //创建启动时间控制线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Time_TextView = findViewById(R.id.Time_TextView);
TextView01 = findViewById(R.id.TextView01);
TextView01.setText("开始");
TextView02 = findViewById(R.id.TextView02);
TextView03 = findViewById(R.id.TextView03);
TextView01.setOnClickListener(this);
TextView02.setOnClickListener(this);
TextView03.setOnClickListener(this);
Recycler_View = findViewById(R.id.RecyclerView);
//设置 Recycler_View 布局样式
Recycler_View.setLayoutManager(new GridLayoutManager(MainActivity.this, 1, GridLayoutManager.VERTICAL, false));
Data = new ArrayList<>(); //新建数据对象
initAdapter(); //设置刷新适配器
}
//Recycler_View 适配器
private void initAdapter() {
Timedapter = new TimeAdapter(R.layout.item_time, Data);
Timedapter.openLoadAnimation();
Recycler_View.setAdapter(Timedapter);
}
//时间显示逻辑
private String getWebData() {
if (s >= 100) { s = 0; n++;
if (n >= 60) { n = 0; m++;
if (m >= 60) { m = 0; H++;
}
}
}
if (s <= 9) {
num_s = String.valueOf("0" + s); s++;
} else {
num_s = String.valueOf(s); s++;
}
if (n <= 9)
num_n = String.valueOf("0" + n + "." + num_s);
else
num_n = String.valueOf(n + "." + num_s);
if (m <= 9)
num_m = String.valueOf("0" + m + ":" + num_n);
else
num_m = String.valueOf(m + ":" + num_n);
L.e("-- Time :"+ H +":"+ num_m);
return num_m;
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
//确定计时变化的控件
mTextView = findViewById(R.id.Time_TextView01);
mTextView.setText((String) msg.obj);
if (H <= 99) {
if (H <= 9)
Time_TextView.setText("00" + H);
else
Time_TextView.setText("0" + H);
} else {
Time_TextView.setText("" + H);
}
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.TextView01: /** 开始按钮 */
TextView01.setText("开始");
if(type){
TimeThread.start();//启动时间控制线程
type = false;
Static.cartoon = true;
Static.agree_flag = false;
}
break;
case R.id.TextView02: /** 计时按钮 */
if(TextUtils.isEmpty(num_m)){ //判断是否为空
Toast.makeText(MainActivity.this,"开始后才能计时",Toast.LENGTH_SHORT).show();
}else{
//设置计时记录
TimeData item = new TimeData();
item.setType("第"+ number + "次计时");
item.setTime(H + ":" + num_m);
Data.add(item);
number++; //计时次数递增
Timedapter.notifyItemInserted(0); //在第0的位置 添加一条新数据数据
initAdapter(); //刷新适配器
Recycler_View.smoothScrollToPosition(0); //添加后定位到第一条
//设置倒序 RecyclerView 列表
LinearLayoutManager layout = new LinearLayoutManager(this);
layout.setStackFromEnd(true); //列表再底部开始展示,反转后由上面开始展示
layout.setReverseLayout(true); //列表翻转
Recycler_View.setLayoutManager(layout);
}
break;
case R.id.TextView03: /** 清零按钮 */
type = true;
Static.cartoon = false;
ResetActivity();
break;
}
}
private class DataThread extends Thread {
@Override
public void run() {
while (true) { //无限循环
try {
Thread.sleep(9); //每一毫秒变化一次
} catch (InterruptedException e) {
e.printStackTrace();
}
final String getWebData = getWebData();
final String sysTime = getTimeData();
mHandler.sendMessage(mHandler.obtainMessage(0, getWebData)); //秒表
mHandlerTime.sendMessage(mHandlerTime.obtainMessage(0, sysTime)); //系统时间
}
}
}
//设定显示系统时间
private String getTimeData() {
long sysTime = System.currentTimeMillis();
CharSequence sysTimeStr = DateFormat.format("" + "yyyy/MM/dd HH:mm:ss", sysTime);
return (String) sysTimeStr;
}
@SuppressLint("HandlerLeak")
private Handler mHandlerTime = new Handler() {
private TextView mTextView;
public void handleMessage(android.os.Message msg) {
//确定系统时间变化的控件
mTextView = findViewById(R.id.System_Time_TextView);
mTextView.setText((String) msg.obj);
}
};
//重启MainActivity
private void ResetActivity(){
startActivity(new Intent(this, MainActivity.class));
finish();//关闭自己
overridePendingTransition(0, 0); //去掉Activity切换间的动画
}
}
App下载:https://aifabu.com/RzM3