2012-11-29 33 views
11

Tôi sẽ bắt đầu bằng cách nói rằng tôi đã đọc chi tiết hầu hết mọi câu hỏi trên SO mà tôi có thể tìm thấy liên quan đến các mục danh sách có thể chọn tùy chỉnh và bộ chọn. Nhiều người trong số họ có các sự cố tương tự nhưng không có câu trả lời nào giải quyết được sự cố của tôi.Mục danh sách tùy chỉnh không phản hồi trạng thái được chọn trong bộ chọn

Tại thời điểm trong ứng dụng của tôi, tôi trình bày hoạt động danh sách tùy chỉnh. Khi được tạo, nó sẽ truy lục một tập dữ liệu tĩnh từ mục đích gọi nó và chuyển dữ liệu đó tới bộ điều hợp mảng tùy chỉnh của nó. Mỗi mục danh sách là một RelativeLayout đơn giản thực hiện giao diện Checkable. Theo mặc định, nếu bạn nhấp vào một trong các mục, một hoạt động mới sẽ được hiển thị, hiển thị thông tin chi tiết về số liên lạc đã chọn. Tuy nhiên, nếu một mục trong danh sách được nhấn lâu, một ActionMode được bắt đầu. Nhấp vào một mục trong danh sách tại thời điểm này không hiển thị hoạt động chi tiết, nó chỉ đặt mục để kiểm tra. Sau đó, nếu người dùng chọn một trong các mục chế độ hành động, nó sẽ thực hiện hành động trên (các) mục được chọn.

Điều quan trọng cần hiểu là trong cả hai chế độ lựa chọn, việc nhấp vào một mục danh sách sẽ đặt nó thành kiểm tra.

Tất cả những gì tôi mô tả ở trên đều hoạt động hoàn hảo. My chỉ vấn đề phải làm với hình nền của các mục danh sách không được đánh dấu khi chúng được thiết lập để kiểm tra, thậm chí bằng cách sử dụng bộ chọn mặc định.

Những gì tôi muốn làm, có hai bộ chọn: một cho mỗi chế độ lựa chọn. Trong phần đầu tiên, nền không thay đổi khi một mục được chọn, và trong phần thứ hai thì nó sẽ xuất hiện. Tôi đã thử triển khai các bộ chọn tùy chỉnh, nhưng ngay cả trong những trạng thái được chọn sẽ bị bỏ qua! Các phần khác của bộ chọn hoạt động tốt, nhưng không hoạt động.

Việc triển khai CheckableListItem của tôi kết hợp ý tưởng từ nhiều ví dụ khác nhau, vì vậy nếu tôi làm điều gì đó sai hoặc nếu có cách nào tốt hơn thì hãy cho tôi biết!

Note: Một điểm thú vị là nếu tôi đặt nền của danh sách các mục trong results_list_item.xml để chọn tôi, thay vì tài sản listSelector của ListView, các nền làm thay đổi khi một mục được kiểm tra . Tuy nhiên, việc này làm cho quá trình chuyển đổi báo chí dài trong bộ chọn của tôi không hoạt động.

ResultsActivity.java:

public class ResultsActivity extends ListActivity implements OnItemLongClickListener { 

    private ListView listView;   // Reference to the list belonging to this activity 
    private ActionMode mActionMode;  // Reference to the action mode that can be started 
    private boolean selectionMode;  // Detail mode or check mode 

    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_results); 

     // When the home icon is pressed, go back 
     ActionBar actionBar = getActionBar(); 
     actionBar.setDisplayHomeAsUpEnabled(true); 

     // Get a reference to the list 
     listView = getListView(); 

     // Initially in detail mode 
     selectionMode = true; 

     // Get the contacts from the intent data and pass them to the contact adapter 
     @SuppressWarnings("unchecked") 
     ArrayList<Contact> results = ((ArrayList<Contact>)getIntent().getSerializableExtra("results")); 
     Contact[] contacts = new Contact[results.size()]; 
     ContactArrayAdapter adapter = new ContactArrayAdapter(this, results.toArray(contacts)); 
     setListAdapter(adapter); 

     // We will decide what happens when an item is long-clicked 
     listView.setOnItemLongClickListener(this); 
    } 

    /** 
    * If we are in detail mode, when an item in the list is clicked 
    * create an instance of the detail activity and pass it the 
    * chosen contact 
    */ 
    public void onListItemClick(ListView l, View v, int position, long id) { 
     if (selectionMode) { 
      Intent displayContact = new Intent(this, ContactActivity.class); 
      displayContact.putExtra("contact", (Contact)l.getAdapter().getItem(position)); 
      startActivity(displayContact); 
     } 
    } 

    public boolean onCreateOptionsMenu(Menu menu) { 
     return super.onCreateOptionsMenu(menu); 
    } 

    /** 
    * If the home button is pressed, go back to the 
    * search activity 
    */ 
    public boolean onOptionsItemSelected(MenuItem item) { 
     switch (item.getItemId()) { 
      case android.R.id.home: 
       Intent intent = new Intent(this, SearchActivity.class); 
       intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
       startActivity(intent); 
       return true; 
      default: 
       return super.onOptionsItemSelected(item); 
     } 
    } 

    /** 
    * When an item is long-pressed, switch selection modes 
    * and start the action mode 
    */ 
    public boolean onItemLongClick(AdapterView<?> adapter, View view, int position, long i) { 
     if (mActionMode != null) 
      return false; 

     if (selectionMode) { 
      toggleSelectionMode(); 
      listView.startActionMode(new ListActionMode(this, getListView())); 
      return true; 
     } 
     return false; 
    } 

    /** 
    * Clear the list's checked items and switch selection modes 
    */ 
    public void toggleSelectionMode() { 
     listView.clearChoices(); 
     ((ContactArrayAdapter)listView.getAdapter()).notifyDataSetChanged(); 
     if (selectionMode) { 
      selectionMode = false; 
     } else { 
      selectionMode = true; 
     } 
    } 
} 

activity_results.xml:

<ListView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@android:id/list" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:choiceMode="multipleChoice" 
    android:listSelector="@drawable/list_selector" /> 

list_selector.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android" > 
    <item android:state_pressed="true" android:drawable="@drawable/blue_transition" /> 
    <item android:state_checked="true" android:drawable="@drawable/blue" /> 
</selector> 

TwoLineArrayAdapter:

public abstract class TwoLineArrayAdapter extends ArrayAdapter<Contact> { 

    private int mListItemLayoutResId; 

    public TwoLineArrayAdapter(Context context, Contact[] results) { 
     this(context, R.layout.results_list_item, results); 
    } 

    public TwoLineArrayAdapter(Context context, int listItemLayoutResourceId, Contact[] results) { 
     super(context, listItemLayoutResourceId, results); 
     mListItemLayoutResId = listItemLayoutResourceId; 
    } 

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

     LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

     View listItemView = convertView; 
     if (convertView == null) { 
      listItemView = inflater.inflate(mListItemLayoutResId, parent, false); 
     } 

     // Get the text views within the layout 
     TextView lineOneView = (TextView)listItemView.findViewById(R.id.results_list_item_textview1); 
     TextView lineTwoView = (TextView)listItemView.findViewById(R.id.results_list_item_textview2); 

     Contact c = (Contact)getItem(position); 

     lineOneView.setText(lineOneText(c)); 
     lineTwoView.setText(lineTwoText(c)); 

     return listItemView; 
    } 

    public abstract String lineOneText(Contact c); 

    public abstract String lineTwoText(Contact c); 

} 

ContactArrayAdapter:

public class ContactArrayAdapter extends TwoLineArrayAdapter { 

    public ContactArrayAdapter(Context context, Contact[] contacts) { 
     super(context, contacts); 
    } 

    public String lineOneText(Contact c) { 
     return (c.getLastName() + ", " + c.getFirstName()); 
    } 

    public String lineTwoText(Contact c) { 
     return c.getDepartment(); 
    } 

} 

CheckableListItem.java:

public class CheckableListItem extends RelativeLayout implements Checkable { 

    private boolean isChecked; 
    private List<Checkable> checkableViews; 

    public CheckableListItem(Context context, AttributeSet attrs, 
      int defStyle) { 
     super(context, attrs, defStyle); 
     initialise(attrs); 
    } 

    public CheckableListItem(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initialise(attrs); 
    } 

    public CheckableListItem(Context context, int checkableId) { 
     super(context); 
     initialise(null); 
    } 

    private void initialise(AttributeSet attrs) { 
     this.isChecked = false; 
     this.checkableViews = new ArrayList<Checkable>(5); 
    } 

    public boolean isChecked() { 
     return isChecked; 
    } 

    public void setChecked(boolean check) { 
     isChecked = check; 
     for (Checkable c : checkableViews) { 
      c.setChecked(check); 
     } 
     refreshDrawableState(); 
    } 

    public void toggle() { 
     isChecked = !isChecked; 
     for (Checkable c : checkableViews) { 
      c.toggle(); 
     } 
    } 

    private static final int[] CheckedStateSet = { 
     android.R.attr.state_checked 
    }; 

    protected int[] onCreateDrawableState(int extraSpace) { 
     final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 
     if (isChecked()) { 
      mergeDrawableStates(drawableState, CheckedStateSet); 
     } 
     return drawableState; 
    } 

    protected void onFinishInflate() { 
     super.onFinishInflate(); 
     final int childCount = this.getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      findCheckableChildren(this.getChildAt(i)); 
     } 
    } 

    private void findCheckableChildren(View v) { 
     if (v instanceof Checkable) { 
      this.checkableViews.add((Checkable) v); 
     } 
     if (v instanceof ViewGroup) { 
      final ViewGroup vg = (ViewGroup) v; 
      final int childCount = vg.getChildCount(); 
      for (int i = 0; i < childCount; i++) { 
       findCheckableChildren(vg.getChildAt(i)); 
      } 
     } 
    } 
} 

results_list_item.xml:

<com.test.mycompany.Widgets.CheckableListItem 
    android:id="@+id/results_list_item" 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:paddingLeft="10dp" 
    android:paddingRight="10dp" 
    android:paddingTop="5dp" 
    android:paddingBottom="5dp" > 

    <TextView android:id="@+id/results_list_item_textview1" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentLeft="true" 
     android:textSize="20sp" 
     android:textColor="#000000" 
     android:focusable="false" /> 

    <TextView android:id="@+id/results_list_item_textview2" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_alignParentLeft="true" 
     android:layout_below="@id/results_list_item_textview1" 
     android:textSize="16sp" 
     android:textColor="@android:color/darker_gray" 
     android:focusable="false" /> 

</com.test.mycompany.Widgets.CheckableListItem> 
+0

Bạn đang kiểm tra và bỏ chọn các mục của mình ở đâu? Bạn cũng có thể thử làm việc với android: state_activited. – DroidBender

+0

Tôi không tự làm. Họ đang nhận được kiểm tra, mặc dù. Khi tôi đặt các câu lệnh tường trình trong phương thức setChecked của các mục, tôi thấy rằng nó đang được gọi. – Groppe

+0

Hãy thử 'android: longClickable = true' trên' CheckableListItem' khi u đặt bg trực tiếp .. (như được chỉ định trong Ghi chú của bạn) – Ronnie

Trả lời

0

tôi đã thay đổi và bổ sung các phương pháp này trong CheckedListItem và nó làm việc cho tôi:

@Override 
public boolean onTouchEvent(MotionEvent event) { 

    int action = event.getAction() & MotionEvent.ACTION_MASK; 
    if (action == MotionEvent.ACTION_UP) { 
     toggle(); 
    } 

    return true; 
} 

public void toggle() { 

    setChecked(!isChecked()); 
} 

private static final int[] CheckedStateSet = { android.R.attr.state_checked }; 

Có vẻ như vấn đề là trên một lần nhấp, bạn không bao giờ xử lý chuyển đổi trạng thái đã chọn của chế độ xem.

+0

Nó không làm việc cho tôi ... bây giờ không có gì xảy ra khi tôi bấm vào một mục. Đó có phải là những điều duy nhất bạn thay đổi không? Bạn có xóa bất kỳ phương pháp nào khác không? – Groppe

+0

Không, tôi đã không, nhưng tôi cũng không đặt nó trong một 'ListView'. Bạn có một trình lắng nghe trên 'ListView' để nhận tất cả các lần nhấp, vì vậy chế độ xem của bạn sẽ không bao giờ nhận được sự kiện chạm. Điều tôi đề nghị là đặt chế độ lựa chọn thành 'CHOICE_MODE_SINGLE' hoặc' CHOICE_MODE_MULTIPLE' (tùy theo bạn cần), và điều này sẽ cho phép chọn các mục danh sách. Vì vậy, sau đó bạn sẽ sử dụng 'state_selected' thay vì' state_checked' trong lớp 'CheckedListItem' của bạn. –

0

Nếu điều này chưa được giải quyết, hãy thử đưa "bố cục có thể kiểm tra" của bạn nền có thể vẽ được bằng công cụ chọn và trạng thái và định nghĩa màu trong đó. Nếu không, drawableStateChanged sẽ không làm bất cứ điều gì vì mBackground là null. (ví dụ: android: background = "@ drawable/list_selector")

Sau đó, hãy đảm bảo bạn sử dụng listview.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE) để cho phép kiểm tra nhiều mục.

Bạn không phải triển khai setOnItemClickListener để kiểm tra các mục khi chế độ chọn cài đặt tự động thực hiện. (KHÔNG thiết lập bố cục có thể kiểm tra để có thể nhấp được)

Vâng, ít nhất đây là cách tôi giải quyết của tôi.