2013-05-09 9 views
21

Ich habe ein mysteriöses Problem gefunden, das ein Fehler sein kann! Ich habe eine Liste in meinem Fragment. Jede Zeile hat eine Schaltfläche. Die Liste sollte nicht auf Klicken reagieren, aber die Schaltflächen sind anklickbar.Android, List Adapter gibt falsche Position in getView zurück

Um zu bekommen, welche Schaltfläche geklickt hat, habe ich einen Listener erstellt und in meinem Fragment implementiert. Dies ist der Code meines Adapters.

public class AddFriendsAdapter extends BaseAdapter { 

    public interface OnAddFriendsListener { 
     public void OnAddUserClicked(MutualFriends user); 
    } 

    private final String TAG = "*** AddFriendsAdapter ***"; 

    private Context context; 
    private OnAddFriendsListener listener; 
    private LayoutInflater myInflater; 
    private ImageDownloader imageDownloader; 
    private List<MutualFriends> userList; 

    public AddFriendsAdapter(Context context) { 
     this.context = context; 
     myInflater = LayoutInflater.from(context); 

     imageDownloader = ImageDownloader.getInstance(context); 
    } 

    public void setData(List<MutualFriends> userList) { 
     this.userList = userList; 

     Log.i(TAG, "List passed to the adapter."); 
    } 

    @Override 
    public int getCount() { 
     try { 
      return userList.size(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return 0; 
     } 
    } 

    @Override 
    public Object getItem(int position) { 
     return null; 
    } 

    @Override 
    public long getItemId(int position) { 
     return position; 
    } 

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

     if (convertView == null) { 
      convertView = myInflater.inflate(R.layout.list_add_friends_row, null); 
      holder = new ViewHolder(); 

      Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf"); 
      holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName); 
      holder.tvUserName.setTypeface(font); 
      holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture); 
      holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd); 
      holder.btnAdd.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        Log.e(TAG, "Item: " + position); 
        listener.OnAddUserClicked(userList.get(position)); 
       } 
      }); 

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

     holder.tvUserName.setText(userList.get(position).getName()); 
     imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl()); 

     return convertView; 
    } 

    public void setOnAddClickedListener(OnAddFriendsListener listener) { 
     this.listener = listener; 
    } 

    static class ViewHolder { 
     TextView tvUserName; 
     ImageView ivPicture; 
     Button btnAdd; 
    } 
} 

Wenn ich die app laufen, kann ich meine Zeilen aber da meine Liste ist lang und hat über 200 Artikel, wenn ich gehe zu Mitte und klicken Sie auf ein Element sehen dann zurück Position falsch ist (es ist so etwas wie 7, manchmal 4 und usw.).

Nun, was ist das Geheimnis? Wenn ich auf Element Listener der Liste von meinem Fragment aktiviere und auf Zeile klicke, dann wird die korrekte Zeilenposition angezeigt, während ich auf dieser Zeile klicke, wenn ich auf die Schaltfläche klicke, dann wird die falsche Position angezeigt.

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
      @Override 
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 
       Log.e(TAG, "item " + position + " clicked."); 
      } 
     }); 

Ergebnis in logcat:

05-09 10:22:25.228: E/AddFriendsFragment(20296): item 109 clicked. 
05-09 10:22:34.453: E/*** AddFriendsAdapter ***(20296): Item: 0 

Jeder Vorschlag würde geschätzt. Dank

+0

Wo implementieren Sie 'OnAddUserClicked()'? Auch wenn Sie einen BaseAdapter und 'List ' verwenden, können Sie Ihren Adapter vereinfachen, indem Sie 'ArrayAdapter' verwenden, da er im Grunde _is_' List' ist und das genaue Problem des Ladens eines Arrays oder einer Liste von Datenobjekten in ein AdapterView – FoamyGuy

Antwort

46

Da der convertView und Halter wird die Verwendung recycelt werden, Ihre setOnClickListener aus der else if-Anweisung verschieben:

if (convertView == null) { 
     convertView = myInflater.inflate(R.layout.list_add_friends_row, null); 
     holder = new ViewHolder(); 

     Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf"); 
     holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName); 
     holder.tvUserName.setTypeface(font); 
     holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture); 
     holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd); 
     convertView.setTag(holder); 
    } else { 
     holder = (ViewHolder) convertView.getTag(); 
    } 
    holder.btnAdd.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) 
       Log.e(TAG, "Item: " + position); 
       listener.OnAddUserClicked(userList.get(position)); 
      } 
     }); 

Es ist nicht die beste Lösung für das ist, weil es einig Performance-Problem sein. Ich empfehle Ihnen, eine Karte für Ihre Ansicht zu erstellen und eine neue Ansicht für Ihr Objekt zu erstellen. Verwenden Sie dann einfach die relative Ansicht für jede Ansicht.

ich denke, es wird eine bessere Lösung mit bester Leistung sein:

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

    if (convertView == null) { 
     convertView = myInflater.inflate(R.layout.list_add_friends_row, null); 
     holder = new ViewHolder(); 

     Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf"); 
     holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName); 
     holder.tvUserName.setTypeface(font); 
     holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture); 
     holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd); 
     holder.btnAdd.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Integer pos = (Integer)v.getTag(); 
       Log.e(TAG, "Item: " + pos); 
       listener.OnAddUserClicked(userList.get(pos)); 
      } 
     }); 

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

    holder.tvUserName.setText(userList.get(position).getName()); 
    imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl()); 
    holder.btnAdd.setTag(position); 
    return convertView; 
} 

Sie können auch selbst Ihre Ansicht verwalten. Erstellen Sie jede eindeutige Ansicht für Ihren Artikel, nicht die Ansicht.

//member various 
private Map<Integer, View> myViews = new HashMap<Integer, View>(); 

@Override 
public View getView(final int position, View convertView, ViewGroup parent) { 
    ViewHolder holder; 
    View view = myViews.get(position); 
    if (view == null) { 
     view = myInflater.inflate(R.layout.list_add_friends_row, null); 
     //don't need use the holder anymore. 

     Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf"); 
     holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName); 
     holder.tvUserName.setTypeface(font); 
     holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture); 
     holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd); 
     holder.btnAdd.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Integer pos = (Integer)v.getTag(); 
       Log.e(TAG, "Item: " + pos); 
       listener.OnAddUserClicked(userList.get(pos)); 
      } 
     }); 

     holder.tvUserName.setText(userList.get(position).getName()); 
     imageDownloader.displayImage(holder.ivPicture, 
       userList.get(position).getPhotoUrl()); 
     myViews.put(position, view); 

    } 
    return view; 
} 
+0

Ich glaube nicht, wo Sie den Click-Listener setzen wird, da es verschiedene Werte für "Position" erhalten soll, ist nachfolgende Aufrufe. Und so wird es dasselbe tun, wenn nur ein anderes Benutzerobjekt basierend auf der Position übergeben wird, die an getView() übergeben wurde. – FoamyGuy

+0

Genau, Baptcoder ist richtig.Als ich es herausbewegte, gaben sowohl der Listenelement-Listener als auch mein Listener die gleiche Position zurück. Nochmals vielen Dank :) Bitte geben Sie mir eine Probe, wenn Sie über Ihren Vorschlag haben. – Hesam

+0

Weil wir in den Anweisungen 'if (convertView == null) {' setzen, wird die Position nicht gesetzt, wenn 'convertView' nicht null ist. Es passiert jedes Mal, wenn listview das Convertview recycelt. Wir müssen also die gesetzte Position aus der is else-Anweisung herausnehmen, damit wir die richtige Position bekommen können. – buptcoder

2

Haben Sie versucht, so etwas wie dies zu tun:

holder.btnAdd.setTag(Integer.valueOf(position)); 

Und dann weicht Reihe abzurufen in der Callback für die Schaltfläche, wie diese angeklickt wurde:

public void btnAddClickListener(View view) 
    { 
     position = (Integer)view.getTag(); 
     Foo foo = (Foo)foos_adapter.getItem(position); //get data of row(position) 
     //do some 
    } 
+0

Seltsam, dass nach drei Jahren und zwei ähnlichen Entscheidungen die erste Erwähnung für diese Antwort ist. Auch dies ist eine universelle Lösung, da wir ein Objekt 'onClickListener = new View.OnClickListener (...)' erstellen und es einer beliebigen Schaltfläche zuweisen können (falls es viele gibt). – CoolMind

1

Ein weiterer Ansatz, den ich gefunden nützlich (wenn Sie das ViewHolder-Muster natürlich verwenden) ist, den Index auf ein separates Attribut zu setzen, wann immer getView() aufgerufen wird, dann müssen Sie in Ihrem onClickListener nur auf das Po Ihres Halters verweisen sition-Attribut, etwa wie folgt:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 

    final ViewHolder holder; 

    if(convertView == null){ 

     convertView = View.inflate(mContext, R.layout.contact_picker_row,null); 

     holder = new ViewHolder(); 

     holder.body = (RelativeLayout)convertView.findViewById(R.id.numberBody); 

     convertView.setTag(holder); 

    }else{ 

     holder = (ViewHolder)convertView.getTag(); 

    } 

    holder.position = position; 

    holder.body.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 

      Toast.makeText(mContext,"Clicked on: "+holder.position,Toast.LENGTH_LONG).show(); 

     } 
    }); 

    return convertView; 
} 

private class ViewHolder{ 

    RelativeLayout body; 
    int position; 

} 
Verwandte Themen