2011-12-15 34 views
13

Tôi đang có một ListView với bộ điều hợp tùy chỉnh của riêng tôi bắt nguồn từ một BaseAdapter. Mỗi mục trong ListView có các mục con như ImageViewTextView.Cách biết chế độ xem nào bên trong một mục ListView cụ thể được nhấp vào

Tôi làm cách nào để biết một trong các mục phụ mà người dùng đã nhấp vào? Có thể đính kèm một người nghe trong hàm getView chẳng hạn, hoặc đó có thể là một vấn đề không?

/Henrik

Edit: Hiện nay tôi có một onItemClick trong Hoạt động chứa ListView. Có cách nào tốt để biết mục phụ nào trong một mục cụ thể trong ListView đã được nhấn bằng cách kiểm tra các tham số trong onItemClick.

@Override 
public void onItemClick(AdapterView<?> a, View v, int pos, long id) { 
. 
. 
} 

Trả lời

3

Bạn có thể thực hiện. Bạn cần phải sửa đổi phương thức getView của mình:

@Override 
public View getView(final int position, View row, final ViewGroup parent) { 
    ...  
    YourWrapper wrapper = null; 
    if (row == null) { 
     row = getLayoutInflater().inflate(R.layout.your_row, parent, false); 
     wrapper = new YourWrapper(row); 
     row.setTag(wrapper); 
    } else { 
     wrapper = (YourWrapper) row.getTag(); 
    } 

    wrapper.yourSubView.setOnClickListener(new View.OnClickListener() 
    {    
    @Override 
    public void onClick(View v) { 
     // do something 
    } 
    ... 
} 
+0

Điều này làm việc tốt, bởi vấn đề là tôi chuyển sang một ImageButton. Và điều đó không hoạt động như mong đợi. Đối với những người khác, hãy xem câu hỏi này: http://stackoverflow.com/questions/6116583/android-listview-custom-adapter-imagebutton – Henrik

0

Vì bạn chỉ có ImageView và TextView, bạn có thể thay đổi ImageView thành ImageButton. Sau đó bạn có thể thêm một người nghe trên ImageButton sẽ được gọi nếu người dùng nhấp vào hình ảnh. Nếu anh ta nhấp vào bất cứ nơi nào khác trong mục (bao gồm cả TextView), onItemclicklistener sẽ được gọi. Điều này đơn giản hơn nhiều.

+0

Tôi mất rằng onItemClickListener-callback, tức là nó không được gọi, chỉ người nghe cho rằng ImageButton được gọi là – Henrik

3

ListView tái chế các đối tượng dạng xem hàng và gán dữ liệu mới cho chúng khi "getView" được gọi, vì vậy cách tiếp cận để sử dụng, là thêm một người nghe vào hàm getView. Dưới đây là mẫu mã từ ứng dụng cho biết cách thực hiện:

private class DeletePlayerAdapter extends ArrayAdapter<Player> { 
     Context context; 
     int layoutResourceId; 
     ArrayList<Player> data; 

     public DeletePlayerAdapter(Context context, int layout, 
       ArrayList<Player> list) { 
      super(context, layout, list); 
      this.layoutResourceId = layout; 
      this.context = context; 
      this.data = list; 
     } 

     @Override 
     public View getView(final int position, View convertView, 
       ViewGroup parent) { 
      View row = convertView; 
      PlayerHolder holder = null; 
      if (row == null) { 
       LayoutInflater inflater = ((Activity) context) 
         .getLayoutInflater(); 
       row = inflater.inflate(layoutResourceId, parent, false); 
       holder = new PlayerHolder(); 
       holder.player_name = (TextView) row 
         .findViewById(R.id.player_name); 
       holder.player_number = (TextView) row 
         .findViewById(R.id.player_number); 
       holder.seeded_button = (ImageButton) row 
         .findViewById(R.id.delete_toggle); 
       holder.player_name.setTypeface(tf); 
       holder.player_number.setTypeface(tf); 
       row.setTag(holder); 
       players_array.get(position).marked_for_delete = false; 

      } else { 
       Log.d("PLAYER_ADAPTER", "NOT_NULL ROW"); 
       holder = (PlayerHolder) row.getTag(); 
      } 
      holder.seeded_button.setOnClickListener(new OnClickListener() { 
       // 
       // Here is the magic sauce that makes it work. 
       // 
       private int pos = position; 

       public void onClick(View v) { 
        ImageButton b = (ImageButton) v; 
        if (b.isSelected()) { 
         b.setSelected(false); 
         players_array.get(pos).marked_for_delete = false; 
        } else { 
         b.setSelected(true); 
         players_array.get(pos).marked_for_delete = true; 
        } 
       } 
      }); 
      Player p = data.get(position); 
      holder.player_name.setText(p.name); 
      holder.player_number.setText(String.valueOf(position+1)); 
      holder.seeded_button 
        .setSelected(players_array.get(position).marked_for_delete); 
      return row; 
     } 

    } 

    static class PlayerHolder { 
     TextView player_number; 
     TextView player_name; 
     ImageButton seeded_button; 
    } 
+0

Nếu tôi làm như thế này tôi nhận được nhấp chuột cho đó ImageView particualar, nhưng onItemClick biến mất, tức là việc nhấp vào tất cả các lĩnh vực khác của mục (mListView.setOnItemClickListener (this);) Có thể kết hợp các hai mà không cần thêm một người nghe mặt hàng cho một sẽ xem phụ trong mục cụ thể đó? – Henrik

+0

Không có lý do gì bạn không thể sử dụng cả hai cách tiếp cận. Nhưng hãy nhớ, không có cách nào để biết đứa trẻ nào được nhấp vào trong mụcNhấp vào, bởi vì ti chỉ cung cấp cho bạn chế độ xem và vị trí. Vì vậy, bạn sẽ nhận được dương tính giả. Nhưng nó không phải là quá nhiều công việc để thêm người nghe cho các lượt xem con khác trong một hàng. Đó là cách tiếp cận mà tôi đề xuất, trừ khi hàm itemSelect nên được gọi trong mọi trường hợp, bao gồm khi vùng có clickListener được nhấn. –

0

Tôi đã đưa ra một giải pháp chung và có thể tái sử dụng. Thay vì mở rộng bộ điều hợp danh sách cụ thể và sửa đổi phương thức getView(), tôi đã tạo một lớp mới triển khai giao diện ListAdapter chuyển tiếp một cách mù quáng gần như mọi thứ sang một số khác ListAdapter ngoại trừ getView(). Nó trông giống như thế này:

public class SubClickableListAdapter implements ListAdapter { 

    public static interface OnSubItemClickListener { 
     public void onSubItemClick(View subView, int position); 
    } 
    private ListAdapter other; 

    private SparseArray<OnSubItemClickListener> onClickListeners; 

    public SubClickableListAdapter(ListAdapter other) { 
     this.other = other; 
     onClickListeners = new SparseArray<OnSubItemClickListener>(); 
    } 

    public void setOnClickListener(int id, OnSubItemClickListener listener) { 
     onClickListeners.put(id, listener); 
    } 

    public void removeOnClickListener(int id) { 
     onClickListeners.remove(id); 
    } 

    @Override 
    public View getView(final int position, View convertView, ViewGroup parent) { 
     View view = other.getView(position, convertView, parent); 
     for(int i = 0; i < onClickListeners.size(); i++) { 
      View subView = view.findViewById(onClickListeners.keyAt(i)); 
      if (subView != null) { 
       final OnSubItemClickListener listener = onClickListeners.valueAt(i); 
       if (listener != null) { 
        subView.setOnClickListener(new OnClickListener() { 
         @Override 
         public void onClick(View v) { 
          listener.onSubItemClick(v, position); 
         } 
        }); 
       } 
      } 
     } 
     return view; 
    } 

    // other implemented methods 
} 

Các phương pháp thực hiện khác chỉ đơn giản giống như một sau:

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

Để sử dụng nó, bạn chỉ cần khởi tạo nó cung cấp bất kỳ khác ListAdapter (có thể là ArrayAdapter hoặc SimpleCursorAdapter hoặc bất cứ điều gì khác). Sau đó, hãy gọi setOnClickListener() cho mỗi chế độ xem nơi bạn muốn nghe trên nhấp chuột, cung cấp id của nó trong thông số id và trình nghe của bạn trong thông số listener. Để lấy id hàng cho hàng đã được nhấp, hãy gọi phương thức getItemIdAtPosition(position) của ListView của bạn (bạn phải theo cách khác, vì nó không được dùng làm tham số cho gọi lại của bạn, nhưng đó không phải là vấn đề lớn nhất các trường hợp).

Ưu điểm của giải pháp này là nó có thể được sử dụng với bất kỳ ListAdapter nào. Vì vậy, nếu ứng dụng của bạn có một số ListView s, mỗi ứng dụng sử dụng các chế độ xem cơ bản khác nhau hoặc thậm chí các bộ điều hợp khác nhau, bạn không phải tạo một lớp bộ điều hợp mới cho từng loại.

Sự cố với vấn đề này giống với tất cả các giải pháp khác: OnItemClick() của ListView sẽ không được gọi nếu bạn nhấp vào chế độ xem mà bạn đã đăng ký người nghe. Đối với các giao diện mà bạn không đăng ký người nghe, cuộc gọi lại này sẽ được gọi là mặc dù.Ví dụ: bạn có hoạt động cho mục danh sách chứa hai trường văn bản và nút và bạn đăng ký trình nghe cho nút, sau đó nhấp vào nút sẽ không gọi số OnItemClick() của số ListView, nhưng gọi lại của bạn . Nhấp vào bất kỳ nơi nào khác gọi OnItemClick().

10

Tôi đã sử dụng ý tưởng từ Miga's Hobby Programming.

Điều quan trọng là gọi performItemClick() từ trình nghe mới trên ClickClick. Điều này chuyển các nhấp chuột vào thông qua onItemClick() đã được sử dụng cho listview. Thật nhanh chóng và dễ dàng, tôi cảm thấy như tôi đang lừa dối.

Dưới đây là getView(), từ danh sách adapter:

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

    // Check if an existing view is being reused, otherwise inflate the view 
    if (convertView == null) { 
     convertView = LayoutInflater.from(getContext()).inflate(R.layout.one_line, parent, false); 
    } 

    // This chunk added to get textview click to register in Fragment's onItemClick() 
    // Had to make position and parent 'final' in method definition 
    convertView.findViewById(R.id.someName).setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      ((ListView) parent).performItemClick(v, position, 0); 
     } 
    }); 
    // do stuff... 
} 

Và onItemClick():

@Override 
public void onItemClick(AdapterView adapterView, View view, int position, long id) { 

    long viewId = view.getId(); 

    if (viewId == R.id.someName) { 
     Toast.makeText(getActivity(), "someName item clicked", Toast.LENGTH_SHORT).show(); 
    } 
    else { 
     Toast.makeText(getActivity(), "ListView clicked: " + id, Toast.LENGTH_SHORT).show(); 

    } 
} 
+0

giải pháp làm sạch tách rời sự kiện tuyên truyền khỏi xử lý sự kiện – younes0

Các vấn đề liên quan