现在我们通过一个更新ProgressBar的小案例来学习下Android的UI更新和消息机制。
如下图,点击“Start”按钮,进度条开始变化。
第一步:如下代码:
btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (int i = 0; i <= 100; i = i + 10) { bar.setProgress(i); try { Thread.sleep(1000);//每隔1S更新Bar } catch (InterruptedException e) { e.printStackTrace(); } } } });
我们发现,
并没有进度条递变的效果,而是start按钮一直被占用,其他的任何操作都被阻塞了,还有可能报ANR。
因为只有一个主线程,而更新进度条是个耗时操作,程序就会暂时阻塞,所有我们可以另起一个线程来处理UI更新问题。
btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread() { public void run() { for (int i = 0; i <= 100; i = i + 10) { bar.setProgress(i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }}}; }.start(); }});
这样的改变,或许能得到我们的需求,可以更新ProgressBar,但是如果你连续点击Start按钮,则会发现,进度条的变化凌乱了。因为这个操作是并发的,而界面只有一个,多次点击Button,则会多次创建多个线程,每个线程都会对界面进行改变,这样的操作是不安全的。所以Android规定,所有的UI更新操作必须放在UI线程中进行,即主线程中去执行。那么我们该怎么做才能让主线程同步操作来更新UI。
正确的做法:
Handler + Runnable
1)创建Runnable对象,UI更新操作主要在run()方法中。
2)创建Handler对象,调用handler.post(r),把r对象加入到线程队列中,然后线程队列就开始执行run()
3)run()中调用UI更新代码,如果是循环操作,可以用handler.postDelayed(r,1000).表示隔1秒后再把线程对象r加入到线程队列中,然后再去调用run().
注意:所有操作都在一个线程中,因为没有调用Thread的start()。
一般来说在工作线程中执行耗时任务,当任务完成时,会返回UI线程,一般是更新UI。这时有两种方法可以达到目的。 一种是handler.sendMessage。发一个消息,再根据消息,执行相关任务代码。 另一种是handler.post(r)。r是要执行的任务代码。意思就是说r的代码实际是在UI线程执行的。可以写更新UI的代码。(工作线程是不能更新UI的)
具体操作请看如下代码:
方法一:handler.post(r);
public class MainActivity2 extends Activity { ProgressBar bar = null; private Button btnStart = null; private Button btnEnd = null; Handler handler = new Handler(); //创建Runnable对象 Runnable r = new Runnable() { int i = 0;// 用来更新bar @Override public void run() { System.out.println(Thread.currentThread().getId() + "-----run--->" + Thread.currentThread().getName()); i = i + 10; bar.setProgress(i); handler.postDelayed(r, 1000); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bar = (ProgressBar) findViewById(R.id.progressBar1); btnStart = (Button) findViewById(R.id.start); btnEnd = (Button) findViewById(R.id.stop); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { System.out.println(Thread.currentThread().getId() + "-----Main--->" + Thread.currentThread().getName()); // 把r加入到线程队列,然后线程队列里就开始执行runnable对象中的run() handler.post(r); }; }); btnEnd.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { handler.removeCallbacks(r); } }); } }
方法2:handler.sendMessage();
Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case 0x333: bar.setProgress(msg.arg1); postDelayed(r, 1000); break; } }; }; Runnable r = new Runnable() { int i = 10; @Override public void run() { System.out.println(Thread.currentThread().getId() + "<>" + Thread.currentThread().getName()); Message msg = new Message(); msg.what = 0x333; i = i + 10; msg.arg1 = i; handler.sendMessage(msg); }; }; start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handler.post(r); } });
相关推荐
Android下ProgressBar和旗下两个子控件的使用,具体可参见博客http://www.cnblogs.com/plokmju/p/android_ProgressBar.html
android 实现progressbar的简单自定义动画
AndroidProgressBar_Android ProgressBar进度条的几乎全部的用法源码集.rar
android progressbar 垂直布局
Android 控件之ProgressBar进度条源码
android自定义ProgressBar(仿淘宝)的加载效果
集成了多种风给的Android 自定义progressbar控件
Android应用源码之ProgressBar 几乎全部的用法
android自定义progressBar颜色,四个角圆度
android 自定义ProgressBar 字体适配 实现了 进度文字跟随进度效果
Android自定义Progressbar 圆形进度条CircleBarView 教程博客:Android自定义View——从零开始实现圆形进度条 效果展示: 水波浪进度框WaveProgressView 教程博客:Android自定义View——从零开始实现...
自定义android矩形progressbar
android progressbar背景
自定义ProgressBar,重写View
android 自定义progressbar,可以实现四种区间的进度显示
android ProgressBar 几乎全部的用法,适合学习练习,相当不错,相互学习
花样Android_ProgressBar_史上最强大讲解.doc
android AsyncTask中更新progressBar 。可用于http请求、下载文件等
NULL 博文链接:https://sunzone.iteye.com/blog/1998093