Forráskód Böngészése

Merge branch 'develop' of https://github.com/swict/LifePlusAndroid into develop

Hasemi 6 éve
szülő
commit
9d939193e5

+ 13 - 60
app/src/main/java/kr/co/zumo/app/lifeplus/view/custom/CustomSquareArrowCheckBox.java

@@ -2,12 +2,8 @@ package kr.co.zumo.app.lifeplus.view.custom;
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.support.annotation.StringRes;
-import android.support.constraint.ConstraintLayout;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
-import android.widget.CheckBox;
-import android.widget.Checkable;
 
 import kr.co.zumo.app.R;
 import kr.co.zumo.app.lifeplus.util.TextUtil;
@@ -22,96 +18,53 @@ import kr.co.zumo.app.lifeplus.util.TextUtil;
  * @history 하세미   [2018-11-02]   [최초 작성]
  * @since 2018-11-02
  */
-public class CustomSquareArrowCheckBox extends ConstraintLayout implements Checkable {
-
-  private CheckBox checkBox;
-  private OnClickListener listener;
+public class CustomSquareArrowCheckBox extends CustomCheckBox {
 
   public CustomSquareArrowCheckBox(Context context) {
     super(context);
-    init(context);
   }
 
   public CustomSquareArrowCheckBox(Context context, AttributeSet attrs) {
     super(context, attrs);
-    init(context);
-    getAttrs(attrs);
   }
 
   public CustomSquareArrowCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
     super(context, attrs, defStyleAttr);
-    init(context);
-    getAttrs(attrs, defStyleAttr);
   }
 
-  private void init(Context context) {
+  public void init(Context context) {
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     inflater.inflate(R.layout.custom_square_arrow_check_box, this);
     checkBox = findViewById(R.id.check_box);
     checkBox.setClickable(false);
-//    checkBox.setOnClickListener(view -> {
-//      if (null != listener) {
-//        listener.onClick(this);
-//      }
-//      setTextStyle();
-//    });
+    checkBox.setOnClickListener(view -> {
+      if (null != listener) {
+        listener.onClick(this);
+      }
+      setTextStyle();
+    });
   }
 
   @Override
-  protected void onFinishInflate() {
-    super.onFinishInflate();
-  }
-
-  private void getAttrs(AttributeSet attrs) {
+  protected void getAttrs(AttributeSet attrs) {
     TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomSquareArrowCheckBox);
     setTypeArray(typedArray);
   }
 
-  private void getAttrs(AttributeSet attrs, int defStyleAttr) {
+  @Override
+  protected void getAttrs(AttributeSet attrs, int defStyleAttr) {
     TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomSquareArrowCheckBox, defStyleAttr, 0);
     setTypeArray(typedArray);
 
   }
 
-  private void setTypeArray(TypedArray typedArray) {
+  @Override
+  protected void setTypeArray(TypedArray typedArray) {
     String textSquareRadio = typedArray.getString(R.styleable.WhiteButtonWithArrowView_text);
     checkBox.setText(textSquareRadio);
     typedArray.recycle();
   }
 
-  public void setText(@StringRes int stringResource) {
-    checkBox.setText(stringResource);
-  }
-
-  public void setText(String str) {
-    checkBox.setText(str);
-  }
-
-//  @Override
-//  public void setOnClickListener(@Nullable OnClickListener listener) {
-//    this.listener = listener;
-//  }
-
-  @Override
-  public boolean isChecked() {
-    return checkBox.isChecked();
-  }
-
-  @Override
-  public void toggle() {
-    // nothing
-  }
-
-  @Override
-  public void setChecked(boolean isChecked) {
-    checkBox.setChecked(isChecked);
-    setTextStyle();
-  }
-
-  public void setCheckBoxEnabled(boolean isEnabled) {
-    checkBox.setEnabled(isEnabled);
-  }
-
   protected void setTextStyle() {
     if (isChecked()) {
       TextUtil.toBold(checkBox);

+ 16 - 7
app/src/main/java/kr/co/zumo/app/lifeplus/view/dialog/CategoryFilterDialog.java

@@ -19,6 +19,7 @@ import java.util.List;
 import kr.co.zumo.app.R;
 import kr.co.zumo.app.lifeplus.bean.CategoryFilterDataSectionBean;
 import kr.co.zumo.app.lifeplus.bean.api.CategoryFilterBean;
+import kr.co.zumo.app.lifeplus.helper.NavigationBar;
 import kr.co.zumo.app.lifeplus.util.ResourceUtil;
 import kr.co.zumo.app.lifeplus.util.StringUtil;
 import kr.co.zumo.app.lifeplus.view.custom.CustomSquareCheckBox;
@@ -83,8 +84,21 @@ public class CategoryFilterDialog extends DialogBase<ICustomFilterListener<Categ
     buttonLoadingView = getView().findViewById(R.id.button_loading_view);
     buttonLoadingView.draw(getView().getContext());
 
-    applyAnimation();
+    // navigation var setting
+    ((ViewGroup) getView().findViewById(R.id.container_navigation_bar))
+      .addView(
+        new NavigationBar.Builder(getContext())
+          .title(R.string.filter)
+          .close(navigationBar -> {
+            if (null != getCustomListener()) {
+              getCustomListener().onDialogCanceled(CategoryFilterDialog.this);
+            }
+          })
+          .build()
+          .getView()
+      );
 
+    applyAnimation();
     applyFullScreen();
 
     if (null == selectedList) {
@@ -97,12 +111,6 @@ public class CategoryFilterDialog extends DialogBase<ICustomFilterListener<Categ
 
     getView().setPadding(0, ResourceUtil.getStatusBarHeight(), 0, ResourceUtil.getNavBarHeight());
 
-    getView().findViewById(R.id.image_view_cancel_main).setOnClickListener(view -> {
-      if (null != getCustomListener()) {
-        getCustomListener().onDialogCanceled(CategoryFilterDialog.this);
-      }
-    });
-
     getView().findViewById(R.id.text_view_reset).setOnClickListener(view -> {
       reset();
       applyContentsNumber();
@@ -199,6 +207,7 @@ public class CategoryFilterDialog extends DialogBase<ICustomFilterListener<Categ
 
   /**
    * 로딩뷰의 visible 설정
+   *
    * @param isVisibleLoading
    */
   private void setVisibleLoading(boolean isVisibleLoading) {

+ 4 - 4
app/src/main/java/kr/co/zumo/app/lifeplus/view/dialog/ISearchFilterListener.java

@@ -23,14 +23,14 @@ public interface ISearchFilterListener<T extends IDialogBase> extends ICustomDia
    * 개별 필터 선택 시
    *
    * @param dialog
-   * @param filterIds
+   * @param pickedList
    */
-  void onFilterChanged(T dialog, List<SearchFilterBean> filterIds);
+  void onFilterChanged(T dialog, List<SearchFilterBean> pickedList);
 
   /**
    * 필터 선택 결과
    * @param dialog
-   * @param selectedList
+   * @param pickedList
    */
-  void onFilterResult(T dialog, List<SearchFilterBean> selectedList);
+  void onFilterResult(T dialog, List<SearchFilterBean> pickedList);
 }

+ 5 - 5
app/src/main/java/kr/co/zumo/app/lifeplus/view/dialog/SearchFilterDetailDialog.java

@@ -102,11 +102,11 @@ public class SearchFilterDetailDialog extends DialogBase<ISearchFilterListener<S
               getCustomListener().onDialogCanceled(SearchFilterDetailDialog.this);
             }
           })
-          .close(navigationBar -> {
-            if (null != getCustomListener()) {
-              getCustomListener().onDialogCanceled(SearchFilterDetailDialog.this);
-            }
-          })
+//          .close(navigationBar -> {
+//            if (null != getCustomListener()) {
+//              getCustomListener().onDialogCanceled(SearchFilterDetailDialog.this);
+//            }
+//          })
           .build()
           .getView()
       );

+ 73 - 174
app/src/main/java/kr/co/zumo/app/lifeplus/view/dialog/SearchFilterDialog.java

@@ -8,19 +8,14 @@ import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Checkable;
 import android.widget.TextView;
 
-import com.wefika.flowlayout.FlowLayout;
-
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 import kr.co.zumo.app.R;
 import kr.co.zumo.app.lifeplus.bean.SearchFilterSectionBean;
+import kr.co.zumo.app.lifeplus.bean.api.FilterTagBean;
 import kr.co.zumo.app.lifeplus.bean.api.LifeplusData;
 import kr.co.zumo.app.lifeplus.bean.api.SearchFilterBean;
 import kr.co.zumo.app.lifeplus.helper.NavigationBar;
@@ -28,11 +23,6 @@ import kr.co.zumo.app.lifeplus.util.ResourceUtil;
 import kr.co.zumo.app.lifeplus.util.StringUtil;
 import kr.co.zumo.app.lifeplus.view.Event;
 import kr.co.zumo.app.lifeplus.view.IWaiterCallable;
-import kr.co.zumo.app.lifeplus.view.SingleSelector;
-import kr.co.zumo.app.lifeplus.view.custom.CustomCheckBox;
-import kr.co.zumo.app.lifeplus.view.custom.CustomCircleCheckBox;
-import kr.co.zumo.app.lifeplus.view.custom.CustomSquareArrowCheckBox;
-import kr.co.zumo.app.lifeplus.view.custom.CustomSquareCheckBox;
 import kr.co.zumo.app.lifeplus.view.custom.loading.ButtonLoadingView;
 
 /**
@@ -55,8 +45,6 @@ public class SearchFilterDialog extends DialogBase<ISearchFilterListener<SearchF
   private ConstraintLayout layoutLoading;
   private ButtonLoadingView buttonLoadingView;
   private int filteredContentsCount;
-  protected Map<Checkable, SearchFilterBean> filterMap;
-  protected Map<String, List<SearchFilterBean>> selectedDetailTagMap;
 
   /**
    * 필터 데이터 설정
@@ -66,7 +54,7 @@ public class SearchFilterDialog extends DialogBase<ISearchFilterListener<SearchF
    */
   public void setFilter(List<SearchFilterSectionBean> filterList, List<SearchFilterBean> selectedList, int filteredContentsCount) {
     filterDataSectionBeans = filterList;
-    if(null != selectedList) {
+    if (null != selectedList) {
       this.selectedList = new ArrayList<>(selectedList);  // 데이터를 복제해서 사용, 내부에서 배열을 변형한다. 외부로 전달할 때도 각 데이터들을 취합해서 전달해주므로 원형을 유지할 필요가 없음.
     }
     this.filteredContentsCount = filteredContentsCount;
@@ -104,9 +92,7 @@ public class SearchFilterDialog extends DialogBase<ISearchFilterListener<SearchF
         new NavigationBar.Builder(getContext())
           .title(R.string.filter)
           .close(navigationBar -> {
-            if (null != getCustomListener()) {
-              getCustomListener().onDialogCanceled(SearchFilterDialog.this);
-            }
+            cancel();
           })
           .build()
           .getView()
@@ -137,28 +123,10 @@ public class SearchFilterDialog extends DialogBase<ISearchFilterListener<SearchF
     });
   }
 
-  /**
-   * 외부에서 전달되는 selectedList 에는 세부 (지역/핫플레이스 등)이 포함되므로 이를 제외하고 속성만 사용한다.
-   * - 디테일 속성은 map 에 저정
-   * - 디테일인지 여부는 level = FILTER_LEVEL_DETAIL
-   */
-  private void parseSelectedList() {
-    Log.w("APP# SearchFilterDialog | parseSelectedList", "| before: " + selectedList.size());
-    for (Iterator<SearchFilterBean> it = selectedList.iterator(); it.hasNext(); ) {
-      SearchFilterBean value = it.next();
-      if (SearchFilterBean.FILTER_LEVEL_DETAIL.equals(value.getFilterLevel())) {
-        List<SearchFilterBean> list = selectedDetailTagMap.get(value.getFilterNo());
-        if (null == list) {
-          selectedDetailTagMap.put(value.getFilterNo(), new ArrayList<>());
-          list = selectedDetailTagMap.get(value.getFilterNo());
-        }
-
-        list.add(value);
-
-        it.remove();
-      }
+  private void cancel() {
+    if (null != getCustomListener()) {
+      getCustomListener().onDialogCanceled(this);
     }
-    Log.w("APP# SearchFilterDialog | parseSelectedList", "| after: " + selectedList.size());
   }
 
   /**
@@ -168,128 +136,48 @@ public class SearchFilterDialog extends DialogBase<ISearchFilterListener<SearchF
    */
   private List<SearchFilterBean> getSelectedList() {
 
-    List<SearchFilterBean> mergedList = new ArrayList<>(selectedList);
+    List<SearchFilterBean> mergedList = new ArrayList<>();
 
-    for (List<SearchFilterBean> searchFilterBeans : selectedDetailTagMap.values()) {
-      mergedList.addAll(searchFilterBeans);
+    for (SearchFilterSection filterSection : sectionList) {
+      mergedList.addAll(filterSection.getSelectedList());
     }
 
     return mergedList;
   }
 
+  private List<SearchFilterSection> sectionList;
+
   private void init() {
     if (null == filterDataSectionBeans || filterDataSectionBeans.size() == 0) {
       return;
     }
     int len = filterDataSectionBeans.size();
 
-    filterMap = new HashMap<>();
-    selectedDetailTagMap = new HashMap<>();
-
-    parseSelectedList();
-
-    LayoutInflater inflater =  LayoutInflater.from(getContext());
-
     ViewGroup container = getView().findViewById(R.id.layout_container);
+    sectionList = new ArrayList<>();
 
     for (int i = 0; i < len; ++i) {
-      View section = inflater.inflate(R.layout.dialog_category_section_view, container, false);
-
       // section
       SearchFilterSectionBean filterDataSectionBean = filterDataSectionBeans.get(i);
 
-      // title
-      TextView titleView = section.findViewById(R.id.text_view_filter_section_title);
-      titleView.setText(filterDataSectionBean.getSectionTitle());
-
-      // section container
-      ViewGroup sectionContainer = section.findViewById(R.id.layout_filter_container);
-
-      List<SearchFilterBean> filterBeans = filterDataSectionBean.getFilterBeans();
-      if (null != filterBeans && filterBeans.size() > 0) {
-        SingleSelector selector = new SingleSelector((checked, unchecked) -> {
-          setSelectedList(checked);
-          for (Checkable checkable : unchecked) {
-            setSelectedList(checkable);
-          }
-        });
-
-        int len2 = filterBeans.size();
-        for (int k = 0; k < len2; ++k) {
-          SearchFilterBean filterBean = filterBeans.get(k);
-
-          FlowLayout.LayoutParams layoutParams = new FlowLayout.LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT, FlowLayout.LayoutParams.WRAP_CONTENT);
-          layoutParams.leftMargin = ResourceUtil.dpToPx(4);
-          layoutParams.rightMargin = ResourceUtil.dpToPx(4);
-          layoutParams.topMargin = ResourceUtil.dpToPx(4);
-          layoutParams.bottomMargin = ResourceUtil.dpToPx(4);
-
-          if (filterBean.getFilterType().equals(SearchFilterBean.FILTER_TYPE_AREA) || SearchFilterBean.FILTER_TYPE_HOT_PLACE.equals(filterBean.getFilterType())) {
-            CustomSquareArrowCheckBox arrowCheckBox = new CustomSquareArrowCheckBox(getContext());
-            filterMap.put(arrowCheckBox, filterBean);
-
-            arrowCheckBox.setText(filterBean.getFilterName());
-            // touch event
-            arrowCheckBox.setOnClickListener(v -> {
-              // 세부 팝업 표시
-              showDialogDetail(filterBean);
-            });
-
-            // 선택 표시
-            for (String filterNo : selectedDetailTagMap.keySet()) {
-              if (filterNo.equals(filterBean.getFilterNo())) {
-                arrowCheckBox.setChecked(true);
-                break;
-              }
-            }
-
-            sectionContainer.addView(arrowCheckBox, layoutParams);
-          }
-          else {
-            CustomCheckBox checkBox;
-            if (filterBean.getFilterType().equals(SearchFilterBean.FILTER_TYPE_ORDER)) {
-              // 라디오 박스
-              checkBox = new CustomCircleCheckBox(getContext());
-            }
-            else {
-              // 체크 박스
-              checkBox = new CustomSquareCheckBox(getContext());
-            }
-            // 하나만 선택 가능.
-            if (LifeplusData.isTrue(filterBean.getMultiSelection()) == false) {
-              selector.addChildBox(checkBox);
-            }
-
-            filterMap.put(checkBox, filterBean);
-
-            checkBox.setText(filterBean.getFilterName());
-            checkBox.setOnClickListener(v -> {
-              if (selector.contains((Checkable) v)) {
-                // 셀렉터에 포함되있으면 셀렉터에서 처리
-                selector.check((Checkable) v);
-              }
-              else {
-                setSelectedList((Checkable) v);
-              }
-
-              applyContentsNumber();
-            });
-
-            // 이미 선택된 필터는 체크 해준다.
-            if (selectedList.indexOf(filterBean) > -1) {
-              checkBox.setChecked(true);
-            }
+      SearchFilterSection section = new SearchFilterSection(container, filterDataSectionBean, selectedList, new SearchFilterSection.ISectionListener() {
+        @Override
+        public void onRequestDetail(SearchFilterSection section, SearchFilterBean filterBean, SearchFilterBean selectedBean) {
+          showDialogDetail(section, filterBean, selectedBean);
+        }
 
-            sectionContainer.addView(checkBox, layoutParams);
-          }
+        @Override
+        public void onRequestContentsCount(SearchFilterSection section) {
+          applyContentsNumber();
         }
-      }
+      });
+      sectionList.add(section);
 
-      container.addView(section);
+      container.addView(section.getView());
     }
   }
 
-  private void showDialogDetail(SearchFilterBean searchFilterBean) {
+  private void showDialogDetail(SearchFilterSection section, SearchFilterBean filterBean, SearchFilterBean selectedBean) {
     new DialogBuilder<SearchFilterDetailDialog, ISearchFilterListener>(getFragmentManager(), DialogID.SEARCH_FILTER_DETAIL)
       .listener(new ISearchFilterListener<SearchFilterDetailDialog>() {
         @Override
@@ -300,69 +188,80 @@ public class SearchFilterDialog extends DialogBase<ISearchFilterListener<SearchF
         @Override
         public void onDialogCanceled(SearchFilterDetailDialog dialog) {
           dialog.dispose();
-
         }
 
         @Override
-        public void onFilterChanged(SearchFilterDetailDialog dialog, List<SearchFilterBean> filterIds) {
+        public void onFilterChanged(SearchFilterDetailDialog dialog, List<SearchFilterBean> pickedList) {
           // nothing
         }
 
         @Override
-        public void onFilterResult(SearchFilterDetailDialog dialog, List<SearchFilterBean> selectedList) {
+        public void onFilterResult(SearchFilterDetailDialog dialog, List<SearchFilterBean> pickedList) {
           dialog.dispose();
 
-          /*
-          선택된 세부 항목이 있다면, 부모 filter no 를 key 로 해당 리스트를 저장한다.
-           */
-          String filterNo = searchFilterBean.getFilterNo();
-          if (null != selectedList) {
-            selectedDetailTagMap.put(filterNo, selectedList);
+          Log.w("APP# SearchFilterDialog | onFilterResult", "|" + "detail selected..................");
+          for (SearchFilterBean bean : pickedList) {
+            Log.i("APP# SearchFilterDialog | onFilterResult", "|" + bean.toJson());
           }
-
-          // 1개 이상 선택됐다면 해당 체크박스를 체크해준다.
-          for (Checkable checkable : filterMap.keySet()) {
-            if (filterNo.equals(filterMap.get(checkable).getFilterNo())) {
-              checkable.setChecked(selectedDetailTagMap.containsKey(filterNo) && selectedDetailTagMap.get(filterNo).size() > 0);
-              break;
-            }
+          Log.w("APP# SearchFilterDialog | onFilterResult", "|" + "detail selected+++++++++++++++++++");
+
+          SearchFilterBean convertedPickedFilterBean = new SearchFilterBean(
+            filterBean.getFilterName(),
+            filterBean.getFilterType(),
+            filterBean.getFilterNo(),
+            filterBean.getFilterLevel(),
+            ""
+          );
+          List<FilterTagBean> tagBeans = new ArrayList<>();
+          for (SearchFilterBean bean : pickedList) {
+            FilterTagBean tagBean = new FilterTagBean(bean.getFilterName());
+            tagBean.setTagNo(bean.getTagNo());
           }
 
+          convertedPickedFilterBean.setTagBeans(tagBeans);
+          section.onDetailResult(filterBean, convertedPickedFilterBean);
+
           applyContentsNumber();
         }
       })
       .attribute(dialog -> {
         String title;
-        if (SearchFilterBean.FILTER_TYPE_AREA.equals(searchFilterBean.getFilterType())) {
-          title = ResourceUtil.getString(R.string.search_filter_detail_title_area, searchFilterBean.getFilterName());
+        if (SearchFilterBean.FILTER_TYPE_AREA.equals(filterBean.getFilterType())) {
+          title = ResourceUtil.getString(R.string.search_filter_detail_title_area, filterBean.getFilterName());
         }
         else {
-          title = ResourceUtil.getString(R.string.search_filter_detail_title, searchFilterBean.getFilterName());
+          title = ResourceUtil.getString(R.string.search_filter_detail_title, filterBean.getFilterName());
+        }
+
+        // 이전 선택된 태그들 체크 해줌.
+        List<SearchFilterBean> convertedSelectedList = new ArrayList<>();
+        if(null != selectedBean) {
+          List<FilterTagBean> tagBeans = selectedBean.getTagBeans();
+          if (null != tagBeans) {
+            int len = tagBeans.size();
+            FilterTagBean tagBean;
+            for (int i = 0; i < len ; ++i) {
+              tagBean = tagBeans.get(i);
+              convertedSelectedList.add(new SearchFilterBean(
+                tagBean.getTagName(),
+                selectedBean.getFilterType(),
+                selectedBean.getFilterNo(),
+                selectedBean.getFilterLevel(),
+                tagBean.getTagNo()
+              ));
+            }
+          }
         }
-        dialog.setFilterDetail(searchFilterBean.getTagBeans(),
-          selectedDetailTagMap.get(searchFilterBean.getFilterNo()),
+        dialog.setFilterDetail(filterBean.getTagBeans(),
+          convertedSelectedList,
           title,
-          LifeplusData.isTrue(searchFilterBean.getMultiSelection()),
-          searchFilterBean.getFilterType(),
-          searchFilterBean.getFilterNo());
+          LifeplusData.isTrue(filterBean.getMultiSelection()),
+          filterBean.getFilterType(),
+          filterBean.getFilterNo());
       })
       .show();
   }
 
-  private void setSelectedList(Checkable checkBox) {
-    SearchFilterBean filterBean = filterMap.get(checkBox);
-    if (null != filterBean) {
-      if (checkBox.isChecked()) {
-        if (selectedList.indexOf(filterBean) == -1) {
-          selectedList.add(filterBean);
-        }
-      }
-      else {
-        selectedList.remove(filterBean);
-      }
-    }
-  }
-
   /**
    * 현재 필터로 표시할 수 있는 컨텐츠 구함.
    */

+ 276 - 0
app/src/main/java/kr/co/zumo/app/lifeplus/view/dialog/SearchFilterSection.java

@@ -0,0 +1,276 @@
+/*
+ * COPYRIGHT (c) 2018 All rights reserved by HANWHA LIFE.
+ */
+package kr.co.zumo.app.lifeplus.view.dialog;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Checkable;
+import android.widget.TextView;
+
+import com.wefika.flowlayout.FlowLayout;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import kr.co.zumo.app.R;
+import kr.co.zumo.app.lifeplus.bean.SearchFilterSectionBean;
+import kr.co.zumo.app.lifeplus.bean.api.LifeplusData;
+import kr.co.zumo.app.lifeplus.bean.api.SearchFilterBean;
+import kr.co.zumo.app.lifeplus.util.ResourceUtil;
+import kr.co.zumo.app.lifeplus.view.SingleSelector;
+import kr.co.zumo.app.lifeplus.view.custom.CustomCheckBox;
+import kr.co.zumo.app.lifeplus.view.custom.CustomCircleCheckBox;
+import kr.co.zumo.app.lifeplus.view.custom.CustomSquareArrowCheckBox;
+import kr.co.zumo.app.lifeplus.view.custom.CustomSquareCheckBox;
+
+/**
+ * SearchFilterSection
+ * <pre>
+ * </pre>
+ *
+ * @author 민효동
+ * @version 1.0
+ * @history 민효동   [2019. 1. 22.]   [최초 작성]
+ * @since 2019. 1. 22.
+ */
+public class SearchFilterSection {
+
+  private List<SearchFilterBean> selectedList;
+  private View section;
+  private SearchFilterSectionBean sectionBean;
+  private ISectionListener listener;
+
+  protected Map<Checkable, SearchFilterBean> filterMap;
+//  protected Map<String, List<SearchFilterBean>> selectedDetailTagMap;
+
+  public SearchFilterSection(ViewGroup container, SearchFilterSectionBean searchFilterSectionBean, List<SearchFilterBean> selectedList, ISectionListener listener) {
+    this.sectionBean = searchFilterSectionBean;
+    this.selectedList = new ArrayList<>(selectedList);
+    this.listener = listener;
+
+    section = LayoutInflater.from(container.getContext()).inflate(R.layout.dialog_category_section_view, container, false);
+
+    init(container.getContext());
+  }
+
+  /**
+   * 외부에서 전달되는 selectedList 에는 세부 (지역/핫플레이스 등)이 포함되므로 이를 제외하고 속성만 사용한다.
+   * - 디테일 속성은 map 에 저정
+   * - 디테일인지 여부는 level = FILTER_LEVEL_DETAIL
+   */
+  private void parseSelectedList() {
+    List<SearchFilterBean> sectionSelectedList = new ArrayList<>();
+    List<SearchFilterBean> filterBeans = sectionBean.getFilterBeans();
+
+    for (SearchFilterBean filterBean : filterBeans) {
+      for (SearchFilterBean bean : selectedList) {
+        if (equals(bean, filterBean)) {
+          Log.i("APP# SearchFilterSection | parseSelectedList", "|" + " pick... " + bean.toJson());
+          sectionSelectedList.add(bean);
+          break;
+        }
+      }
+    }
+
+    selectedList = sectionSelectedList;
+    Log.w("APP# SearchFilterSection | parseSelectedList", "| before: " + selectedList.size());
+//    for (Iterator<SearchFilterBean> it = selectedList.iterator(); it.hasNext(); ) {
+//      SearchFilterBean value = it.next();
+//      if (SearchFilterBean.FILTER_LEVEL_DETAIL.equals(value.getFilterLevel())) {
+//        List<SearchFilterBean> list = selectedDetailTagMap.get(value.getFilterNo());
+//        if (null == list) {
+//          selectedDetailTagMap.put(value.getFilterNo(), new ArrayList<>());
+//          list = selectedDetailTagMap.get(value.getFilterNo());
+//        }
+//
+//        list.add(value);
+//
+//        it.remove();
+//      }
+//    }
+//    Log.w("APP# SearchFilterSection | parseSelectedList", "| after: " + selectedList.size());
+  }
+
+  private void init(Context context) {
+
+    filterMap = new HashMap<>();
+//    selectedDetailTagMap = new HashMap<>();
+    parseSelectedList();
+
+    // title
+    TextView titleView = section.findViewById(R.id.text_view_filter_section_title);
+    titleView.setText(sectionBean.getSectionTitle());
+
+    // section container
+    ViewGroup sectionContainer = section.findViewById(R.id.layout_filter_container);
+
+    List<SearchFilterBean> filterBeans = sectionBean.getFilterBeans();
+    if (null != filterBeans && filterBeans.size() > 0) {
+      SingleSelector selector = new SingleSelector((checked, unchecked) -> {
+        setSelectedList(checked);
+        for (Checkable checkable : unchecked) {
+          setSelectedList(checkable);
+        }
+      });
+
+      int len2 = filterBeans.size();
+      for (int k = 0; k < len2; ++k) {
+        SearchFilterBean filterBean = filterBeans.get(k);
+
+        FlowLayout.LayoutParams layoutParams = new FlowLayout.LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT, FlowLayout.LayoutParams.WRAP_CONTENT);
+        layoutParams.leftMargin = ResourceUtil.dpToPx(4);
+        layoutParams.rightMargin = ResourceUtil.dpToPx(4);
+        layoutParams.topMargin = ResourceUtil.dpToPx(4);
+        layoutParams.bottomMargin = ResourceUtil.dpToPx(4);
+
+
+        CustomCheckBox checkBox;
+        if (filterBean.getFilterType().equals(SearchFilterBean.FILTER_TYPE_AREA) || SearchFilterBean.FILTER_TYPE_HOT_PLACE.equals(filterBean.getFilterType())) {
+          // 세부 설정 필요
+          checkBox = new CustomSquareArrowCheckBox(context);
+          // touch event
+          checkBox.setOnClickListener(v -> {
+            // 세부 팝업 표시
+            requestDetail(filterBean);
+          });
+        }
+        else {
+          if (filterBean.getFilterType().equals(SearchFilterBean.FILTER_TYPE_ORDER)) {
+            // 라디오 박스
+            checkBox = new CustomCircleCheckBox(context);
+          }
+          else {
+            // 체크 박스
+            checkBox = new CustomSquareCheckBox(context);
+          }
+          checkBox.setOnClickListener(v -> {
+            if (selector.contains((Checkable) v)) {
+              // 셀렉터에 포함되있으면 셀렉터에서 처리
+              selector.check((Checkable) v);
+            }
+            else {
+              setSelectedList((Checkable) v);
+            }
+
+//            applyContentsNumber();
+            requestContentsCount();
+          });
+        }
+
+        // 하나만 선택 가능.
+        if (LifeplusData.isTrue(filterBean.getMultiSelection()) == false) {
+          selector.addChildBox(checkBox);
+        }
+
+        filterMap.put(checkBox, filterBean);
+
+        checkBox.setText(filterBean.getFilterName());
+
+        // 이미 선택된 필터는 체크 해준다.
+        for (SearchFilterBean bean : selectedList) {
+          if (equals(bean, filterBean)) {
+            checkBox.setChecked(true);
+            break;
+          }
+        }
+
+        sectionContainer.addView(checkBox, layoutParams);
+      }
+    }
+  }
+
+  private void requestDetail(SearchFilterBean filterBean) {
+
+    SearchFilterBean selectedBean = null;
+    for (SearchFilterBean bean : selectedList) {
+      if (equals(bean, filterBean)) {
+        selectedBean = bean;
+        break;
+      }
+    }
+    listener.onRequestDetail(this, filterBean, selectedBean);
+  }
+
+  private void requestContentsCount() {
+    listener.onRequestContentsCount(this);
+  }
+
+  /**
+   * 세부 항목을 선택 했을 때
+   *
+   * @param filterBean
+   * @param selectedFilterBean
+   */
+  public void onDetailResult(SearchFilterBean filterBean, SearchFilterBean selectedFilterBean) {
+
+    /*
+      선택된 세부 항목이 있다면, 선택 리스트에 저장해준다.
+     */
+    if (null != selectedFilterBean && null != selectedFilterBean.getTagBeans()) {
+      for (SearchFilterBean bean : selectedList) {
+        if (equals(bean, filterBean)) {
+          Log.i("APP# SearchFilterSection | onDetailResult", "|" + " replace... " + bean.toJson());
+          selectedList.remove(bean);
+          break;
+        }
+      }
+    }
+    selectedList.add(selectedFilterBean);
+
+    // 1개 이상 선택됐다면 해당 체크박스를 체크해준다. 0개는 선택 해제
+    for (Checkable checkable : filterMap.keySet()) {
+      if (filterBean.getFilterNo().equals(filterMap.get(checkable).getFilterNo())) {
+        checkable.setChecked(selectedFilterBean.getTagBeans().size() > 0);
+        break;
+      }
+    }
+  }
+
+  private boolean equals(SearchFilterBean bean1, SearchFilterBean bean2) {
+    return null != bean2 && null != bean1 && bean2.getFilterNo().equals(bean1.getFilterNo()) && bean2.getFilterName().equals(bean1.getFilterName());
+  }
+
+  private void setSelectedList(Checkable checkBox) {
+    SearchFilterBean filterBean = filterMap.get(checkBox);
+    if (null != filterBean) {
+      if (checkBox.isChecked()) {
+        if (selectedList.indexOf(filterBean) == -1) {
+          selectedList.add(filterBean);
+        }
+      }
+      else {
+        selectedList.remove(filterBean);
+      }
+    }
+  }
+
+  /**
+   * 지역/핫 플레이스 별로 저장하고 있던 '선택된 필터 리스트'를 기본 필터와 병합하여 반환한다.
+   *
+   * @return
+   */
+  public List<SearchFilterBean> getSelectedList() {
+    List<SearchFilterBean> mergedList = new ArrayList<>(selectedList);
+
+//    for (List<SearchFilterBean> searchFilterBeans : selectedDetailTagMap.values()) {
+//      mergedList.addAll(searchFilterBeans);
+//    }
+    return mergedList;
+  }
+
+  public View getView() {
+    return section;
+  }
+
+  interface ISectionListener {
+    void onRequestDetail(SearchFilterSection section, SearchFilterBean filterBean, SearchFilterBean selectedBean);
+
+    void onRequestContentsCount(SearchFilterSection section);
+  }
+}

+ 4 - 4
app/src/main/java/kr/co/zumo/app/lifeplus/view/screen/search/SearchResultPresenter.java

@@ -203,18 +203,18 @@ public class SearchResultPresenter extends Presenter<SearchResultModel, ISearchR
           }
 
           @Override
-          public void onFilterResult(SearchFilterDialog dialog, List<SearchFilterBean> selectedList) {
+          public void onFilterResult(SearchFilterDialog dialog, List<SearchFilterBean> pickedList) {
             filterDialog = null;
             dialog.dispose();
-            Log.w("APP# FirstCategoryMainPresenter | onFilterResult", "|" + selectedList.size());
+            Log.w("APP# FirstCategoryMainPresenter | onFilterResult", "|" + pickedList.size());
 
             model.commitFilter();
           }
 
           @Override
-          public void onFilterChanged(SearchFilterDialog dialog, List<SearchFilterBean> selectedList) {
+          public void onFilterChanged(SearchFilterDialog dialog, List<SearchFilterBean> pickedList) {
             // 변경 된 필터를 적용한 컨텐츠의 수를 다이얼로그로 전달한다.
-            model.setFilter(selectedList);
+            model.setFilter(pickedList);
             model.loadFilteringContents(filterDialog, event -> {
               if (event.getEventId() == Event.SUCCESS) {
                 filterDialog.setContentsCount(event.getInteger());