Android中用ListView創建應用商店時的幾個優化
package com.example.ex01_1;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.database.CursorJoiner.Result;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity<E> extends Activity {
private ArrayList<String> dataList = new ArrayList<String>();
private ArrayList<String> fileurl = new ArrayList<String>();
private MyAdapater myadapater;
// String PATH = "http://192.168.56.1:8080/server/";
String PATH = "http://192.168.56.1:8080/server/";
private ListView listView;
/**問題:圖片下載完成,上下滑動ListView,圖片重新下載
原因:上下滑動,觸發getView()方法,新建異步任務,重新下載
解決:創建一個容器,保存已經下載的Bitmap對象
*/
// 為了防止重復下載,把以及下載的圖片放在imgmap這個容器中
HashMap<String, Bitmap> imgmap = new HashMap<String, Bitmap>();
/**
* 問題:下載過程中,上下滑動ListView,等待下載完成,圖片多次下載
原因:上下滑動ListView,觸發getView()方法,判斷圖片還未下載完成,創
建新的異步任務
解決:創建一個容器保存已經開始執行的AsyncTask對象
* */
// 創建一個容器用於保存已經開始的異步任務
HashMap<String, ImgAsy> asymap = new HashMap<String, ImgAsy>();
/**問題是:某一行下載完成後,上下滑動ListView,
* 其他未點擊下載的行會出現前面個行的狀態。
引用容器保存文件下載狀態,
我們通過下載狀態的區別來更新控件的狀態,得以解決這個問題。*/
// 創建一個容器用來保存當前行的文件的下載狀態,用來解決文件下載時,復用View帶來的Bug
HashMap<Integer, Boolean> stateMap = new HashMap<Integer, Boolean>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 首先找到activity_mian.xml裡面的listView1
listView = (ListView) findViewById(R.id.listView1);
myadapater = new MyAdapater();
listView.setAdapter(myadapater);
// 添加圖片
for (int i = 1; i < 16; i++) {
dataList.add(PATH + "ic" + i + ".png");
}
// 添加文件
for (int i = 1; i < 16; i++) {
fileurl.add(PATH + "ic" + i + ".rar");
}
}
// 得到一段保存地址
private String getSavePath(String fileurl) {
String sdcard = Environment.getExternalStorageDirectory() + "";
// 截取一段地址
int lastIndexOf = fileurl.lastIndexOf("/");
String savename = fileurl.substring(lastIndexOf);
String savePath = sdcard + savename;
return savePath;
}
// 創建一個適配器的類
class MyAdapater extends BaseAdapter {
private ImgAsy asy;
// 獲得行數
@Override
public int getCount() {
return 15;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
// 得到每一行控件的樣式和內容,則對每一行的控件的內容和樣式的修改都在這裡進行
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
ViewHolder holder = null;
// 繼續優化,優化1復用布局,防止重復下載,浪費不必要的流量
if (convertView == null) // convertView:消失的行布局
{
// 找到XML文件轉換器
LayoutInflater inflater = getLayoutInflater();
// 把每一行的樣式轉換成VIEW對象
view = inflater.inflate(R.layout.listview, null);
// 優化2,減少控件的查找.封裝一個類包含所有的控件
holder = new ViewHolder();
holder.iv = (ImageView) view.findViewById(R.id.imageView1);
holder.btn = (Button) view.findViewById(R.id.button1);
holder.tv = (TextView) view.findViewById(R.id.textView1); holder.pb = (ProgressBar)view.findViewById(R.id.progressBar1);
view.setTag(holder);
} else
{
view = convertView;
holder = (ViewHolder) view.getTag();// 優化2
}
/** 問題:ListView某一行下載完成之後,上下滑動ListView,還未下載的行數顯示圖片
原因:復用造成
解決:還未下載完成的行數,設置默認圖片顯示*/
// 把沒有參與下載的行的圖片設置為默認圖片,解決未下載的圖片提前顯示出來的問題
holder.iv.setImageResource(R.drawable.ic_launcher);
// 這兩行代碼是為了判斷當前行圖片是否下載完成和異步任務是否開啟而寫的
Bitmap bitmap = imgmap.get(dataList.get(position));
ImgAsy asy = asymap.get(dataList.get(position));
if (bitmap == null)
{
// 圖片尚未下載,判斷當前行的異步任務是否開始
if (asy == null)
{ // 這是一個構造方法
asy = new ImgAsy(holder.iv, position);
asy.execute(dataList.get(position));
asymap.put(dataList.get(position), asy);
}
// 解決上下滑動過程中,ImageView對象創建了,卻沒有創建新的異步任務的問題。
asy.changView(holder.iv);
} else {
holder.iv.setImageBitmap(bitmap);
}
//根據下載狀態設置各控件的顯示狀態
Boolean state=stateMap.get(position);
if (state==null)
{
holder.btn.setEnabled(true);
holder.pb.setProgress(0);
holder.tv.setText("未下載");
holder.btn.setText("下載");
}else
{ if (state==true)
{
holder.btn.setEnabled(false);
holder.pb.setProgress(100);
holder.tv.setText("下載完成");
holder.btn.setText("下載完成");
}else
{
holder.btn.setEnabled(false);
holder.tv.setText("下載中");
holder.btn.setText("下載中");
}
}
final ProgressBar pb = holder.pb;
final TextView tv = holder.tv;
final Button btn = holder.btn;
//用匿名內部類的方法寫按鈕的監聽事件
holder.btn.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
//文件下載異步事件的創建與開啟
FileAsy fileAsy = new FileAsy(tv, pb, btn, position);
fileAsy.execute(fileurl.get(position));//帶上了行號。
}
});
return view;
}
}
// 下載文件的異步任務
class FileAsy extends AsyncTask<String, Integer, String> {
TextView tv;
ProgressBar pb;
Button btn;// 把Button 傳入到下載文件的異步任務裡,為了能夠控制Button的狀態
private int position;
public FileAsy(TextView tv, ProgressBar pb, Button btn, int position) {
super();
this.tv = tv;
this.pb = pb;
this.btn = btn;
this.position = position;
}
// 保存路徑
String savepath = Environment.getExternalStorageDirectory() + "/wenjian.apk";
private String result;
// 執行在doInBackgound之前
@Override
protected void onPreExecute()
{
btn.setEnabled(false);
btn.setText("下載");
stateMap.put(position, false);//文件每一行的下載狀態
super.onPreExecute();
}
@SuppressWarnings("resource")
@Override
protected String doInBackground(String... params)
{
try {
URL url = new URL(params[0]);
// 打開鏈接
URLConnection connection = url.openConnection();
int length = connection.getContentLength();
InputStream is = connection.getInputStream();
// File file = new File(savepath);
String savePath2 = getSavePath(params[0]);
FileOutputStream fos = new FileOutputStream(savePath2);
byte[] buffer = new byte[1024];
int len = 0;
int dllength = 0;// 要寫在這裡
while (-1 != (len = is.read(buffer)))// 先讀取數據
{
fos.write(buffer, 0, len);// 在寫出數據
// 推送進度
dllength += len;
publishProgress(dllength * 100 / length);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
result = "下載失敗";
e.printStackTrace();
}
result = "下載成功";
return result;
}
protected void onProgressUpdate(Integer... values) {
//該判斷語句為了解決BUG:文件在下載過程中,緩慢上下滑動LiseView,沒有點下載的行出現了對應的下載的狀態。
//只要Button當前行去
if (isVisiablePositon(listView, position)) {
pb.setProgress(values[0]);
tv.setText("已下載" + values[0] + "%");
}
super.onProgressUpdate(values);
}
// 下載完成後,根據位圖去更新圖片
protected void onPostExecute(String result) {
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
if (isVisiablePositon(listView, position)) {
btn.setEnabled(true);
btn.setText("下載完成");
}
stateMap.put(position, true);
super.onPostExecute(result);
}
}
// 下載圖片
class ImgAsy extends AsyncTask<String, Void, Bitmap> {
private Bitmap bitmap;
private ImageView iv;
private int position;// 加入一個行號的參數
public ImgAsy(ImageView iv, int position) {
super();
this.iv = iv;
this.position = position;
}
/**
* 問題:下載過程中,上下滑動ListView,等待下載完成,有些行的圖片不顯示
原因:上下滑動ListView,創建新的ImageView對象,沒有創建新的 AsyncTask
解決: 1.寫一個方法重新給AsyncTask中的ImageView賦值
* 通知AsyncTask更新ImageView對象
* 2.在getView()中調用
*/
public void changView(ImageView iv) {
this.iv = iv;
}
@Override
protected Bitmap doInBackground(String... params) {
try {
URL url = new URL(params[0]);
InputStream is = url.openStream();// 開流
bitmap = BitmapFactory.decodeStream(is);// 下載位圖
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bitmap;// 相當於handlermessage
}
// 下載完成後,根據位圖去更新圖片(寫在內部類裡)
protected void onPostExecute(Bitmap bitmap) {
isVisiablePositon(listView, position);
if (isVisiablePositon(listView, position)) {
iv.setImageBitmap(bitmap);
}
imgmap.put(dataList.get(position), bitmap);// 保存
super.onPostExecute(bitmap);
}
}
// 寫一個方法判斷當前行是否可見。讓數據更新只在當前行進行。返回值是Boolean類型
public Boolean isVisiablePositon(ListView listView, int position) {
// 獲得可見行第一行行號
int fp = listView.getFirstVisiblePosition();
// 獲得可見行最後一行行號
int lp = listView.getLastVisiblePosition();
// 在構造方法裡面去添加position
// 判斷當前行是否在可見范圍
if (position >= fp && position <= lp) {// 讓可見行去更新控件
// iv.setImageBitmap(bitmap);
return true;//返回真,結束方法
}
return false;
}
// 優化2,減少控件的查找.封裝一個類包含所有的控件
class ViewHolder {
ImageView iv;
Button btn;
TextView tv;
ProgressBar pb;
}
}
1
<br>