Skip to content

Instantly share code, notes, and snippets.

@mkuprionis
Created October 10, 2014 13:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mkuprionis/7d9138b03e06cf379174 to your computer and use it in GitHub Desktop.
Save mkuprionis/7d9138b03e06cf379174 to your computer and use it in GitHub Desktop.
List view with sticky headers and filtering
public class BrandListFragment extends BaseFragment {
private static final String ARGS_LOCATION_SPEC = "location_spec";
private LocationSpec location;
@InjectView(R.id.progress) View progress;
@InjectView(R.id.brand_list) StickyListHeadersListView brandList;
private Subscription dataSubscription;
public static BaseFragment newInstance(LocationSpec spec) {
Bundle args = new Bundle();
args.putString(ARGS_LOCATION_SPEC, spec.serialize());
BaseFragment f = new BrandListFragment();
f.setArguments(args);
return f;
}
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.brand_list, container, false);
ButterKnife.inject(this, v);
View header = inflater.inflate(R.layout.brand_list_search, null);
brandList.addHeaderView(header);
dataSubscription = dataService.onData((navTree, brands) -> {
location = LocationSpec.deserialize(navTree, getArguments().getString(ARGS_LOCATION_SPEC));
if (progress != null) {
BrandAdapter adapter = new BrandAdapter(getActivity(), brands.getByGender(location.getGender()), location, navigator, api);
EditText searchInput = findById(header, R.id.brand_search);
searchInput.setHint(getString(R.string.brand_list_search_hint, brands.getByGender(location.getGender()).size()));
searchInput.addTextChangedListener(new TextWatcher() {
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.getFilter().filter(s);
}
@Override public void afterTextChanged(Editable s) {}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
});
progress.setVisibility(View.GONE);
brandList.setVisibility(View.VISIBLE);
brandList.setAdapter(adapter);
}
});
return v;
}
@Override public void onDestroyView() {
hideKeyboard();
super.onDestroyView();
dataSubscription.unsubscribe();
}
private static class BrandAdapter extends BindableAdapter<NavigationItem> implements StickyListHeadersAdapter, Filterable {
private LocationSpec defaultLocation;
private Navigator navigator;
private Bpi api;
private List<NavigationItem> brands;
private List<NavigationItem> favorites;
private List<NavigationItem> popular;
private List<NavigationItem> filtered;
private boolean isFilterOn;
private OnClickListener itemClickListener = (view) -> {
NavigationItem brand = (NavigationItem) view.getTag();
LocationSpec location = new LocationSpec(defaultLocation);
location.subNavItem(brand);
navigator.goToProductListing(location);
};
private OnClickListener favoritesBtnClickListener = (view) -> {
NavigationItem brand = (NavigationItem) view.getTag();
Callback<NavigationItem> cb = new Callback<NavigationItem>() {
@Override public void success(NavigationItem navigationItem, Response response) {}
@Override public void failure(RetrofitError error) {}
};
if (brand.isFavorite()) {
api.unfavoriteBrand(brand.getBrandId(), cb);
favorites.remove(brand);
} else {
api.favoriteBrand(brand.getBrandId(), cb);
favorites.add(brand);
}
brand.setFavorite(!brand.isFavorite());
view.setActivated(brand.isFavorite());
notifyDataSetChanged();
};
BrandAdapter(Context context, List<NavigationItem> brands, LocationSpec defaultLocation, Navigator navigator, Api api) {
super(context);
this.brands = brands;
this.navigator = navigator;
this.api = api;
this.defaultLocation = defaultLocation;
favorites = new ArrayList<>();
popular = new ArrayList<>();
for (NavigationItem b : brands) {
if (b.isFavorite()) {
favorites.add(b);
}
if (b.isPopular()) {
popular.add(b);
}
}
}
@Override public int getCount() {
return !isFilterOn
? brands.size() + popular.size() + favorites.size()
: filtered.size();
}
@Override public NavigationItem getItem(int position) {
if (isFilterOn) {
return filtered.get(position);
} else if (position < favorites.size()) {
return favorites.get(position);
} else if (position < favorites.size() + popular.size()) {
return popular.get(position - favorites.size());
} else {
return brands.get(position - favorites.size() - popular.size());
}
}
@Override public long getItemId(int position) {
// A bit lame to make ids unique this way, but should do the job
if (isFilterOn) {
return getItem(position).getId();
} else if (position < favorites.size()) {
return getItem(position).getId() * 3;
} else if (position < favorites.size() + popular.size()) {
return getItem(position).getId() * 2;
} else {
return getItem(position).getId();
}
}
@Override public View newView(LayoutInflater inflater, int position, ViewGroup container) {
View v = inflater.inflate(R.layout.brand_list_item, container, false);
BrandItemHolder holder = new BrandItemHolder();
ButterKnife.inject(holder, v);
v.setTag(holder);
holder.title.setOnClickListener(itemClickListener);
holder.favoriteBtn.setOnClickListener(favoritesBtnClickListener);
return v;
}
@Override public void bindView(NavigationItem item, int position, View view) {
BrandItemHolder holder = (BrandItemHolder) view.getTag();
holder.brand = getItem(position);
holder.title.setText(holder.brand.getTitle());
holder.title.setTag(holder.brand);
holder.favoriteBtn.setActivated(holder.brand.isFavorite());
holder.favoriteBtn.setTag(holder.brand);
}
@Override public boolean areAllItemsEnabled() {
return false;
}
@Override public View getHeaderView(int position, View view, ViewGroup viewGroup) {
View v = (view != null)
? view
: LayoutInflater.from(getContext()).inflate(R.layout.brand_list_header, viewGroup, false);
TextView title = findById(v, R.id.header_title);
// <...>
title.getLayoutParams().height = !isFilterOn
? getContext().getResources().getDimensionPixelSize(R.dimen.margin_4x)
: 0;
title.setVisibility(
isFilterOn ? View.GONE : View.VISIBLE
);
return v;
}
@Override public long getHeaderId(int position) {
if (isFilterOn) {
return 1;
} else if (position < favorites.size()) {
return 2;
} else if (position < favorites.size() + popular.size()) {
return 3;
} else {
return getItem(position).getTitle().charAt(0);
}
}
@Override public Filter getFilter() {
Filter filter = new Filter() {
@Override protected void publishResults(CharSequence constraint, FilterResults results) {
if (constraint.length() == 0) {
isFilterOn = false;
} else {
isFilterOn = true;
filtered = (List<NavigationItem>) results.values;
}
notifyDataSetChanged();
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<NavigationItem> filteredBrands = new ArrayList<>();
if (constraint.length() > 0) {
constraint = constraint.toString().toLowerCase();
for (NavigationItem brand : brands) {
if (brand.getTitle().toLowerCase().startsWith(constraint.toString())) {
filteredBrands.add(brand);
}
}
results.count = filteredBrands.size();
results.values = filteredBrands;
}
return results;
}
};
return filter;
}
}
static class BrandItemHolder {
NavigationItem brand;
@InjectView(R.id.brand_title) TextView title;
@InjectView(R.id.brand_favorite_toggle) View favoriteBtn;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment