我有一个带有自定义适配器的ListView。每行都有一个我使用位图呈现的ImageView,但是当我执行下载位图的AsyncTask之后,当前的代码阻塞了UI线程,因为我正在使用get()
。我想更改我的代码并访问onPostExecute()
或类似名称中的imageViews。这样就可以显示行,而无需等待所有精灵加载。
适配器类(在此处触发下载)
public class PokemonAdapter extends ArrayAdapter<PokemonPOJO> implements View.OnClickListener{
private ArrayList<PokemonPOJO> dataSet;
Context mContext;
private int lastPosition = -1;
// View lookup cache
private static class ViewHolder {
TextView txtName;
TextView txtCP;
TextView txtGenderShiny;
ImageView sprite;
Button btnDelete;
}
public PokemonAdapter(ArrayList<PokemonPOJO> data, Context context) {
super(context, R.layout.row_pokemon, data);
this.dataSet = data;
this.mContext=context;
}
@Override
public void onClick(View v) {
int position=(Integer) v.getTag();
Object object= getItem(position);
PokemonPOJO dataModel=(PokemonPOJO)object;
switch (v.getId())
{
case R.id.btn_delete:
FirebaseDatabase.getInstance().getReference("pokemons").child(dataModel.getUid()).removeValue();
Toast.makeText(getContext(), "Pokemon removed!", Toast.LENGTH_SHORT).show();
this.remove(dataModel);
break;
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
PokemonPOJO dataModel = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_pokemon, parent, false);
viewHolder.txtName = (TextView) convertView.findViewById(R.id.text_name);
viewHolder.txtCP = (TextView) convertView.findViewById(R.id.text_cp);
viewHolder.txtGenderShiny = (TextView) convertView.findViewById(R.id.text_gendershiny);
viewHolder.sprite = (ImageView) convertView.findViewById(R.id.img_sprite);
viewHolder.btnDelete = (Button)convertView.findViewById(R.id.btn_delete);
result=convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
lastPosition = position;
viewHolder.txtName.setText(dataModel.getName());
viewHolder.txtCP.setText("CP: " + Integer.toString(dataModel.getCP()));
viewHolder.txtGenderShiny.setText(dataModel.getGender() + (dataModel.isShiny() ? " (Shiny)" : ""));
viewHolder.btnDelete.setOnClickListener(this);
try {
Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
viewHolder.sprite.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
viewHolder.btnDelete.setTag(position);
// Return the completed view to render on screen
return convertView;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap bm = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
bm = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return bm;
}
}
具有ListView的片段
public class MyPokemonFragment extends Fragment {
private FirebaseAuth auth;
private DatabaseReference pokemonDb;
private TextView text_noPokemon;
private ListView listViewPokemon;
private static PokemonAdapter adapter;
private populateListViewTask populateListView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mypokemon,null);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
auth = FirebaseAuth.getInstance();
listViewPokemon = view.findViewById(R.id.list_pokemon);
text_noPokemon= view.findViewById(R.id.text_noPokemon);
Query getUserPokemon = FirebaseDatabase.getInstance().getReference("pokemons").orderByChild("userUid").equalTo(auth.getCurrentUser().getUid());
getUserPokemon.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if(!snapshot.hasChildren()) {
text_noPokemon.setText("You have not added any Pokémon yet.");
}
else {
TreeMap<String, Pokemon> pokemons = new TreeMap<>();
for (DataSnapshot pokemon : snapshot.getChildren()) {
pokemons.put(pokemon.getKey(), pokemon.getValue(Pokemon.class));
}
populateListView = new populateListViewTask();
populateListView.execute(pokemons);
}
}
@Override
public void onCancelled(DatabaseError databaseError) { }
});
}
@Override
public void onDestroy() {
super.onDestroy();
if(populateListView != null && populateListView.getStatus() == AsyncTask.Status.RUNNING)
populateListView.cancel(true);
}
private class populateListViewTask extends AsyncTask<TreeMap<String, Pokemon>, Void, ArrayList<PokemonPOJO>> {
@Override
protected ArrayList<PokemonPOJO> doInBackground(TreeMap<String, Pokemon>... maps) {
ArrayList<PokemonPOJO> pojos = new ArrayList<>();
HttpURLConnection connection = null;
BufferedReader reader = null;
Iterator it = maps[0].entrySet().iterator();
while(it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
Pokemon p = (Pokemon)pair.getValue();
try {
URL url = new URL("https://pokeapi.co/api/v2/pokemon/" + p.getPokedexNr() + "/");
connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream stream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
JSONObject j = new JSONObject(buffer.toString());
String name = j.getString("name");
String spriteUrl = (p.isShiny() ? j.getJSONObject("sprites").getString("front_shiny") : j.getJSONObject("sprites").getString("front_default"));
PokemonPOJO pojo = new PokemonPOJO((String)pair.getKey(), p.getPokedexNr(), name, spriteUrl, p.isShiny(), p.getGender(), p.getCP());
pojos.add(pojo);
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
try {
if (reader != null)
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return pojos;
}
@Override
protected void onPostExecute (ArrayList < PokemonPOJO > pojos) {
adapter = new PokemonAdapter(pojos, getContext());
listViewPokemon.setAdapter(adapter);
}
}
}
口袋妖怪行XML
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/img_sprite"
android:layout_width="96dp"
android:layout_height="96dp"
android:scaleType="fitCenter" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="left|center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/text_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/text_cp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />
<TextView
android:id="@+id/text_gendershiny"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="end"
android:orientation="vertical">
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@color/colorPrimary"
android:text="DELETE"
android:textColor="#ffffff"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
答案 0 :(得分:1)
您遇到性能问题,因为您正在get()
上调用AsyncTask
方法。 get()
方法基本上使主线程等待,直到AsyncTask
中的代码完成执行,然后主线程才继续执行其他指令。至少可以说,为什么Google添加此方法很奇怪。因此,这样做可以修复您的代码。
创建一个新的Java类文件。将文件命名为“ DownloadImageTask”并添加以下代码:
public interface DownloadImageListener {
void onCompletedImageDownload(Bitmap bm);
}
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private static final String TAG = DownloadImageTask.class.getSimpleName();
private DownloadImageListener mListener;
private String imageUrl = "";
public DownloadImageTask(String imageUrl, DownloadImageListener listener){
this.imageUrl = imageUrl;
this.mListener = listener;
}
@Override
protected Bitmap doInBackground(String... urls) {
Bitmap bm = null;
try {
InputStream in = new java.net.URL(imageUrl).openStream();
bm = BitmapFactory.decodeStream(in);
} catch (Exception e) {
e.printStackTrace();
}
return bm;
}
protected void onPostExecute(Bitmap bm) {
mListener.onCompletedImageDownload(bm);
}
}
如果在将公共interface
添加到“ DownloadImageTask” Java文件时遇到任何问题,只需创建一个单独的Java文件名“ DownloadImageListener”,然后在其中放置interface
代码即可。
设置代码以查询AsyncTask
。
从此更改Adapter
中的getView()
代码:
try {
Bitmap bm = new DownloadImageTask().execute(dataModel.getSpriteUrl()).get();
viewHolder.sprite.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
对此:
try {
DownloadImageListener listener = new DownloadImageListener() {
@Override
public void onCompletedImageDownload(Bitmap bm) {
if(bm != null){
viewHolder.sprite.setImageBitmap(bm);
}
}
};
String imageUrl = dataModel.getSpriteUrl();
DownloadImageTask downloadImageTask = new DownloadImageTask(imageUrl, listener);
downloadImageTask.execute();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
这允许您的AsyncTask
执行,并且当返回Bitmap
时,将以onPostExecute()
方法触发侦听器,将Bitmap
发送到您的ListView
onCompletedImageDownload()
回调方法。
其他信息:
为了进一步提高性能,您可以创建一个缓存模型来保存和检索设备中的图像(如果您以前已经下载过这些图像)。但这需要一些非常先进的技术-当您希望下载的图像可能不时更改时,就会变得非常棘手。