溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

支持多項選擇的ExpandableListView怎么用

發(fā)布時間:2021-09-10 13:45:48 來源:億速云 閱讀:133 作者:小新 欄目:移動開發(fā)

這篇文章主要介紹支持多項選擇的ExpandableListView怎么用,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

目標(biāo)(需求):

1. 創(chuàng)建一個可展開可收縮的列表;

2. 其列表項包含多個checkable的部件,當(dāng)選擇某一行時,該行包含的checkable的部件需要作出相應(yīng)的變化;

3. 可以選擇多個列表項,并且這些列表項可被讀出

結(jié)果圖:

支持多項選擇的ExpandableListView怎么用

實現(xiàn):

1. 創(chuàng)建主layout用于規(guī)劃列表顯示。對于具體的列表項,為了實現(xiàn)的方便我們也創(chuàng)建一個layout文件。

<?xml version="1.0" encoding="utf-8"?> 
<com.home.mymultichecklistview.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:id="@+id/layout" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="horizontal" > 
 <com.home.mymultichecklistview.CheckableTextView  
   android:id="@+id/item" 
   android:layout_width="match_parent" 
   android:layout_height="wrap_content" 
   android:layout_marginTop="6dip" 
    
   android:layout_weight="1" 
 /> 
   
 <com.home.mymultichecklistview.InertCheckBox 
   android:id="@+id/checkbox" 
   android:layout_width="wrap_content" 
   android:layout_height="wrap_content" 
   android:layout_marginRight="5dp" 
   android:maxWidth="40dp"  
   android:maxHeight="40dp"  
   android:focusable="false"  
   android:layout_gravity="right" 
   android:button="@drawable/checkbox" 
 />  
 
  
</com.home.mymultichecklistview.CheckableLinearLayout>

2. 類似ListView,ExpandableListView也是通過Adapter來管理其包含的各種元素和操作,這里我們創(chuàng)建一個擴展自BaseExpandableListAdapter的Adapter。與ListView不同的是,ExpandableListAdapter要渲染實現(xiàn)兩級View(Group級和列表項級)的操作。它通過getGroupView()渲染Group項,通過getChildView()渲染列表子項。

@Override 
  public View getGroupView(int groupPosition, boolean isExpanded, 
      View convertView, ViewGroup parent) { 
    View groupView = convertView; 
    if (groupView == null) { 
      groupView = new TextView(context); 
      ((TextView)groupView).setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); 
      groupView.setPadding(50,0,0,0); 
    } 
    ((TextView)groupView).setText(groupData[groupPosition]); 
    ((TextView)groupView).setTextColor(context.getResources().getColor(R.color.fgcolor)); 
     
    return groupView; 
  } 
 
  @Override 
  public View getChildView(final int groupPosition, final int childPosition, 
      boolean isLastChild, View convertView, ViewGroup parent) { 
    View itemView = convertView; 
    final ViewHolder vh; 
    if (itemView == null) { 
      LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      itemView = inflater.inflate(R.layout.item_view, null); 
       
      vh = new ViewHolder(); 
      vh.layout = (CheckableLinearLayout)itemView.findViewById(R.id.layout); 
      vh.item = (TextView)itemView.findViewById(R.id.item); 
      itemView.setTag(vh); 
    } else { 
      vh = (ViewHolder)itemView.getTag(); 
    } 
    vh.item.setText(itemData[groupPosition][childPosition]); 
    final ExpandableListView listView = ((ExpandableListView)((MainActivity)context).findViewById(R.id.list)); 
    final int position = listView.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition)); 
    listView.setItemChecked(position, checkedState[groupPosition][childPosition]); 
    vh.layout.setOnClickListener(new OnClickListener() { 
 
      @Override 
      public void onClick(View v) { 
        ((CheckableLinearLayout)v).toggle(); 
        checkedState[groupPosition][childPosition] = !checkedState[groupPosition][childPosition];  
        listView.setItemChecked(position, ((CheckableLinearLayout)v).isChecked()); 
      } 
    }); 
    return itemView; 
  }

3. 為每一列表子項容器創(chuàng)建OnClickListener監(jiān)聽鼠標(biāo)的點擊事件。在這里要注意,由于列表子項包含了CheckBox,所以為了使點擊事件不要被CheckBox捕獲,我們需要創(chuàng)建一個擴展自CheckBox的類來屏蔽鼠標(biāo)和鍵盤事件。同時,需要在這個容器里搜索其包含的checkable的部件并將check操作傳給這些部件。

Adapter中的方法getChildView()需要實現(xiàn)鼠標(biāo)點擊監(jiān)聽器:

public View getChildView(final int groupPosition, final int childPosition, 
      boolean isLastChild, View convertView, ViewGroup parent) { 
  View itemView = convertView; 
  final ViewHolder vh; 
... 
 
    final int position = listView.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition)); 
    listView.setItemChecked(position, checkedState[groupPosition][childPosition]); 
  vh.layout.setOnClickListener(new OnClickListener() { 
 
  @Override 
  public void onClick(View v) { 
    ((CheckableLinearLayout)v).toggle(); 
    checkedState[groupPosition][childPosition] = !checkedState[groupPosition][childPosition];  
     listView.setItemChecked(position, ((CheckableLinearLayout)v).isChecked()); 
  } 
 }); 
 return itemView; 
}

擴展自CheckBox的InertCheckBox需要屏蔽鍵盤和鼠標(biāo)事件

public class InertCheckBox extends CheckBox { 
  @Override 
  public boolean onKeyDown(int keyCode, KeyEvent event) { 
  //直接返回false 
  return false; 
  } 
 
  @Override 
  public boolean onKeyUp(int keyCode, KeyEvent event) { 
  //直接返回false 
  return false; 
} 
 
  @Override 
  public boolean onTouchEvent(MotionEvent event) { 
  //直接返回false 
  return false; 
  } 
 
   @Override 
   public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { 
  //直接返回false 
  return false; 
  ... 
}

列表項容器需要實現(xiàn)Checkable接口并且把check操作傳遞給其checkable的子部件

public class CheckableLinearLayout extends LinearLayout implements Checkable { 
... 
  @Override 
  public void setChecked(boolean checked) { 
  this.isChecked = checked; 
  for (Checkable view : checkableViews) { 
   view.setChecked(checked); 
   } 
  } 
   
  @Override 
  public boolean isChecked() { 
   return this.isChecked; 
  } 
 
  @Override 
  public void toggle() { 
   isChecked = !isChecked; 
   for (Checkable view : checkableViews)    { 
  view.toggle(); 
   } 
  } 
 
  @Override 
  protected void onFinishInflate() { 
  super.onFinishInflate(); 
     
    for (int i=0; i<this.getChildCount(); i++) { 
  findCheckableChild(this.getChildAt(i)); 
  } 
 } 
 
  private void findCheckableChild(View child) { 
  if (child instanceof Checkable) { 
    checkableViews.add((Checkable)child); 
  } 
     
  if (child instanceof ViewGroup) { 
      for (int i=0; i<((ViewGroup)child).getChildCount(); i++) { 
    findCheckableChild(((ViewGroup) child).getChildAt(i)); 
    } 
  } 
  } 
... 
}

開發(fā)中遇到的問題:

1. 渲染后的child view類似于放在一個cache中,下一次再通過convertView取時,由于Group的收縮或擴展操作會隱藏/顯示一些child view,導(dǎo)致某一child View的flat position發(fā)生變化,獲取到的convertView不是原來希望獲取的view。所以,每次獲取到view后都需要對其內(nèi)容重新設(shè)置(比如設(shè)置正確文本,設(shè)置監(jiān)聽器等)

2. check的狀態(tài)設(shè)置很tricky。我開始認(rèn)為直接在監(jiān)聽器中調(diào)用容器的toggle()方法即可。結(jié)果發(fā)現(xiàn)一旦某個group做了expand或collapse操作后,所有列表項的check狀態(tài)全沒了。后來發(fā)現(xiàn)原來group做了expand/collapse操作后,ListView會對其所有子項重新設(shè)置check狀態(tài),而check狀態(tài)的值是存在ListView的一個SparseBooleanArray表里(mCheckStates)。由于沒有對這個表進行設(shè)置,所以一刷新check狀態(tài)就全丟了。并且由于這個表的key是基于拉平后所有可見的列表項的位置定的,當(dāng)group擴展或收縮后,同一個列表項,它拉平后的位置還會變化。所以,為了解決這個問題,我在adapter里增加了一個二維表用于記錄每一列表項的check狀態(tài)。在執(zhí)行 listView的setItemChecked函數(shù)時,其check狀態(tài)是從這個自己創(chuàng)建的表中讀出的,不能通過ListView的mCheckStates來讀。這個我認(rèn)為是ExpandableListView的一個缺陷。

遺留的已知問題:

我使用了@drawable/checkbox 來定義checkbox check 和uncheck時的圖片,但當(dāng)checkbox被check上時,這個checked的圖片沒有生效。不知道為什么,還需要進一步debug.

以上是“支持多項選擇的ExpandableListView怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI