【Android】进度条与线程之间的消息处理

时间:2022-01-05 23:32:52

有点没想到的是,这样的一个小小的程序弄了很久才做完。

这个程序看起来很简单的,如下图:

【Android】进度条与线程之间的消息处理

一个进度条在不断地增加,累加到超过100%,隐藏载入进度条,并且文字改变成一个“倒数3秒”继续执行。

数完三秒之后则继续进行进度条的累加。

首先,由于标签文本是动态的,通过Java文件控制,在res\values\string.xml,仅仅需要把程序名称改成“进度条”,没有什么特别的:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">进度条</string>
    <string name="action_settings">Settings</string>

</resources>
之后,布局也没有什么特别的,思想如下图:

【Android】进度条与线程之间的消息处理

在res\layout\activity_main.xml中,修改成如下代码即可:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <ProgressBar
            android:id="@+id/ProgressBar1"
            style="@android:style/Widget.ProgressBar.Large"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
             android:id="@+id/TextView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    <ProgressBar
        android:id="@+id/ProgressBar2"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100" />

</LinearLayout>
其中,这里细长进度条ProgressBar2的style可能比较特别,但安卓是这样要求的没有办法,指定其最大值为100%。进度条1,看起来像是个不断旋转的载入图像,但其实也是进度条的一种,其没有最大值,也不能通过Java文件设置其当前进度,在MainActivity.Java中只能设定其显示与否。

这里为各个组件设置ID,同时使用了嵌套线性布局。

关键是MainActivity.java这个文件弄了我好久,代码如下:

package com.progressbar;

import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {
	private ProgressBar ProgressBar1;
	private ProgressBar ProgressBar2;
	private TextView TextView1;
	//定义一个消息处理器。
	private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//获取各个组件,没有什么特别的。
		ProgressBar1 = (ProgressBar) findViewById(R.id.ProgressBar1);
		ProgressBar2 = (ProgressBar) findViewById(R.id.ProgressBar2);
		TextView1 = (TextView) findViewById(R.id.TextView1);

		//设置一个每隔1000毫秒,也就是1秒就运行一次的定时器
		new Timer().schedule(new TimerTask() {
			int i = 0;//这里代表进度
			int j = 3;//这里代表进度达100%时的倒数

			@Override
			public void run() {
				Message msg = new Message();
				//这里的消息声明必须放在run()方法之中,否则程序,由于旧的消息不消亡会卡死
				//把Message的new方法,放在run()这里,定时器每一次重新执行,则会杀死旧信息,创建新的信息。
				if (i < 100) {
					msg.what = i;
					handler.sendMessage(msg);//将i存放到msg的what类成员中传给消息处理器
					i += Math.random() * 20;//i每次递增20*(0.xxx)的进度
				} else {
					if (j > 0) {
						msg.what = i;
						msg.arg1 = j;
						handler.sendMessage(msg);//将j放到msg的arg1类成员中传递给消息处理器
						j--;
					} else {//倒数完毕,重新开始
						i = 0;
						j = 3;
					}
				}
			}
		}, 0, 1000);

		//不停在接受定时器的消息,根据消息的参数,进行处理
		handler = new Handler(new Handler.Callback() {//这样写,就不弹出什么泄漏的警告了
			@Override
			public boolean handleMessage(Message msg) {
				if (msg.what < 100) {//如果消息的 what参数少于100,则设置进度条
					ProgressBar2.setProgress(msg.what);//设置细长进度条ProgressBar2的进度
					if (msg.what == 0) {//仅仅是在what参数等于0的时候,设置标签文本与进度条,不要每次读取进度都加载。
						TextView1.setText("正在运行中……");
						ProgressBar1.setVisibility(View.VISIBLE);
					}
				} else {
					if (msg.arg1 == 3) {
						ProgressBar1.setVisibility(View.GONE);
						ProgressBar2.setProgress(0);
					}
					TextView1.setText("运行完毕,等待" + msg.arg1 + "秒继续运行下一次的程序……");
				}
				return false;
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}
可能有人不解,为何不直接在定时器中设置文本,还要非常复杂的样子整个消息处理器Handle,又要处理器的泄露问题。

我最初也不像这样整的,全因为定时器是一条新的线程,安卓不允许在别的线程中设置标签文本TextView1的值,如果你不是在Android进程中设置标签文本的,则会弹出一下的错误提示:

【Android】进度条与线程之间的消息处理

因此,必须利用安卓在线程中的消息传递,让处于安卓的Original Thread来设置标签文本的值,对进度条进行处理。就这个东西搞了我很久。

同时,这里的定时器的设置使用了Java中的匿名内部类,具体见《【Java】定时器、线程与匿名内部类》(点击打开链接)。这里不赘述了。

最后随便说一句,这里的标签文本可以设置其TextSize="24sp",默认的字体太小,不太好看。