最近一段时间做了一个应用的项目,感觉一年没写应用,很多东西都忘记了,也遇到了很多问题,搞得焦头烂额的,以后还是会写一写应用小demo,练练手。前段时间做了一个简单的倒计时控件,效果图如下。
该控件由数字选择和时间倒计时两个界面组成,时间设置完毕点击 “开始” 后就会跳转到倒计时界面开始倒计时。
一、数字选择界面
由于是针对TV产品的,所以界面做了焦点的处理。针对子控件的焦点问题,统一将焦点给父布局,子布局不能获取焦点。
下图是小时的布局代码。这里采用我说的那种焦点处理方式:1、父布局根节点 android:focusable="true" 2、子控件里能自动获取焦点的控件android:focusable="false" 3、子控件加上属性 android:duplicateParentState="true" 这个属性非常的重要,它可以跟随父控件的状态变化,这样子控件的颜色才会随焦点变化
布局的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<com.jmgo.countdowntimerview.widget.SelectStateLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/cd_hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:focusable="true">
<ImageView
android:id="@+id/prev1"
android:layout_width="16px"
android:layout_height="16px"
android:layout_centerVertical="true"
android:src="@drawable/prev_icon1"
android:duplicateParentState="true"
android:visibility="invisible"/>
<Button
android:id="@+id/bt_hour"
android:layout_width="151px"
android:layout_height="87px"
android:layout_marginLeft="18px"
android:layout_toRightOf="@id/prev1"
android:background="@drawable/bt_hour_bg_selector"
android:focusable="false"
android:text="0"
android:textColor="@color/item_color_selector"
android:duplicateParentState="true"/>
<ImageView
android:id="@+id/prev2"
android:layout_width="16px"
android:layout_height="16px"
android:layout_centerVertical="true"
android:layout_marginLeft="18px"
android:layout_toRightOf="@id/bt_hour"
android:src="@drawable/prev_icon2"
android:duplicateParentState="true"
android:visibility="invisible"/>
<TextView
android:id="@+id/tv_hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="28px"
android:layout_toRightOf="@id/prev2"
android:text="hour"
android:textColor="@color/btn_focus_color"
android:textSize="28px"
android:duplicateParentState="true"/>
</com.jmgo.countdowntimerview.widget.SelectStateLayout>
二、数字选择界面逻辑代码
逻辑部分就很简单了,只有一个onkey 监听事件和一个接口将选择的数字时间给倒计时界面回调。
1、初始化部分
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Log.d(TAG, "--onFinishInflate---");
LayoutInflater.from(getContext()).inflate(R.layout.custome_coundown_timer, this, true);
customCountHour = findViewById(R.id.hour);
customCountHour.setOnKeyListener(this);
customCountHour.setOnFocusChangeListener(this);
mHour = (Button) findViewById(R.id.bt_hour);
mHour.setText(String.valueOf(0));
prv1 = (ImageView) findViewById(R.id.prev1);
prv2 = (ImageView) findViewById(R.id.prev2);
customCountMinute = findViewById(R.id.minute);
customCountMinute.setOnKeyListener(this);
customCountMinute.setOnFocusChangeListener(this);
mMinute = (Button) findViewById(R.id.bt_minute);
mMinute.setText(String.valueOf(0));
prv3 = (ImageView) findViewById(R.id.prev3);
prv4 = (ImageView) findViewById(R.id.prev4);
}
2、Button 的OnKey监听时间,当小时到0或24的时候让显示 为24或0,这样有一个数字循环的效果。
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//Log.d(TAG,"v="+v+",keyCode="+keyCode+",event="+event);
if (v == customCountHour) {
int i = Integer.valueOf((String) mHour.getText());
if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
//Log.d(TAG, "左键被按下了"+",i="+i);
if (i == 0) {
mHour.setText(String.valueOf(24));
} else {
mHour.setText(String.valueOf(i - 1));
}
Log.d(TAG, "mHour.getText()=" + mHour.getText() + ",mMinute.getText()=" + mMinute.getText());
pickTime.getPickTime(Integer.valueOf((String) mHour.getText()), Integer.valueOf((String) mMinute.getText()));
return true;
} else if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
//Log.d(TAG, "右键被按下了");
if (i == 24) {
mHour.setText(String.valueOf(0));
} else {
mHour.setText(String.valueOf(i + 1));
}
Log.d(TAG,"onCustomPickTime=="+pickTime);
Log.d(TAG, "mHour.getText()=" + mHour.getText() + ",mMinute.getText()=" + mMinute.getText());
Log.d(TAG,"--"+ Integer.valueOf((String) mHour.getText())+",.... "+ Integer.valueOf((String) mMinute.getText()));
pickTime.getPickTime(Integer.valueOf((String) mHour.getText()), Integer.valueOf((String) mMinute.getText()));
return true;
}
3、定义一个接口将设置的时间给倒计时界面回调
public interface OnCustomPickTime {
void getPickTime(int hour, int minute);
}
三、倒计时界面的逻辑代码
这里面同样留了一个接口用于倒计时结束后的操作回调
public class CountDownView extends Chronometer {
private long mTime;
private long mNextTime;
private OnTimeCompleteListener mListener;
private SimpleDateFormat mTimeFormat;
public CountDownView(Context context) {
super(context);
}
public CountDownView(Context context, AttributeSet attrs) {
super(context, attrs);
mTimeFormat = new SimpleDateFormat("hh:mm:ss");
this.setOnChronometerTickListener(listener);
}
//重新启动计时
public void reStart(long _time_s)
{
if (_time_s == -1)
{
mNextTime = mTime;
}
else
{
mTime = mNextTime = _time_s;
}
this.start();
}
public void reStart()
{
reStart(-1);
}
//不建议方法名用onResume()或onPause(),容易和activity生命周期混淆
//继续计时
public void onResume()
{
this.start();
}
//暂停计时
public void onPause()
{
this.stop();
}
/**
* 设置时间格式
*
* @param pattern 计时格式
*/
public void setTimeFormat(String pattern)
{
mTimeFormat = new SimpleDateFormat(pattern);
}
public void setOnTimeCompleteListener(OnTimeCompleteListener l)
{
mListener = l;
}
OnChronometerTickListener listener = new OnChronometerTickListener()
{
@Override
public void onChronometerTick(Chronometer chronometer)
{
if (mNextTime <= 0)
{
if (mNextTime == 0)
{
CountDownView.this.stop();
if (null != mListener)
mListener.onTimeComplete();
}
mNextTime = 0;
updateTimeText();
return;
}
mNextTime--;
updateTimeText();
}
};
//初始化时间(秒)
public void initTime(long _time_s)
{
mTime = mNextTime = _time_s;
updateTimeText();
}
//初始化时间(分秒)
public void initTime(long _time_h,long _time_m,long _time_s) {
initTime(_time_h*3600+_time_m * 60 + _time_s);
}
private void updateTimeText()
{
this.setText(FormatMiss(mNextTime));
}
// 将秒转化成小时分钟秒
public String FormatMiss(long miss){
String hh=miss/3600>9?miss/3600+"":"0"+miss/3600;
String mm=(miss % 3600)/60>9?(miss % 3600)/60+"":"0"+(miss % 3600)/60;
String ss=(miss % 3600) % 60>9?(miss % 3600) % 60+"":"0"+(miss % 3600) % 60;
return hh+":"+mm+":"+ss;
}
public interface OnTimeCompleteListener
{
void onTimeComplete();
}
}
四、遇到的问题
我在做的时候遇到了一个很有意思的问题,这个倒计时控件我在初始化的的时候我是
customPickNumberView=new CustomPickNumberView(this);
而不是customPickNumberView=findViewById(R.id.customCountDownView);这么写的,结果一直报空指针
不知道有哪个同学可以解释下原因 ?
这里面介绍部分就是这样子的,代码我传到了GitHub ,有不足的地方欢迎指出,也可以Star 下哦,我以后也会多写一写开源项目。