前言
在Android开发中,常常会有计时的一些操作,例如收验证码的时候倒计时,秒表的计时等等,于是我就有了一个写自定义View的想法,本文效果图。
正文
那么现在我们将想法换成现实,这个自定义View比较简单,我们来看怎么写的,首先我们还是在EasyView
中进行添加。
一、XML样式
根据上面的效果图,我们首先来确定XML中的属性样式,在attrs.xml中增加如下代码:
<!--计时文字--> <declare-styleable name="TimingTextView"> <!--倒计时--> <attr name="countdown" format="boolean" /> <!--时间最大值--> <attr name="max" format="integer" /> <!--时间单位,时:h,分:m,秒:s--> <attr name="unit"> <enum name="h" value="1" /> <enum name="m" value="2" /> <enum name="s" value="3" /> </attr> </declare-styleable>
这里的计时文字目前有3个属性,第一个boolean用来确定是计时还是倒计时,第二个是最大时间,第三个是时间单位:时分秒。
二、构造方法
之前我说自定义View有三种方式,一种是继承View,一种是继承现有的View,还有一种是继承ViewGroup,那么今天的这个计时文字,我们就可以继承现有的View,这样做的目的就是可以让我们减少一定的工作量,专注于功能上,下面我们在com.llw.easyview
包下新建一个TimingTextView
类,里面的代码如下所示:
public class TimingTextView extends MaterialTextView { /** * 时间单位 */ private int mUnit; /** * 计时最大值 */ private int mMax; /** * 是否倒计时 */ private boolean mCountDown; private int mTotal; /** * 是否计时中 */ private boolean mTiming; public TimingTextView(Context context) { this(context, null); } public TimingTextView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public TimingTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); @SuppressLint("CustomViewStyleable") TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TimingTextView); mCountDown = typedArray.getBoolean(R.styleable.TimingTextView_countdown, false); mMax = typedArray.getInteger(R.styleable.TimingTextView_max, 60); mUnit = typedArray.getInt(R.styleable.TimingTextView_unit, 3); typedArray.recycle(); } }
因为有计时的缘故,所以我们需要一个计时监听,主要用于结束的时候进行调用,可以在com.llw.easyview
下新建一个TimingListener
接口,代码如下:
public interface TimingListener { void onEnd(); }
三、API方法
下面在TimingTextView中新增一些API方法和变量,首先增加变量:
private TimingListener listener; private CountDownTimer countDownTimer;
然后增加API方法:
/** * 设置时间单位 * * @param unit 1,2,3 */ public void setUnit(int unit) { if (unit <= 0 || unit > 3) { throw new IllegalArgumentException("unit value can only be between 1 and 3"); } mUnit = unit; } /** * 设置最大时间值 * * @param max 最大值 */ public void setMax(int max) { mMax = max; } /** * 设置是否为倒计时 * * @param isCountDown true or false */ public void setCountDown(boolean isCountDown) { mCountDown = isCountDown; } public void setListener(TimingListener listener) { this.listener = listener; } public boolean isTiming() { return mTiming; } /** * 开始 */ public void start() { switch (mUnit) { case 1: mTotal = mMax * 60 * 60 * 1000; break; case 2: mTotal = mMax * 60 * 1000; break; case 3: mTotal = mMax * 1000; break; } if (countDownTimer == null) { countDownTimer = new CountDownTimer(mTotal, 1000) { @Override public void onTick(long millisUntilFinished) { int time = 0; if (mCountDown) { time = (int) (millisUntilFinished / 1000); setText(String.valueOf(time)); } else { time = (int) (mTotal / 1000 - millisUntilFinished / 1000); } setText(String.valueOf(time)); } @Override public void onFinish() { //倒计时结束 end(); } }; mTiming = true; countDownTimer.start(); } } /** * 计时结束 */ public void end() { mTotal = 0; mTiming = false; countDownTimer.cancel(); countDownTimer = null; if (listener != null) { listener.onEnd(); } }
代码还是很简单的,你敢信,这个自定义View就写完了,不过可能存在一些问题,我将自定义View的代码都放到了一个library下面里,然后将这个library进行构建成aar,然后上传到mavenCentral()
中。
四、使用
然后我们修改一下activity_main.xml,代码如下所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="16dp" tools:context=".MainActivity"> <com.easy.view.MacAddressEditText android:id="@+id/mac_et" android:layout_width="wrap_content" android:layout_height="wrap_content" app:boxBackgroundColor="@color/white" app:boxStrokeColor="@color/black" app:boxStrokeWidth="2dp" app:boxWidth="48dp" app:separator=":" app:textColor="@color/black" app:textSize="16sp" /> <Button android:id="@+id/btn_mac" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="获取地址" /> <com.easy.view.CircularProgressBar android:id="@+id/cpb_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" app:maxProgress="100" app:progress="10" app:progressbarBackgroundColor="@color/purple_500" app:progressbarColor="@color/purple_200" app:radius="80dp" app:strokeWidth="16dp" app:text="10%" app:textColor="@color/teal_200" app:textSize="28sp" /> <Button android:id="@+id/btn_set_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="随机设置进度" /> <com.easy.view.TimingTextView android:id="@+id/tv_timing" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="计时文字" android:textColor="@color/black" android:textSize="32sp" app:countdown="false" app:max="60" app:unit="s" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:gravity="center" android:orientation="vertical"> <CheckBox android:id="@+id/cb_flag" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="计时" /> <Button android:id="@+id/btn_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始" /> </LinearLayout> </LinearLayout>
预览效果如下图所示:
下面我们回到MainActivity中,在onCreate()
方法中添加如下代码:
//计时文本操作 TimingTextView tvTiming = findViewById(R.id.tv_timing); CheckBox cbFlag = findViewById(R.id.cb_flag); Button btnStart = findViewById(R.id.btn_start); tvTiming.setListener(new TimingListener() { @Override public void onEnd() { tvTiming.setText("计时文字"); btnStart.setText("开始"); } }); cbFlag.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { cbFlag.setText(isChecked ? "倒计时" : "计时"); } }); //计时按钮点击 btnStart.setOnClickListener(v -> { if (tvTiming.isTiming()) { //停止计时 tvTiming.end(); btnStart.setText("开始"); } else { tvTiming.setMax(6); tvTiming.setCountDown(cbFlag.isChecked()); tvTiming.setUnit(3);//单位 秒 //开始计时 tvTiming.start(); btnStart.setText("停止"); } });
下面运行一下看看:
五、源码
如果对你有所帮助的话,不妨 Star 或 Fork,山高水长,后会有期~
源码地址:EasyView