本篇文章通过三种方式来实现UI控件的更新,Handler异步更新UI在安卓开发中最常用也非常实在。这篇文章注重实现思路,所以我就不在界面方面进行美化了,都是最原始的控件。有需要的可以收藏下,。虽说搜索引擎上关于Handler消息机制的文章已经数不尽数了,但是我写这篇文章也是希望在开发中能帮助自己记忆起Handler的用法。
学会使用Handler来更新UI,由于在主线程中直接更新UI会阻塞线程,造成假死现象,所以我们通常采用Handler消息机制在UI线程中来更新UI控件。至于Handler消息机制,在这里简单介绍一下。本来还打算写一种的,这里就不详细说了,通过在子线程使用Bundle封装属性到Message数据中,其次在Handler中解封装得到Message数据再显示到控件中。其原理与方法三无太大差别。
Handler消息机制原理简介:通过Handler对象向消息队列中Message Queue中发送消息Message,通过Looper对象来管理Queue中的Message。具体的大家可以查看Handler的源码。
好了,看到我们的效果图,三种方式实现的最终效果一致。
项目UI界面实现:3个Button,1个EditText,1个TextView。
项目实现原理:Handler机制实现UI更新。
项目逻辑实现:通过点击按钮获取输入框的时间并显示在一个TextView上,然后通过点击开始计时按钮开始倒计时,可以通过停止计时按钮停止计时。
实现方式一:Handler+Timer+TimerTask
通过该方式也是比较实用的,顾名思义,TimerTask计时器任务。由于Timer和TimerTask是同时出现的,TimerTask实现了Runnable接口,并且要求实现run方法。
首先,我们先编写我们的布局文件activity_main.xml,三种实现方式统一使用了该布局。
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/inputTime" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" android:hint="@null"/> <Button android:id="@+id/ensureTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击" /> <TextView android:id="@+id/showTime" android:layout_width="fill_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/startTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始计时" /> <Button android:id="@+id/stopTime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止计时" /> </LinearLayout>
接下来就是我们的Activity实现步骤了。三种方式实现代码如下:
MainActivity.java
package com.mero.countTime; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener{ private EditText inputTime; private TextView showTime; private Button ensureTime,startTime,stopTime; private Timer timer = null; private TimerTask task = null; private int i;//显示的倒计时数字 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView();//实例化控件 } /**实例化控件方法*/ private void initView() { inputTime = (EditText) findViewById(R.id.inputTime); showTime = (TextView) findViewById(R.id.showTime); ensureTime = (Button) findViewById(R.id.ensureTime); startTime = (Button) findViewById(R.id.startTime); stopTime = (Button) findViewById(R.id.stopTime); /**注册监听事件*/ ensureTime.setOnClickListener(this); startTime.setOnClickListener(this); stopTime.setOnClickListener(this); }; @Override public void onClick(View v) { switch (v.getId()) { /**当选择点击按钮的监听事件*/ case R.id.ensureTime: showTime.setText(inputTime.getText().toString()); i=Integer.parseInt(inputTime.getText().toString()); break; /**当选择开始计时按钮的监听事件*/ case R.id.startTime: startTime(); break; case R.id.stopTime: stopTime(); break; } } /**当选择停止计时按钮的监听事件*/ private Handler handler=new Handler(){ /**重写handleMessage方法*/ @Override public void handleMessage(Message msg) { showTime.setText(msg.arg1+""); startTime();//执行计时方法 } }; /**开始计时方法*/ private void startTime(){ timer = new Timer(); task = new TimerTask() { @Override public void run() { i--; Message message = handler.obtainMessage();//获取Message对象 message.arg1 = i;//设置Message对象附带的参数 handler.sendMessage(message);//向主线程发送消息 } }; timer.schedule(task, 1000);//执行计时器事件 }; /**停止计时方法*/ private void stopTime(){ timer.cancel();//注销计时器事件 }; }
MainActivity.java
package com.mero.countTime; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener{ private EditText inputTime;//声明输入框 private TextView showTime;//声明用于显示当前计时的时间 private Button ensureTime,startTime,stopTime;//声明计时按钮,停止计时按钮和点击按钮 private int i;//显示的倒计时数字 private Runnable update; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView();//实例化控件 } /**实例化控件方法*/ private void initView() { inputTime = (EditText) findViewById(R.id.inputTime); showTime = (TextView) findViewById(R.id.showTime); ensureTime = (Button) findViewById(R.id.ensureTime); startTime = (Button) findViewById(R.id.startTime); stopTime = (Button) findViewById(R.id.stopTime); /**注册监听事件*/ ensureTime.setOnClickListener(this); startTime.setOnClickListener(this); stopTime.setOnClickListener(this); }; @Override public void onClick(View v) { switch (v.getId()) { case R.id.ensureTime: showTime.setText(inputTime.getText().toString());//获取输入框上的时间并设置到显示文本控件上 i=Integer.parseInt(inputTime.getText().toString()); break; case R.id.startTime: startTime();//开始计时 handler.post(update); break; case R.id.stopTime: stopTime();//停止计时 break; } } final Handler handler=new Handler(); /**开始计时方法*/ private void startTime(){ update=new Runnable(){ @Override public void run() { i--; showTime.setText(i+""); handler.postDelayed(update, 1000);//每隔1s将线程提交到线程队列中 } }; } /**停止计时方法*/ private void stopTime(){ handler.removeCallbacks(update);//移除Runnable对象 }; }
MainActivity.java
package com.mero.countTime; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener{ private EditText inputTime; private TextView showTime; private Button ensureTime,startTime,stopTime; private int i;//显示的倒计时数字 private boolean flag; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView();//实例化控件 } private void initView() { inputTime = (EditText) findViewById(R.id.inputTime); showTime = (TextView) findViewById(R.id.showTime); ensureTime = (Button) findViewById(R.id.ensureTime); startTime = (Button) findViewById(R.id.startTime); stopTime = (Button) findViewById(R.id.stopTime); /**注册监听事件*/ ensureTime.setOnClickListener(this); startTime.setOnClickListener(this); stopTime.setOnClickListener(this); }; @Override public void onClick(View v) { switch (v.getId()) { /**点击按钮事件监听*/ case R.id.ensureTime: showTime.setText(inputTime.getText().toString()); i=Integer.parseInt(inputTime.getText().toString()); break; /**开始按钮事件监听*/ case R.id.startTime: flag=true; startTime(); break; /**停止按钮事件监听*/ case R.id.stopTime: stopTime(); break; } } final Handler handler=new Handler(){ public void handleMessage(Message msg) { int p=msg.what; showTime.setText(p+""); }; }; /**开始计时方法*/ private void startTime() { /**开启一个新线程*/ new Thread(){ public void run() { /**每睡眠1秒后发送Message给Handler处理*/ for(int j=i;j>=0;j--){ if(flag==true){ try { Thread.sleep(1000); Message msg=new Message(); msg.what=j;//设置Message附带的参数 handler.sendMessage(msg);//发送Message对象给Handler i=j;//将当前的时间传递给全局时间变量 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }.start(); } /**停止计时方法,通过设置boolean标志为false来停止*/ @SuppressLint("NewApi") private void stopTime(){ flag=false; }; }
方法一:简单实用,尤其定时刷新控件,效果非常good,使用简单。注意Timer和TimerTask必须同时使用。使用Timer的schedule(task,delayed)方法提交TimerTask线程消息。由Handler处理线程消息。通过Timer.cancel(task)方式进行移除线程任务。
方法二:同方式一,简单实用,原理实质一致。通过实例化Runnable对象来构造实现run创建新线程,在新线程中不断将线程加入Looper池中进行处理。在主线程中通过post提交线程进行处理。通过handler.removeCallbacks(runnable)方式移除线程任务。
方法三:很经典实用的一种方式。通过for循环加上线程睡眠不断创建新消息。缺点是不易于控制,本文通过标志进行控制。
好了,本篇文章就到此结束了。如果还有什么问题的话,可以在下面留言。大家共同讨论共同进步。谢谢阅读 !