5

My question is about filtering in Android. Both Ebay and Foursquare have what looks to be a dialog fragment that slides in from the right. Within this fragment are several nested listviews that open and expand to fill the fragment. When a nested listview is opened a back arrow appears at the top as well.

Once filters are selected they show as the listview text under each category name within the top level fragment. (see the different colored text for "Disney" and "HTC" in the ebay screenshot)

I'm wondering is there a library to implement nested listviews like this? Is this considered best practice for filtering search results?

I've included screenshots to hopefully show what i'm talking about.

Ebay Filter Fragment top level Ebay Filter nested listview Foursquare Filter nested listview Foursquare Filter Fragment top level

user2110291
  • 141
  • 8

2 Answers2

0

You able to do this via android Expandable ListView with custom cell.

Here is one example about Expandable ListView with custom cell.

Thanks

Md Abdul Gafur
  • 6,120
  • 2
  • 25
  • 37
0

May can achieve by android Recyclerview and some logic. I have handled a similar situation in the following manner.

FilterViewAdapter - Adapter of Recycler.
FilterListPresenter - class used to separate business logic.

Eight different type of cells used to generate filter list dynamically.

   static final int FILTER_HEADER = 0;
    static final int FILTER_DROPDOWN_HEADER = 1;
    static final int FILTER_DROPDOWN_ITEM_RADIO = 2;
    static final int FILTER_DROPDOWN_ITEM_CHECKBOX = 3;
    static final int FILTER_DROPDOWN_ITEM_RADIO_LAST = 22;
    static final int FILTER_DROPDOWN_ITEM_CHECKBOX_LAST = 33;
    static final int FILTER_DUEDATE = 4;
    static final int FILTER_PROGRESS = 5;

getItemViewType() provide the type of cell based on the logic. Example if a filter is opened then provide a different type of cell than usual filter header.

@Override
    public int getItemViewType(int position) {
        return filterListPresenter.getItemViewType(position);
    }

Find my Adapter class

public class FilterViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements FilterListPresenter.AdapterCallBacks {


    FilterListPresenter filterListPresenter;
    Context context;
    public FilterViewAdapter(Context context,   FilterListPresenter filterListPresenter) {
        this.filterListPresenter = filterListPresenter;
        this.context = context;
        filterListPresenter.setAdapterCallback(this);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(filterListPresenter.getLayoutForView(viewType), parent, false);
        return filterListPresenter.getViewHolder(view,viewType);
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
      filterListPresenter.onBindCallLogRowViewAtPosition(position,holder,context);
    }

    @Override
    public int getItemViewType(int position) {
        return filterListPresenter.getItemViewType(position);
    }

    @Override
    public int getItemCount() {
        return filterListPresenter.getRowsCount();
    }


    @Override
    public void notifyDataSetChanged1() {
        notifyDataSetChanged();
    }
}

Find my Presenter class

public class FilterListPresenter<T extends RecyclerView.ViewHolder> implements FilterListener {

    static final int FILTER_HEADER = 0;
    static final int FILTER_DROPDOWN_HEADER = 1;
    static final int FILTER_DROPDOWN_ITEM_RADIO = 2;
    static final int FILTER_DROPDOWN_ITEM_CHECKBOX = 3;
    static final int FILTER_DROPDOWN_ITEM_RADIO_LAST = 22;
    static final int FILTER_DROPDOWN_ITEM_CHECKBOX_LAST = 33;
    static final int FILTER_DUEDATE = 4;
    static final int FILTER_PROGRESS = 5;
    Context context;
    SessionData sessionData;
    int openPosition = -1;
    List<FIlterData> fIlterDatas ;

    int numberOfRows = 5;
    String[] filterNAmes = {"Checkin Template", "Employee", "Due Date", "Percentage of progress", "Status"}; //Main filters
    int[] filterICons = {R.drawable.outline_view_agenda_black_24,
            R.drawable.outline_person_black_24 ,
            R.drawable.outline_calendar_today_black_24,
            R.drawable.outline_trending_up_black_24,
            R.drawable.outline_check_circle_black_24};
    private String checkBoxValue;

    public FilterListPresenter(Context context, SessionData sessionData) {
        this.context = context;
        this.sessionData  = sessionData;
        initilizeData(sessionData);
    }

    private void initilizeData(SessionData sessionData) {
        fIlterDatas = new ArrayList<>();
     //add data

    }


    public void onBindCallLogRowViewAtPosition(int position, T rowView, Context context) {
        int itemViewType = getItemViewType(position);
        switch (itemViewType) {
            case FILTER_HEADER:
                FilterHeaderHolder filterHeaderHolder = (FilterHeaderHolder) rowView;
                filterHeaderHolder.setListener(this);
                filterHeaderHolder.setTitle(getFilterTitle(position));
                filterHeaderHolder.setImageView(getIcon(position), context);
                break;
            case FILTER_DROPDOWN_HEADER:
                //add logic for each type cell
                break;
            case FILTER_DROPDOWN_ITEM_CHECKBOX:
             case FILTER_DROPDOWN_ITEM_CHECKBOX_LAST:
                break;
            case FILTER_DROPDOWN_ITEM_RADIO:
            case FILTER_DROPDOWN_ITEM_RADIO_LAST:
                break;
            case FILTER_PROGRESS:

                break;

            case FILTER_DUEDATE:

                break;

        }

    }

    private int getIcon(int position) {
        if((openPosition == -1)  || (position<=openPosition))
            return filterICons[position];
        else
            return filterICons[position - getExtraRowCountForFIlter(openPosition)];
    }

    private String getFilterTitle(int position) {
        if((openPosition == -1)  || (position<=openPosition))
            return filterNAmes[position];
        else
            return filterNAmes[position - getExtraRowCountForFIlter(openPosition)];
    }


    public int getRowsCount() {
        if(openPosition == -1) return numberOfRows;
         else return numberOfRows+ getExtraRowCountForFIlter(openPosition);
    }

    private int getExtraRowCountForFIlter(int position) {
        switch (position){  //Additional one for header
            case 0: return ((List<CheckinTemplate>) fIlterDatas.get(0).getFilterData()).size()+1;
            case 1: try{return ((List<Employee>) fIlterDatas.get(1).getFilterData()).size()+1;}catch (Exception exc){
                         return 0;
                       }
            case 4: return  ((List<Status>) fIlterDatas.get(4).getFilterData()).size()+1;
            default: return 1;
        }
    }

    public int getItemViewType(int position) {  //Complex logic to determine my cell type
      if(openPosition == -1  || (position<=openPosition))
          return FILTER_HEADER;
      else {
          int extraRowsForOpenFilter = getExtraRowCountForFIlter(openPosition);
          if(position > (openPosition+extraRowsForOpenFilter))
              return FILTER_HEADER;

          switch (openPosition){
              case 0:
                  if(openPosition+1 == position)  return FILTER_DROPDOWN_HEADER;
                  else if(openPosition+extraRowsForOpenFilter == position)  return FILTER_DROPDOWN_ITEM_RADIO_LAST;
                  else return FILTER_DROPDOWN_ITEM_RADIO;
              case 1:  if(openPosition+1 == position)  return FILTER_DROPDOWN_HEADER;
                       else if(openPosition+extraRowsForOpenFilter == position)  return FILTER_DROPDOWN_ITEM_CHECKBOX_LAST;
                       else return FILTER_DROPDOWN_ITEM_CHECKBOX;
              case 4:
                  if(openPosition+1 == position)  return FILTER_DROPDOWN_HEADER;
                  else if(openPosition+extraRowsForOpenFilter == position)  return FILTER_DROPDOWN_ITEM_CHECKBOX_LAST;
                   else return FILTER_DROPDOWN_ITEM_CHECKBOX;
              case 2:
                  return FILTER_DUEDATE;
              case 3:
                  return FILTER_PROGRESS;
              default: return FILTER_HEADER;
          }
      }
    }

    public int getLayoutForView(int viewType) {
        switch (viewType) {
            case FILTER_HEADER:
                return R.layout.a_filter_list_item_header;
            case FILTER_DROPDOWN_HEADER:
                return R.layout.a_filter_list_item_dropdown_header;
            case FILTER_DROPDOWN_ITEM_RADIO:
                return R.layout.a_filter_list_item_dropdown_item_radio;
            case FILTER_DROPDOWN_ITEM_RADIO_LAST:
                return R.layout.a_filter_list_item_dropdown_item_radio_last;
            case FILTER_DROPDOWN_ITEM_CHECKBOX:
                return R.layout.a_filter_list_item_dropdown_item_chekbox;
            case FILTER_DROPDOWN_ITEM_CHECKBOX_LAST:
                return R.layout.a_filter_list_item_dropdown_item_chekbox_last;
            case FILTER_DUEDATE:
                return R.layout.a_filter_list_item_duedate;
            case FILTER_PROGRESS:
                return R.layout.a_filter_list_item_progress;
        }
        return FILTER_HEADER;
    }

    @Override
    public void onFilterItemClick(int position, int viewTypeId) {
        switch (viewTypeId) {
            case FILTER_HEADER:
                if(openPosition!=-1) minimizeFilters();
                else {
                    openPosition = position;
                    adapterCallback.notifyDataSetChanged1();
                }

            case FILTER_DROPDOWN_HEADER:

                break;
            case FILTER_DROPDOWN_ITEM_RADIO:
            case FILTER_DROPDOWN_ITEM_RADIO_LAST:


                break;
            case FILTER_DROPDOWN_ITEM_CHECKBOX:
            case FILTER_DROPDOWN_ITEM_CHECKBOX_LAST:

                break;
            case FILTER_DUEDATE:

                break;
            case FILTER_PROGRESS:

                break;

        }
    }

    @Override
    public RecyclerView.ViewHolder getViewHolder(View view, int viewType) {
        switch (viewType) {
            case FILTER_HEADER:
                return new FilterHeaderHolder(view);
            case FILTER_DROPDOWN_HEADER:
                return new FilterListDropdownHeader(view);
            case FILTER_DROPDOWN_ITEM_RADIO:
            case FILTER_DROPDOWN_ITEM_RADIO_LAST:
                return new FilterListItemRadioButton(view);
            case FILTER_DROPDOWN_ITEM_CHECKBOX:
            case FILTER_DROPDOWN_ITEM_CHECKBOX_LAST:
                return new FilterListItemCheckBox(view);
            case FILTER_DUEDATE:
                return new FilterListItemDueDate(view);
            case FILTER_PROGRESS:
                return new FilterListItemProgress(view);
            default:  return new FilterHeaderHolder(view);
        }
    }

    private void minimizeFilters() {
        openPosition = -1;
        adapterCallback.notifyDataSetChanged1();
    }

    AdapterCallBacks adapterCallback;
    public void setAdapterCallback(FilterViewAdapter adapterCallback) {
        this.adapterCallback = adapterCallback;
    }

    public String getDropdownHeader() {
        if( ((List<?>) fIlterDatas.get(openPosition).getFilterData()).get(0) instanceof CheckinTemplate){
            return "Select Template";
        }else if( ((List<?>) fIlterDatas.get(openPosition).getFilterData()).get(0) instanceof Employee){
            return "Select Employees";
        }else if( ((List<?>) fIlterDatas.get(openPosition).getFilterData()).get(0) instanceof Status){
            return "Select status";
        }
        return "";
    }

    public String getCheckBoxValue(int pos) {
        if( ((List<?>) fIlterDatas.get(openPosition).getFilterData()).get(0) instanceof CheckinTemplate){
            return ((List<CheckinTemplate>) fIlterDatas.get(openPosition).getFilterData()).get(pos).getName();
        }else if( ((List<?>) fIlterDatas.get(openPosition).getFilterData()).get(0) instanceof Employee){
            return ((List<Employee>) fIlterDatas.get(openPosition).getFilterData()).get(pos).getEmpName();
        }else if( ((List<?>) fIlterDatas.get(openPosition).getFilterData()).get(0) instanceof Status){
            return ((List<Status>) fIlterDatas.get(openPosition).getFilterData()).get(pos).getName();
        }
        return "";
    }

    public interface AdapterCallBacks{
        void notifyDataSetChanged1();
    }
}

Similar way you can construct your filter. Add your thoughts.

Ebin Joy
  • 1,478
  • 2
  • 14
  • 34