您好,我正在处理一个使用 GeoJson 的项目,它作为要素集合存储在列表中,然后将其用于带有项目单击的回收器视图 在 Mapbox 上单击相机定位和绘制路线,它在初始列表中效果很好,但是当我尝试实现 SearchView 过滤时,我陷入了困境, 它肯定会过滤列表,但似乎没有正确更新卡位置,并且对我的相机定位和过滤结果的路线绘制方法造成了麻烦。
任何帮助将不胜感激。
以下是在我的主活动上使用搜索和OnItemClick实现的相关方法:
List<Feature> featureList = featureCollection.features();
// Retrieve and update the source designated for showing the store location icons
GeoJsonSource source = mapboxMap.getStyle().getSourceAs("store-location-source-id");
if (source != null) {
source.setGeoJson(FeatureCollection.fromFeatures(featureList));
}
if (featureList != null) {
for (int x = 0; x < featureList.size(); x++) {
Feature singleLocation = featureList.get(x);
// Get the single location's String properties to place in its map marker
String singleLocationName = singleLocation.getStringProperty("name");
String singleLocationHours = singleLocation.getStringProperty("hours");
String singleLocationDescription = singleLocation.getStringProperty("description");
String singleLocationPhoneNum = singleLocation.getStringProperty("phone");
// Add a boolean property to use for adjusting the icon of the selected store location
singleLocation.addBooleanProperty(PROPERTY_SELECTED, false);
// Get the single location's LatLng coordinates
Point singleLocationPosition = (Point) singleLocation.geometry();
// Create a new LatLng object with the Position object created above
LatLng singleLocationLatLng = new LatLng(singleLocationPosition.latitude(),
singleLocationPosition.longitude());
// Add the location to the Arraylist of locations for later use in the recyclerview
listOfIndividualLocations.add(new IndividualLocation(
singleLocationName,
singleLocationDescription,
singleLocationHours,
singleLocationPhoneNum,
singleLocationLatLng
));
// Call getInformationFromDirectionsApi() to eventually display the location's
// distance from mocked device location
getInformationFromDirectionsApi(singleLocationPosition, false, x);
}
// Add the fake device location marker to the map. In a real use case scenario,
// the Maps SDK's LocationComponent can be used to easily display and customize
// the device location's puck
addMockDeviceLocationMarkerToMap();
setUpRecyclerViewOfLocationCards(chosenTheme);
mapboxMap.addOnMapClickListener(MapActivity.this);
Toast.makeText(MapActivity.this, "Click on a card", Toast.LENGTH_SHORT).show();
// Show 3d buildings if the blue theme is being used
if (customThemeManager.getNavigationLineColor() == R.color.navigationRouteLine_blue) {
showBuildingExtrusions();
}
}
}
});
}
});
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.search_menu,menu);
MenuItem searchitem = menu.findItem(R.id.searchview);
SearchView searchView = (SearchView) searchitem.getActionView();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
styleRvAdapter.getFilter().filter(newText);
return false;
}
});
return true;
}
private boolean featureSelectStatus(int index) {
if (featureCollection == null) {
return false;
}
return featureCollection.features().get(index).getBooleanProperty(PROPERTY_SELECTED);
}
private void setSelected(int index) {
Feature feature = featureCollection.features().get(index);
setFeatureSelectState(feature, true);
refreshSource();
}
private void setFeatureSelectState(Feature feature, boolean selectedState) {
feature.properties().addProperty(PROPERTY_SELECTED, selectedState);
refreshSource();
}
private void refreshSource() {
GeoJsonSource source = mapboxMap.getStyle().getSourceAs("store-location-source-id");
if (source != null && featureCollection != null) {
source.setGeoJson(featureCollection);
}
}
private void setUpRecyclerViewOfLocationCards(int chosenTheme) {
// Initialize the recyclerview of location cards and a custom class for automatic card scrolling
locationsRecyclerView = findViewById(R.id.map_layout_rv);
locationsRecyclerView.setHasFixedSize(true);
locationsRecyclerView.setLayoutManager(new LinearLayoutManagerWithSmoothScroller(this));
styleRvAdapter = new LocationRecyclerViewAdapter(listOfIndividualLocations,
getApplicationContext(), this, chosenTheme);
locationsRecyclerView.setAdapter(styleRvAdapter);
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(locationsRecyclerView);
}
@Override
public void onItemClick(int position) {
// Get the selected individual location via its card's position in the recyclerview of cards
IndividualLocation selectedLocation = styleRvAdapter.getItem(position);
// Evaluate each Feature's "select state" to appropriately style the location's icon
List<Feature> featureList = featureCollection.features();
assert featureCollection.features() != null;
Point selectedLocationPoint = (Point) featureCollection.features().get(position).geometry();
for (int i = 0; i < featureList.size(); i++) {
if (featureList.get(i).getStringProperty("name").equals(selectedLocation.getName())) {
if (featureSelectStatus(i)) {
setFeatureSelectState(featureList.get(i), false);
} else {
setSelected(i);
}
} else {
setFeatureSelectState(featureList.get(i), false);
}
}
// Reposition the map camera target to the selected marker
if (selectedLocation != null) {
repositionMapCamera(selectedLocationPoint);
}
// Check for an internet connection before making the call to Mapbox Directions API
if (deviceHasInternetConnection()) {
// Start call to the Mapbox Directions API
if (selectedLocation != null) {
getInformationFromDirectionsApi(selectedLocationPoint, true, null);
}
} else {
Toast.makeText(this, R.string.no_internet_message, Toast.LENGTH_LONG).show();
}
}
这是我的适配器:
public class LocationRecyclerViewAdapter extends
RecyclerView.Adapter<LocationRecyclerViewAdapter.ViewHolder> implements Filterable {
private ArrayList<IndividualLocation> listOfLocations;
private ArrayList<IndividualLocation> listOfLocationsFull;
private Context context;
private int selectedTheme;
private static ClickListener clickListener;
private int upperCardSectionColor = 0;
private LocationRecyclerViewAdapter styleRv;
private int locationNameColor = 0;
private int locationAddressColor = 0;
private int locationPhoneNumColor = 0;
private int locationPhoneHeaderColor = 0;
private int locationHoursColor = 0;
private int locationHoursHeaderColor = 0;
private int locationDistanceNumColor = 0;
private int milesAbbreviationColor = 0;
public LocationRecyclerViewAdapter(List<IndividualLocation> styles,
Context context, ClickListener cardClickListener, int selectedTheme) {
this.context = context;
this.listOfLocations = (ArrayList<IndividualLocation>) styles;
this.selectedTheme = selectedTheme;
this.clickListener = cardClickListener;
listOfLocationsFull = new ArrayList<>(listOfLocations);
}
public IndividualLocation getItem(int position){
return listOfLocationsFull.get(position);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int singleRvCardToUse = R.layout.single_location_map_view_rv_card;
View itemView = LayoutInflater.from(parent.getContext()).inflate(singleRvCardToUse, parent, false);
return new ViewHolder(itemView);
}
public interface ClickListener {
void onItemClick(int position);
}
@Override
public int getItemCount() {
return listOfLocations.size();
}
@Override
public Filter getFilter(){
return listOfLocationsFilter;
}
private Filter listOfLocationsFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<IndividualLocation> filteredList = new ArrayList<>();
if(constraint == null || constraint.length() == 0 ){
filteredList.addAll(listOfLocationsFull);
}
else{
String filterpattern = constraint.toString().toLowerCase().trim();
for(IndividualLocation item : listOfLocationsFull){
if(item.getName().toLowerCase().contains(filterpattern)){
filteredList.add(item);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
listOfLocations.clear();
listOfLocations.addAll((List)results.values);
notifyDataSetChanged();
}
};
@Override
public void onBindViewHolder(ViewHolder card, int position) {
IndividualLocation locationCard = listOfLocations.get(position);
card.nameTextView.setText(locationCard.getName());
card.addressTextView.setText(locationCard.getAddress());
card.phoneNumTextView.setText(locationCard.getPhoneNum());
card.hoursTextView.setText(locationCard.getHours());
card.distanceNumberTextView.setText(locationCard.getDistance());
card.constraintUpperColorSection.setBackgroundColor(upperCardSectionColor);
card.nameTextView.setTextColor(locationNameColor);
card.phoneNumTextView.setTextColor(locationPhoneNumColor);
card.hoursTextView.setTextColor(locationHoursColor);
card.hoursHeaderTextView.setTextColor(locationHoursHeaderColor);
card.distanceNumberTextView.setTextColor(locationDistanceNumColor);
card.milesAbbreviationTextView.setTextColor(milesAbbreviationColor);
card.addressTextView.setTextColor(locationAddressColor);
card.phoneHeaderTextView.setTextColor(locationPhoneHeaderColor);
}
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView nameTextView;
TextView addressTextView;
TextView phoneNumTextView;
TextView hoursTextView;
TextView distanceNumberTextView;
TextView hoursHeaderTextView;
TextView milesAbbreviationTextView;
TextView phoneHeaderTextView;
ConstraintLayout constraintUpperColorSection;
CardView cardView;
ImageView backgroundCircleImageView;
ImageView emojiImageView;
ViewHolder(final View itemView) {
super(itemView);
nameTextView = itemView.findViewById(R.id.location_name_tv);
addressTextView = itemView.findViewById(R.id.location_description_tv);
phoneNumTextView = itemView.findViewById(R.id.location_phone_num_tv);
phoneHeaderTextView = itemView.findViewById(R.id.phone_header_tv);
hoursTextView = itemView.findViewById(R.id.location_hours_tv);
backgroundCircleImageView = itemView.findViewById(R.id.background_circle);
emojiImageView = itemView.findViewById(R.id.emoji);
constraintUpperColorSection = itemView.findViewById(R.id.constraint_upper_color);
distanceNumberTextView = itemView.findViewById(R.id.distance_num_tv);
hoursHeaderTextView = itemView.findViewById(R.id.hours_header_tv);
milesAbbreviationTextView = itemView.findViewById(R.id.miles_mi_tv);
cardView = itemView.findViewById(R.id.map_view_location_card);
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clickListener.onItemClick(getAdapterPosition());
}
});
}
@Override
public void onClick(View view) {
}
}
}
我知道已经晚了,但我希望有人能得到我的回答。
androidRecyclerView
使用从RecyclerView.Adapter
类扩展而来的适配器,该适配器需要List
或ArrayList
的数据来填充onBindViewHolder
重写方法中回收器视图中的项目。
此方法提供的position
参数是任何给定时间回收器视图中项目的相对位置,这意味着当您首次加载回收器视图时,此位置将与加载到回收器视图的完整数据的数据ArrayList
中的位置同步。
当您在回收器视图上执行搜索时,您会传入过滤结果的不同数据集,例如filteredList
,此列表将再次从 0 开始position(s)
,依此类推,这意味着当您单击一个项目时,传递给onItemClick
的位置是来自filteredResults
的新 ArrayList 的位置。 这就是导致混淆的原因,因为您从一个列表(filteredList
(中获取位置并使用它来从另一个列表(originalList
(获取数据。
一旦您进行搜索并使用新列表(filteredList
(调用notifyDataSetChanged()
,您的回收器视图位置将有所不同,并且获取点击项目位置的方法(例如viewHolder.getAdapterPosition()
(将返回您新位置,这不会有太大帮助,因为这是filteredList
的位置。
有了这种理解,我将提出一个简单但有效的解决方案,我已经测试过了。
挑战:要获得回收者的正确位置,即使搜索结果也会查看项目点击次数。
解决方案:在模型/数据集中具有唯一值,该值表示原始数据集中该数据项的正确索引。此值永远不会更改,因为它属于您的模型/数据集。就我而言,这是从MySQL Database
中获取的row
的id
.
在 XML 格式的回收器查看行项中,定义一个将容纳此 id 值的TextView
。我们将设置其android:visibility="gone"
,因为我们不一定希望用户看到它,并且它也不会占用该设置的屏幕空间。
<TextView
android:id="@+id/tvItemId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
在适配器的视图持有者中,初始化此文本视图:
tvItemId = temView.findViewById(R.id.tvItemId)
在onBindViewHolder
集合中,它的值:holder.tvItemId.setText(itemId)
在onItemClick
中,获取单击的视图,因为它包含即使在搜索时也具有原始项目 ID 的文本视图。
TextView item = view.findViewById(R.id.tvItemId)
获取该文本框的值,这是正确的项 ID,将为点击事件加载正确的数据。
int itemId = Integer.parseInt(item.getText().toString());
使用indexOf()
方法在 ArrayList 中获取正确的索引/位置
int pos = arrayList.indexOf(itemId);
去吧,施展魔法,保持动力!