seperater Image Download in Custom Adapter

  • Antworten:12
  • OffenNicht stickyBentwortet
  • Forum-Beiträge: 137

22.08.2014, 16:53:57 via Website

Hallo Leute,

steh mal wieder vor einem Problem.

Ich habe Bilder auf meinem Webspace welche in ein custom_listitem über einen Custom Adapter zuweise.

Die Bilder werden in einem Async Task mittels UrlConnection abgerufen und via ViewHolder an das item übergeben.

Funktioniert alles toll. nach ein paar Sekunden ist die Liste mit den Daten + Bilder da.

Das Problem ist das ich wenn ich jetzt scrolle und die getView wieder ausgeführt wird immer einen riesigen Lagg hab.

Code:

CustomAdapter:

public class SearchAdapter extends BaseAdapter {

Context ctx;
private ArrayList<String[]> mData = new ArrayList<String[]>();
private LayoutInflater mInflater;



public SearchAdapter(Context ctx){

    mInflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.ctx = ctx;

}

public void addItem(final String name,final String rolle, String mail) {

    String[] s = new String[3];
    s[0]=name;
    s[1]=rolle;
    s[2]=mail;





    mData.add(s);
    notifyDataSetChanged();
}


@Override
public int getCount() {
    // TODO Auto-generated method stub
    return mData.size();
}

@Override
public String getItem(int position) {
    // TODO Auto-generated method stub
    return mData.get(position).toString();
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;


    if (convertView == null) {
        holder = new ViewHolder();

                convertView = mInflater.inflate(R.layout.searchitem, null);
                holder.username = (TextView)convertView.findViewById(R.id.resultname);
                holder.role = (TextView)convertView.findViewById(R.id.searchrolle);
                holder.profilepic = (ImageView)convertView.findViewById(R.id.profileimage);



                convertView.setTag(holder);

        }


        else{
             holder = (ViewHolder)convertView.getTag();
        }







    String data[] = mData.get(position);


    holder.username.setText(data[0]);

//
holder.role.setText(data[1]);

    try {

        if(new getpic().execute(data[2]).get()!=null){

        holder.profilepic.setImageBitmap(getCircleBitmap(new getpic().execute(data[2]).get()));

        }
        else{
        holder.profilepic.setImageBitmap(getCircleBitmap(BitmapFactory.decodeResource(ctx.getResources(), R.drawable.avatarbild)));
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ExecutionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    //holder.profilepic.setImageBitmap(getCircleBitmap(BitmapFactory.decodeResource(ctx.getResources(), R.drawable.ic_action_person)));







    return convertView;
}

public class ViewHolder {
    public TextView username;
    public TextView role;
    public ImageView profilepic;


}

private Bitmap getCircleBitmap(Bitmap bitmap) {
     final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
      bitmap.getHeight(), Bitmap.Config.ARGB_8888);
     final Canvas canvas = new Canvas(output);

     final int color = Color.RED;
     final Paint paint = new Paint();
     final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
     final RectF rectF = new RectF(rect);

     paint.setAntiAlias(true);
     canvas.drawARGB(0, 0, 0, 0);
     paint.setColor(color);
     canvas.drawOval(rectF, paint);

     paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
     canvas.drawBitmap(bitmap, rect, rect, paint);

     bitmap.recycle();

     return output;
    }


//----------------------------------


private class getpic extends AsyncTask<String, Void, Bitmap> {


    Bitmap image;

    URL urlpic;

    public void onPreExecute() {


    }

    @Override
    protected Bitmap doInBackground(String... params) {
        // TODO Auto-generated method stub


        try {

            urlpic = new URL("http://bastian-seidemann.de/profilbilder/" + params[0] + ".jpg");
            image = BitmapFactory.decodeStream(urlpic.openConnection().getInputStream());




        } catch (Exception e) {
            Log.e("error", "FEHLERBESCHREIBUNG" + e.toString());
        }

        return image;

    }

    public void onPostExecute(Bitmap result) {





        }

    }

}

Wie kriege ich hin das die Liste erscheint und den Text anzeigt. Und die Bilder erst nach und nach dazu lädt.

Danke schonmal vornweg :)

— geändert am 22.08.2014, 17:02:04

Antworten
  • Forum-Beiträge: 11.137

22.08.2014, 17:03:10 via App

Ist klar immer wenn die getView aufgerufen wird, werden alle Daten neu geladen.
Du musst die Daten vorher laden und sie bsp anhand eines Arrays über die Viewpos setzen.
Ich habe das auch erst gemacht.
Hast du auch das Problem, dass die ListEinträge eine falsche reihenfolge haben?

LG Pascal //It's not a bug, it's a feature. :) ;)

Bastian Seidemann

Antworten
  • Forum-Beiträge: 137

22.08.2014, 17:16:20 via Website

Nein von der Reihenfolge her ist alles da wo es hingehört :)

Allerdings weiß ich nicht so recht wie ich das ganze in den Adapter integrieren kann.

Hast du ein Beispiel?

Antworten
  • Forum-Beiträge: 11.137

22.08.2014, 18:13:39 via App

Ich würde eine for schleife über alle Elemente sprich mData.size() machen.
Darin
den string array der aktuellen posiotion holen den Download ausführen und die geladenen Bilder dann in ein 2. Array vom Typ Bitmap oder wie auch immer.

Im getView musst du dann nurnoch aufs Array über die Position zugreifen

LG Pascal //It's not a bug, it's a feature. :) ;)

Bastian Seidemann

Antworten
  • Forum-Beiträge: 11.137

23.08.2014, 12:15:41 via App

Nein dann musst du noch den Text laden.
Aber warscheinlich ist es einfacher du hast ein Objekt indem du 1 Bild und den zugehörigen Text speicherst.
Dann brauchst du nurnoch ein Array deines Objektes machen und im getView über die pos auslesen.

— geändert am 23.08.2014, 12:15:55

LG Pascal //It's not a bug, it's a feature. :) ;)

Bastian Seidemann

Antworten
  • Forum-Beiträge: 137

23.08.2014, 16:21:26 via Website

Puh, ich stehe echt auf dem Schlauch.

Wie implementiere ich das in den Adapter?

Quasi geht es um Profilbilder: jeder user hat eine feste Mailadresse. Anhand dieser wird das Profilbild vom server abgerufen.

Der Link setzt sich immer folgendermaßen zusammen:

www.xxxxxxxxxxxxx.de/profilbilder/'useremail'+"jpg"

Ich brauch nen Denkanstoß.

Antworten
  • Forum-Beiträge: 11.137

23.08.2014, 16:45:01 via App

Du hast doch in deinem oberen Code schon fast alles gehabt.
Das einzige was du ändern musst ist der Zeitpunkt.
Es soll nämlich nicht in der getView geladen werden sondern in einer anderen Methode.
Was ist denn unklar verstehe dein Problem nicht.

LG Pascal //It's not a bug, it's a feature. :) ;)

Bastian Seidemann

Antworten
  • Forum-Beiträge: 137

23.08.2014, 17:36:17 via Website

Sooo jetzt scrollt die Liste super schnell weil ich es aus Arraylisten hole die ich in der addItem() anlege.

ich scrolle flott runter und dann stürzt das ganze ab mit:

08-23 17:34:14.157: E/AndroidRuntime(10324): FATAL EXCEPTION: main
08-23 17:34:14.157: E/AndroidRuntime(10324): Process: android.developerskitchen.joindevelopers, PID: 10324
08-23 17:34:14.157: E/AndroidRuntime(10324): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@42cc1e10
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.graphics.Canvas.throwIfCannotDraw(Canvas.java:1084)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.graphics.Canvas.drawBitmap(Canvas.java:1201)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.developerskitchen.joindevelopers.SearchAdapter.getCircleBitmap(SearchAdapter.java:159)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.developerskitchen.joindevelopers.SearchAdapter.getView(SearchAdapter.java:128)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.widget.AbsListView.obtainView(AbsListView.java:2255)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.widget.ListView.makeAndAddView(ListView.java:1790)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.widget.ListView.fillUp(ListView.java:725)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.widget.ListView.fillGap(ListView.java:664)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5143)
08-23 17:34:14.157: E/AndroidRuntime(10324): at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3243)

Code:

public class SearchAdapter extends BaseAdapter {

Context ctx;
private ArrayList<String[]> mData = new ArrayList<String[]>();
private LayoutInflater mInflater;
ArrayList<String> namen = new ArrayList<String>();
ArrayList<String> rollen = new ArrayList<String>(); 
ArrayList<Bitmap> bilder = new ArrayList<Bitmap>();



public SearchAdapter(Context ctx){

    mInflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    this.ctx = ctx;

}

public void addItem(final String name,final String rolle, String mail) throws InterruptedException, ExecutionException {

    String[] s = new String[3];
    s[0]=name;
    s[1]=rolle;
    s[2]=mail;

    namen.add(name);
    rollen.add(rolle);

    Bitmap picture = new getpic().execute(mail).get();

    if(picture!=null&amp;&amp;!picture.isRecycled()){
    bilder.add(picture);
    }else{
        bilder.add(BitmapFactory.decodeResource(ctx.getResources(), R.drawable.avatarbild));
    }




    mData.add(s);
    notifyDataSetChanged();
}


@Override
public int getCount() {
    // TODO Auto-generated method stub
    return mData.size();
}

@Override
public String getItem(int position) {
    // TODO Auto-generated method stub
    return mData.get(position).toString();
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;


    if (convertView == null) {
        holder = new ViewHolder();

                convertView = mInflater.inflate(R.layout.searchitem, null);
                holder.username = (TextView)convertView.findViewById(R.id.resultname);
                holder.role = (TextView)convertView.findViewById(R.id.searchrolle);
                holder.profilepic = (ImageView)convertView.findViewById(R.id.profileimage);



                convertView.setTag(holder);

        }


        else{
             holder = (ViewHolder)convertView.getTag();
        }



    holder.username.setText(namen.get(position));

    holder.role.setText(rollen.get(position));

    holder.profilepic.setImageBitmap(getCircleBitmap(bilder.get(position)));



    return convertView;
}

public class ViewHolder {
    public TextView username;
    public TextView role;
    public ImageView profilepic;


}

private Bitmap getCircleBitmap(Bitmap bitmap) {
     final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
      bitmap.getHeight(), Bitmap.Config.ARGB_8888);
     final Canvas canvas = new Canvas(output);

     final int color = Color.RED;
     final Paint paint = new Paint();
     final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
     final RectF rectF = new RectF(rect);

     paint.setAntiAlias(true);
     canvas.drawARGB(0, 0, 0, 0);
     paint.setColor(color);
     canvas.drawOval(rectF, paint);

     paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
     canvas.drawBitmap(bitmap, rect, rect, paint);

     bitmap.recycle();

     return output;
    }


//----------------------------------


private class getpic extends AsyncTask<String, Void, Bitmap> {


    Bitmap image;

    URL urlpic;

    public void onPreExecute() {


    }

    @Override
    protected Bitmap doInBackground(String... params) {
        // TODO Auto-generated method stub


        try {

            urlpic = new URL("http://xxxxxxxxxxxxxxxxxx.de/profilbilder/" + params[0] + ".jpg");
            image = BitmapFactory.decodeStream(urlpic.openConnection().getInputStream());




        } catch (Exception e) {
            Log.e("error", "FEHLERBESCHREIBUNG" + e.toString());
        }

        return image;

    }

    public void onPostExecute(Bitmap result) {





        }

    }

}

Antworten
  • Forum-Beiträge: 11.137

23.08.2014, 17:44:38 via Website

Nach dem Log Tritt der Fehler in Zeile 159 auf:
Meiner meinung nach machst du da laut Code gerate btimap.recycle();

Das Problem ist, dass du das bild recylcest. Dadurch wird zwar speicherplatz gespart aber es kann nicht mehr verwendet werden. Also wenn du einmal die Liste herunterscrollst sollte das gehen aber beim hochscrollen wirds abstürzen.
Deswegen musst du das recycle rausnehmen, denn Android erlaubt nicht, recycelte Bitmaps nochmal zu benutzen:
http://developer.android.com/training/displaying-bitmaps/index.html

LG Pascal //It's not a bug, it's a feature. :) ;)

Bastian Seidemann

Antworten
  • Forum-Beiträge: 137

23.08.2014, 17:48:02 via Website

Und Spiel , Satz und Sieg!

Danke jetzt klappt es :)

in der funktion getCircleBitmap() habe ich das Bitmap recycled.

Vielen Dank :))

Antworten
  • Forum-Beiträge: 11.137

23.08.2014, 17:51:23 via Website

Kein Problem,
vlt. solltest du trotzdem noch das Ramverhalten deiner App überprüfen.
Wenn es übermäßig viel ist, dann solltest du dir noch was überlegen.

LG Pascal //It's not a bug, it's a feature. :) ;)

Bastian Seidemann

Antworten