Android控件系列之ProgressBar&在Android中利用Handler处理多线程
学习目的:
1、掌握在Android中如何建立ProgressBar
2、掌握ProgressBar的常用属性
3、掌握在另一个线程中控制UI线程中的控件
介绍:
进度条是一种代表程序运行状态的动态控件,一般它不受用户控制,它的目的是为了告诉用户:请您稍等!
进度条分为2种:
第一种是不能告诉用户具体进度,但能一直处于滚动状态,适合未知结束时间的场合。
第二种时能告诉用户具体进度的,到达100%时,需要处理完所有后台任务。
您需要明白一下几点:
1、进度条只是反应进度的控件,真正的后台处理应该在另一个线程中进行。
2、反应实际进度的进度条的设计需要尽可能准确和平稳。
3、当进度条到达100%时,任务必须完成,否则用户会认为他们受到了欺骗。
样例实现了2种进度条,您会掌握如何在另一个线程中控制它,通常下载大量数据又不希望界面假死的情况下,您务必这么做:
第一个不停旋转的圆形进度条只适合不清楚结束时间,或者您能保证立刻会完成的操作。
第二个进度条拥有主进度和副进度,您可以根据实际需要分别为它们赋值,右边的TextView为了告诉用户实际进度的值,否则99%和100%将很难察觉出区别。
下方的按钮用来给您验证我们的确是可以一边异步下载数据一边使用界面上的其他控件的。
XML布局:
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 >
7 <ProgressBar
8 android:layout_width="wrap_content"
9 android:layout_height="wrap_content"
10 ></ProgressBar>
11
12 <LinearLayout
13 android:orientation="horizontal"
14 android:layout_width="fill_parent"
15 android:layout_height="wrap_content"
16 >
17 <ProgressBar
18 style="?android:attr/progressBarStyleHorizontal"
19 android:id = "@+id/pb"
20 android:layout_width="170px"
21 android:layout_height="wrap_content"
22 ></ProgressBar>
23 <TextView
24 android:id="@+id/tvPersent"
25 android:paddingLeft="10px"
26 android:layout_width="wrap_content"
27 android:layout_height="wrap_content"
28 >
29 </TextView>
30 </LinearLayout>
31 <Button
32 android:text="点我测试界面是否假死"
33 android:layout_width="wrap_content"
34 android:layout_height="wrap_content"
35 ></Button>
36 </LinearLayout>
后台代码:
1 //进度条
2 ProgressBar pb = null;
3 //显示具体进度值的文本控件
4 TextView tvPersent = null;
5 /** Called when the activity is first created. */
6 @Override
7 public void onCreate(Bundle savedInstanceState) {
8 super.onCreate(savedInstanceState);
9 setContentView(R.layout.main);
10
11 pb = (ProgressBar)this.findViewById(R.id.pb);
12 tvPersent = (TextView)this.findViewById(R.id.tvPersent);
13
14 //申明一个线程,它将循环的更新某个值,这个值最终将被通过作为Message的一个参数发送给主线程的Handler
15 Thread thread = new Thread(myRun);
16 //开始异步执行
17 thread.start();
18 }
19
20 //运行在主线程的Handler,它将监听所有的消息(Message)
21 Handler handler = new Handler(new Callback() {
22
23 @Override
24 public boolean handleMessage(Message arg0) {
25 //接受到另一个线程的Message,拿到它的参数,这个参数代表了进度
26 // TODO Auto-generated method stub
27 pb.setProgress(arg0.arg1);
28 tvPersent.setText(arg0.arg1 + "%");
29 pb.setSecondaryProgress(arg0.arg1 + 10);
30 return false;
31 }
32 });
33
34 //一个异步线程
35 Runnable myRun = new Runnable() {
36 int progressValue = 0;
37 @Override
38 public void run() {
39 while(true){
40 // TODO Auto-generated method stub
41 //拿到主线程Handler的Message
42 Message msg = handler.obtainMessage();
43 //将进度值作为消息的参数包装进去,进度自加1
44 msg.arg1 = progressValue ++;
45 //将消息发送给主线程的Handler
46 handler.sendMessage(msg);
47 //这个例子是反复循环,实际项目中可能没必要
48 if(progressValue>100){
49 progressValue = 0;
50 }
51 try{
52 //为了让您看到进度滚动效果,放慢进度上升的速度
53 Thread.sleep(300);
54 }
55 catch(InterruptedException e){
56 e.printStackTrace();
57 }
58 }
59 }
60 };
Handler
Android的控件是非线程安全的,在其他线程中操作主线程的控件将可能发生意外的情况,因此设计者希望对控件的操作依然交给主线程去完成,但在其他线程中,我们希望有一种机制去通知主线程去改变控件,于是Handler诞生了。这有点类似C#中的委托,但因为Java中的函数本身不能作为其他函数的参数,我们只能通过发送某个消息来告知主线程中的这个函数:你该如何操作某个控件了!
Handler对象也是运行一个线程上的,默认的空构造函数的Handler将会运行在创建它的线程上。也就是说,样例代码中的Handler是运行在主线程上的一个对象,它拥有一个接受消息的回调函数,这里可以处理一些逻辑,因为它是运行在主线程上的,所以它可以安全的操作控件。而在另一个线程中,我们可以截获Handler的Message,它就向一个邮递员,我们把要送达的信塞给他,然后告诉他:你快送过去吧!而信里则是你希望收信者做的事,邮递员根本没必要知道具体的任务。最终邮递员将信送达,主线程上的收信者拆开信封,看到“50”,他立刻明白:发信者要我把进度改成50%。
如果文字太过枯燥,看一下生动的图片吧:
总结:
本文详细介绍了在Android中使用ProgressBar的方法,并详细描述了在其他线程中如何操作UI线程的控件,这是Android开发必须掌握的知识,最后又分析了Handler的内部运行原理。