有点没想到的是,这样的一个小小的程序弄了很久才做完。
这个程序看起来很简单的,如下图:
一个进度条在不断地增加,累加到超过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>
之后,布局也没有什么特别的,思想如下图:
在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进程中设置标签文本的,则会弹出一下的错误提示:
因此,必须利用安卓在线程中的消息传递,让处于安卓的Original Thread来设置标签文本的值,对进度条进行处理。就这个东西搞了我很久。
同时,这里的定时器的设置使用了Java中的匿名内部类,具体见《【Java】定时器、线程与匿名内部类》(点击打开链接)。这里不赘述了。
最后随便说一句,这里的标签文本可以设置其TextSize="24sp",默认的字体太小,不太好看。