我没有找到一个解决方案,我原来的问题,但我想出了一个更好的方法来整体情况。我不知道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() –