同步加載網絡圖片阻塞 UI,需異步下載並顯示進度對話框,完成後顯示圖片。
public class ImageLoadActivity extends AppCompatActivity {
private ImageView imageView;
private ProgressDialog progressDialog;
private static final String IMAGE_URL = "https://example.com/large-image.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_load);
imageView = findViewById(R.id.imageView);
// 初始化 ProgressDialog
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("加載中");
progressDialog.setMessage("正在下載圖片...");
progressDialog.setCancelable(false);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); // 或 STYLE_HORIZONTAL 需進度更新
// 啟動異步任務
new DownloadImageTask().execute(IMAGE_URL);
}
private class DownloadImageTask extends AsyncTask {
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show(); // 顯示對話框
}
@Override
protected Bitmap doInBackground(String... urls) {
String url = urls[0];
try {
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(10000);
conn.connect();
int contentLength = conn.getContentLength();
InputStream input = conn.getInputStream();
// 可選:下載到 ByteArrayOutputStream 並按塊發布進度 (STYLE_HORIZONTAL)
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int len, total = 0;
while ((len = input.read(buffer)) != -1) {
output.write(buffer, 0, len);
total += len;
if (contentLength > 0) {
int progress = (int) (total * 100 / contentLength);
publishProgress(progress); // 觸發 onProgressUpdate
}
}
input.close();
byte[] data = output.toByteArray();
return BitmapFactory.decodeByteArray(data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressDialog.setProgress(values[0]); // 更新水平進度條
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
progressDialog.dismiss(); // 隱藏對話框
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
Toast.makeText(ImageLoadActivity.this, "圖片加載失敗", Toast.LENGTH_SHORT).show();
imageView.setImageResource(R.drawable.ic_error);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
}
| 方法 | 執行線程 | 用途 |
|---|---|---|
onPreExecute() | UI 線程 | 任務啟動前:顯示 ProgressDialog、初始化 UI |
doInBackground() | 後台線程 | 耗時操作:網絡下載、解碼、發布進度 |
onProgressUpdate() | UI 線程 | 更新進度條、提示文本 |
onPostExecute() | UI 線程 | 任務完成:隱藏對話框、設置圖片、錯誤處理 |
AsyncTask 在 API 30 棄用,新項目請使用:
viewModelScope.launch { ... }Observable.fromCallable(...).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())CoroutineLiveData。
Glide.with(this)
.load(url)
.listener(new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
progressDialog.dismiss(); return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
progressDialog.dismiss(); return false;
}
})
.into(imageView);
progressDialog.show();
教學/維舊項目可用 AsyncTask + ProgressDialog 實現基本流程。**生產新項目請直接接入 Glide/Coil 等庫**,獲得緩存、生命周期感知、錯誤重試、動畫轉場等企業級能力,避免重複造輪子。