Handler

YH Lin
6 min readJan 21, 2021

--

應用程式在執行時,主要與使用者互動,在執行互動的過程中,是以一個UI thread /main thread 來處理,假設在應用程式須執行耗時的工作,例如網路連線,如果以main thread 來執行,應用程式的元件就無法與使用者互動.就是常見的ANR.因此在Android 4.0 後就規定網路通訊等程式,必須用thread 來處理

https://developer.android.com/guide/components/processes-and-threads#java

然而,我們不能直接從非UI thread 的thread直接更改UI,因此有幾個方法可以來更改

下面我們來練習用Handler 來處理UI吧.主要來寫一隻跟碼表有關的程式

在layout 的部份,先來配置主畫面

在listview 對應的items.xml 配置如下

接下來進行MainActivity的部分

先寫button 控制的部分,利用isStart = !isStart來設計按下按鈕後,會變更第一顆按鈕為Stop或Start,第二顆為Add Lap 或Reset

public void doStartOrStop(View view) {
if (!isStart)
{
btn.setText("Stop");
btn1.setText("Add Lap");
intHsec = 0;
}else {
btn.setText("Start");
btn1.setText("Reset");
}
isStart = !isStart;
}

主要的核心部分為 Timer,利用Timer 來設計每隔1/100 計算一次,同時發送給handler 處理

timer = new Timer();
timer.schedule(new MyTask(),0,10);

Handler中的handleMessage 收到指令後便來調整UI

private class UIHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
tv.setText(toClock(intHsec));
}
}

intHSec 為1/100 的單位,取餘數即為毫秒(th),

intHSec /100 即為秒數,

intHSec /3600 則為小時(hh),

(intHSec-hh*3600)/60 則為分數

private String toClock(int intHsec) {
int th = intHsec %100;
int tsec = intHsec/100;
int hh = tsec/(60*60);
int mm = (tsec - hh*60*60)/60;
int ss = tsec %60 ;
return (hh<10?"0"+hh:hh)+":"+ (mm<10?"0"+mm:mm)+":"+(ss<10?"0"+ss:ss)+":"+(th<10?0+"th":th);
}

目前完成的部分如下

接下來要來撰寫ListView 呈現的部分了,ListView 要與item.xml界接,需要用一個adapter,習慣上我會繼承BaseAdapter

開啟一個新的java 檔,命名為TimerAdapter ,撰寫建構函式與set方法

protected TimerAdapter(List<String> data)
{
this.data = data;
}
void setData(List<String> data)
{
this.data = data;
}

實作BaseAdapter的方法

@Override
public int getCount() {
return data.size();
}

@Override
public Object getItem(int position) {
return data.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.items,parent,false);
TextView tv_record = v.findViewById(R.id.tv_record);
tv_record.setText(data.get(position));
return v;
}

一個完整的TimerAdapter 會如下

回到MainActivity 中來串接listview,在btn2 的onClick 事件中,撰寫

public void addLap(View view) {
if (isStart)
{
doAddLap();
}else{
doReset();
}
}

然後實作doAddLap() 跟doReset

public void doAddLap()
{
record.add(toClock(intHsec));
adapter.setData(record);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
adapter.notifyDataSetInvalidated();
}

public void doReset()
{
intHsec = 0;
record = new ArrayList<>();
adapter = new TimerAdapter(record);
adapter.notifyDataSetChanged();
adapter.notifyDataSetInvalidated();
listView.setAdapter(adapter);
}

完整的MainActivity 如下

完整的專案如下

--

--

No responses yet