我沒有找到一個解決方案,我原來的問題,但我想出了一個更好的方法來整體情況。我不知道Android中有可用的ExpandableListView
。這基本上是一個ListView,但是這些項目被劃分爲可擴展和可摺疊的Groups和它們的Childs,正是我想要的。
這裏是我一起工作的過濾器和組實現了它:
因此,要開始,這是我的主要佈局文件。請注意,我正在使用片段,這就是爲什麼代碼在獲取上下文方面有點不同。雖然組件的功能保持不變。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/fragment_data_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="@string/data_search_hint"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" />
<ExpandableListView
android:id="@+id/fragment_data_expandable_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:groupIndicator="@null" />
</LinearLayout>
您還需要兩個用於頁眉/組項目和子項目的佈局文件。我的標題項目有一個顯示類別名稱的TextView和一個顯示+或 - 以顯示類別是否摺疊或展開的ImageView。
這裏是我的頭佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:descendantFocusability="blocksDescendants" >
<TextView
android:id="@+id/fragment_data_list_view_category"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:gravity="start"
android:textStyle="bold"
android:textSize="18sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:textColor="@android:color/primary_text_light"
android:text="@string/placeholder_header_listview"
android:maxLines="1"
android:ellipsize="end" />
<ImageView
android:id="@+id/fragment_data_list_view_category_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_gravity="end"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:contentDescription="@string/content_description_list_view_header"
android:src="@drawable/ic_remove_black_24dp"
android:tag="maximized"/>
</RelativeLayout>
android:descendantFocusability="blocksDescendants"
修正了一個錯誤,當我試圖設置一個onItemClickListener屬性。如果你有這個問題,請嘗試使用RelativeLayout作爲你的孩子佈局,如果你還沒有。它修復了我的問題,onClickItemListener沒有用LinearLayout執行。
這裏是爲孩子的項目我的佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:descendantFocusability="blocksDescendants" >
<TextView
android:id="@+id/fragment_data_list_view_carrier_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/placeholder_item_listview"
android:textSize="18sp"
android:textStyle="normal"
android:textColor="@android:color/primary_text_light"
android:maxLines="1"
android:ellipsize="end" />
</RelativeLayout>
下面的代碼是從我的片段類,它處理所有的邏輯爲ExpandableListView:
public class Fragment_Data extends Fragment {
private Context mContext;
private ExpandableListView expandableListView;
private List<String> categories_list;
private HashMap<String, List<Carrier>> carriers_list;
private DataExpandableListAdapter adapter;
private DatabaseHelper dbHelper;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getActivity().setTitle(R.string.nav_item_data);
}
這第一部分顯示所需變量的聲明和必要的方法onViewCreated
。 Carrier
類是一個具有名稱,類別等屬性的自定義對象。 DatabaseHelper
也是一個自定義類,它處理我的數據庫並獲取所有數據,並將其輸入Carrier對象。你當然可以使用任何你喜歡的數據類型。
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_data_layout, container, false);
mContext = getContext();
expandableListView = (ExpandableListView) view.findViewById(R.id.fragment_data_expandable_list_view);
dbHelper = new DatabaseHelper(mContext, null, null, 1);
adapter = new DataExpandableListAdapter(mContext, categories_list, carriers_list);
displayList();
expandAllGroups();
EditText searchEditText = (EditText) view.findViewById(R.id.fragment_data_search);
searchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.filterData(s.toString());
expandAllGroups();
}
@Override
public void afterTextChanged(Editable s) {
}
});
expandableListView.setOnItemLongClickListener(deleteSelectedItem);
expandableListView.setOnChildClickListener(editSelectedItem);
return view;
}
與所有喜歡設置轉接器,膨脹的佈局和設置的項目和搜索領域的onTextChange事件的onclick事件的重要的東西onCreate
方法處理。
private void expandAllGroups() {
for(int i = 0; i < adapter.getGroupCount(); i++) {
expandableListView.expandGroup(i);
}
}
private void displayList() {
prepareListData();
adapter = new DataExpandableListAdapter(mContext, categories_list, carriers_list);
expandableListView.setAdapter(adapter);
expandAllGroups();
}
private void prepareListData() {
categories_list = new ArrayList<>();
carriers_list = new HashMap<>();
categories_list = dbHelper.getCategoryList();
for(int i = 0; i < categories_list.size(); i++) {
List<Carrier> carrierList = dbHelper.getCarriersWithCategory(categories_list.get(i));
carriers_list.put(categories_list.get(i), carrierList);
}
}
隨着expandAllGroups()
你可以簡單地擴大所有羣體,因爲他們在默認情況下崩潰。 displayList()
只是爲ExpandableListView設置適配器,並調用prepareListData()
,這將填充類別(組)列表和載體(子)列表。請注意,子List是一個散列表,其中的鍵是類別,值本身就是載體列表,因此Adapter知道哪些子項屬於哪個父項。
下面是Adapter
代碼:
class DataExpandableListAdapter extends BaseExpandableListAdapter {
private Context mContext;
private List<String> list_categories = new ArrayList<>();
private List<String> list_categories_original = new ArrayList<>();
private HashMap<String, List<Carrier>> list_carriers = new HashMap<>();
private HashMap<String, List<Carrier>> list_carriers_original = new HashMap<>();
DataExpandableListAdapter(Context context, List<String> categories, HashMap<String, List<Carrier>> carriers) {
this.mContext = context;
this.list_categories = categories;
this.list_categories_original = categories;
this.list_carriers = carriers;
this.list_carriers_original = carriers;
}
您需要同時擁有原始的名單的副本,如果你想使用過濾。這用於在搜索查詢爲空或再次或完全不同時恢復所有數據。該過濾器將刪除與原始列表不匹配的所有項目。
@Override
public int getGroupCount() {
return this.list_categories.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return this.list_carriers.get(this.list_categories.get(groupPosition)).size();
}
@Override
public Object getGroup(int groupPosition) {
return this.list_categories.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return this.list_carriers.get(this.list_categories.get(groupPosition)).get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
當您展開BaseExpandableListAdapter
時,需要覆蓋這些方法。根據您的數據,您可以用類似的東西替換所有return null;
語句。
@SuppressLint("InflateParams")
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
String headerTitle = (String) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.listview_header_data_layout, null);
}
TextView lblListHeader = (TextView) convertView.findViewById(R.id.fragment_data_list_view_category);
lblListHeader.setText(headerTitle);
ImageView expandIcon = (ImageView) convertView.findViewById(R.id.fragment_data_list_view_category_icon);
if(isExpanded) {
expandIcon.setImageResource(R.drawable.ic_remove_black_24dp);
} else {
expandIcon.setImageResource(R.drawable.ic_add_black_24dp);
}
return convertView;
}
這種覆蓋方法簡單地膨脹爲每個頭/組/類別項的佈局,並將其設置文本和圖像取決於該組的狀態下,如果它的摺疊或展開。
@SuppressLint("InflateParams")
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
final String carrierName = ((Carrier)getChild(groupPosition, childPosition)).get_name();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.listview_item_data_layout, null);
}
TextView txtListChild = (TextView) convertView.findViewById(R.id.fragment_data_list_view_carrier_name);
txtListChild.setText(carrierName);
return convertView;
}
與子項目相同的東西。
現在終於到篩選: 我使用此自定義方法篩選出我需要匹配搜索查詢的所有項目。請記住,每次EditText的文本更改時都會調用此方法。
void filterData(String query) {
query = query.toLowerCase();
list_categories = new ArrayList<>();
list_carriers = new HashMap<>();
DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, null, 1);
if(query.trim().isEmpty()) {
list_categories = new ArrayList<>(list_categories_original);
list_carriers = new HashMap<>(list_carriers_original);
notifyDataSetInvalidated();
}
else {
//Filter all data with the given search query. Yes, it's complicated
List<String> new_categories_list = new ArrayList<>();
HashMap<String, List<Carrier>> new_carriers_list = new HashMap<>();
List<String> all_categories_list = dbHelper.getCategoryList();
for(int i = 0; i < all_categories_list.size(); i++) {
List<Carrier> carriersWithCategoryList = dbHelper.getCarriersWithCategory(all_categories_list.get(i));
List<Carrier> matchingCarriersInCategory = new ArrayList<>();
for(Carrier carrierInCategory : carriersWithCategoryList) {
if(carrierInCategory.get_name().toLowerCase().contains(query)) {
matchingCarriersInCategory.add(carrierInCategory);
if(!new_categories_list.contains(all_categories_list.get(i))) {
new_categories_list.add(all_categories_list.get(i));
}
}
}
new_carriers_list.put(all_categories_list.get(i), matchingCarriersInCategory);
}
if(new_categories_list.size() > 0 && new_carriers_list.size() > 0) {
list_categories.clear();
list_categories.addAll(new_categories_list);
list_carriers.clear();
list_carriers.putAll(new_carriers_list);
}
notifyDataSetChanged();
}
}`
這可能會很混亂,但由於我的數據結構,它在我的情況下需要非常複雜。你的情況可能會更容易。
這基本上是做什麼,它首先檢查搜索查詢是否爲空。如果它是空的,它將這兩個列表重置爲我在構造函數中分配的「備份」列表。然後我撥打notifyDataSetInvalidated();
告訴適配器,它的內容將被重新填充。它可能適用於notifyDataSetChanged();
,我沒有對此進行測試,但應該將原始列表恢復到原來的狀態。
現在,如果搜索查詢不爲空,我會遍歷每個類別並查看該特定類別是否有與搜索查詢匹配的任何項目。如果是這種情況,該項目被添加到新的子列表中,並且它的類別/父項目也將被添加到新的父列表中,如果它尚未存在的話。
最後但並非最不重要的是,該方法檢查兩個列表是否都不爲空。如果它們不是空的,則清空原始列表,並將新的過濾數據放入,並通過調用notifyDataSetChanged();
來通知適配器。我希望這能幫助任何人。
move adapter.getFilter()。filter(s.toString());裏面onTextChanged() –
我改變了一點,以測試它是否有所作爲,但它不 –
使用s.toString()而不是searchEditText.getText() –