├── .gitignore ├── FilterViewSipmle ├── .gitignore ├── .idea │ ├── checkstyle-idea.xml │ ├── compiler.xml │ ├── copyright │ │ └── profiles_settings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── widget │ │ │ └── tamic │ │ │ └── com │ │ │ └── filterviewsipmle │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ └── testcaselist.json │ │ ├── java │ │ │ └── com │ │ │ │ └── tamic │ │ │ │ └── widget │ │ │ │ ├── filter │ │ │ │ ├── BaseBean.java │ │ │ │ ├── BaseFilterItem.java │ │ │ │ ├── CategoryBar.java │ │ │ │ ├── CategoryButton.java │ │ │ │ ├── CategoryCheckable.java │ │ │ │ ├── ConditionContainer.java │ │ │ │ ├── DataSelectContainer.java │ │ │ │ ├── DateSelectConditionItem.java │ │ │ │ ├── DateView.java │ │ │ │ ├── FilterBean.java │ │ │ │ ├── FilterContainer.java │ │ │ │ ├── FilterMode.java │ │ │ │ ├── FliterResultItem.java │ │ │ │ ├── GridContainer.java │ │ │ │ ├── GroupFilterItem.java │ │ │ │ ├── ListFilterBean.java │ │ │ │ ├── NewConditionItem.java │ │ │ │ ├── RangeConditionItem.java │ │ │ │ ├── RangeContainer.java │ │ │ │ ├── RangeSeekBar.java │ │ │ │ ├── RegionContainer.java │ │ │ │ ├── SorterContainer.java │ │ │ │ └── UIUtil.java │ │ │ │ └── filterviewsipmle │ │ │ │ ├── MainActivity.java │ │ │ │ └── Test.java │ │ └── res │ │ │ ├── drawable │ │ │ ├── less.png │ │ │ ├── more_unfold.png │ │ │ ├── range_seek_bar_thumb.png │ │ │ ├── selector_item_condition_background.xml │ │ │ ├── selector_item_condition_grid_background.xml │ │ │ ├── selector_item_condition_grid_text_color.xml │ │ │ ├── selector_item_condition_text2_color.xml │ │ │ ├── selector_item_condition_text_color.xml │ │ │ ├── shape_btn_standard_3b4263_rectangle.xml │ │ │ └── shape_list_divider.xml │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── content_main.xml │ │ │ ├── item_condition_1.xml │ │ │ ├── item_condition_2.xml │ │ │ ├── item_condition_3.xml │ │ │ ├── item_condition_grid.xml │ │ │ ├── layout_category_bar_mode_brand_list.xml │ │ │ ├── layout_category_button.xml │ │ │ ├── layout_category_button_new.xml │ │ │ ├── layout_condition_data_select.xml │ │ │ ├── layout_condition_divider.xml │ │ │ ├── layout_condition_filter.xml │ │ │ ├── layout_condition_grid.xml │ │ │ ├── layout_condition_range.xml │ │ │ ├── layout_condition_region.xml │ │ │ └── layout_condition_sorter.xml │ │ │ ├── menu │ │ │ └── menu_main.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-v21 │ │ │ └── styles.xml │ │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ │ └── values │ │ │ ├── attrs-rangeseekbar.xml │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── widget │ │ └── tamic │ │ └── com │ │ └── filterviewsipmle │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | -------------------------------------------------------------------------------- /FilterViewSipmle/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FilterViewSipmle/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.tamic.widget" 8 | minSdkVersion 17 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | lintOptions { 22 | abortOnError false 23 | checkReleaseBuilds false 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.google.code.gson:gson:2.8.1' 33 | compile 'com.android.support:appcompat-v7:25.3.1' 34 | compile 'com.android.support:design:25.3.1' 35 | testCompile 'junit:junit:4.12' 36 | compile 'com.alibaba:fastjson:1.1.60.android' 37 | 38 | //compile 'com.google.android.gms:play-services-appindexing:8.4.0' 39 | } 40 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Users\liuyongkui726\AppData\Local\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/androidTest/java/widget/tamic/com/filterviewsipmle/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package widget.tamic.com.filterviewsipmle; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("widget.tamic.com.filterviewsipmle", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/assets/testcaselist.json: -------------------------------------------------------------------------------- 1 | [[{"keyName":"支付条件","value":"hjjjj"},{"keyName":"金额","value":"8666"}],[{"keyName":"支付条件","value":"hjjjj"},{"keyName":"金额","value":"8666"}]] -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/BaseBean.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | 4 | /** 5 | * Created by tamic on 2017-08-03. 6 | */ 7 | public abstract class BaseBean { 8 | } 9 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/BaseFilterItem.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by tamic on 2017-08-03. 8 | * 9 | * @link {https://github.com/Tamicer/FilterBar} 10 | */ 11 | public class BaseFilterItem extends BaseBean implements Parcelable { 12 | 13 | private int id; 14 | private String name; 15 | private int code; 16 | private String value; 17 | 18 | public BaseFilterItem() { 19 | } 20 | 21 | protected BaseFilterItem(Parcel in) { 22 | id = in.readInt(); 23 | name = in.readString(); 24 | code = in.readInt(); 25 | value = in.readString(); 26 | } 27 | 28 | @Override 29 | public void writeToParcel(Parcel dest, int flags) { 30 | dest.writeInt(id); 31 | dest.writeString(name); 32 | dest.writeInt(code); 33 | dest.writeString(value); 34 | } 35 | 36 | @Override 37 | public int describeContents() { 38 | return 0; 39 | } 40 | 41 | public static final Creator CREATOR = new Creator() { 42 | 43 | @Override 44 | public BaseFilterItem createFromParcel(Parcel in) { 45 | return new BaseFilterItem(in); 46 | } 47 | 48 | @Override 49 | public BaseFilterItem[] newArray(int size) { 50 | return new BaseFilterItem[size]; 51 | } 52 | }; 53 | 54 | public int getId() { 55 | return id; 56 | } 57 | 58 | public void setId(int id) { 59 | this.id = id; 60 | } 61 | 62 | public String getName() { 63 | return name; 64 | } 65 | 66 | public void setName(String name) { 67 | this.name = name; 68 | } 69 | 70 | public int getCode() { 71 | return code; 72 | } 73 | 74 | public void setCode(int code) { 75 | this.code = code; 76 | } 77 | 78 | public String getValue() { 79 | return value; 80 | } 81 | 82 | public void setValue(String value) { 83 | this.value = value; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/CategoryBar.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.ColorDrawable; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.FrameLayout; 10 | import android.widget.PopupWindow; 11 | 12 | 13 | import java.util.HashMap; 14 | 15 | import widget.tamic.com.filterviewsipmle.R; 16 | 17 | 18 | /** 19 | * Created by tamic on 2017-08-03. 20 | * 21 | * @link {https://github.com/Tamicer/FilterBar} 22 | */ 23 | public class CategoryBar extends FrameLayout implements ConditionContainer.Controller, ConditionContainer.FiterModeCallback { 24 | 25 | static final int MODE_LIST = 0; 26 | static final int MODE_MAP = 1; 27 | static final int MODE_BRAND_LIST = 2; 28 | static final int MODE_ED = 2; 29 | 30 | public static final String FILTER_ROOT = "filterRoot"; 31 | public static final String REGION_ROOT = "regionRoot"; 32 | public static final String SORTER_ROOT = "sorterRoot"; 33 | 34 | 35 | private FilterMode filterMode; 36 | private FiterModeCallback mCallback; 37 | private int mMode = MODE_LIST; 38 | CategoryButton mRegion; 39 | CategoryButton mFilter; 40 | CategoryButton mSorter; 41 | PopupWindow mPopup; 42 | 43 | boolean isShowRegion = true; 44 | boolean isShowFilter = true; 45 | boolean isShowSorter = true; 46 | 47 | @Override 48 | public boolean isShown() { 49 | return super.isShown(); 50 | } 51 | 52 | HashMap mBtnMap = new HashMap<>(); 53 | 54 | private ControllerListener mControllerListener; 55 | 56 | public CategoryBar(Context context) { 57 | super(context); 58 | init(); 59 | } 60 | 61 | public CategoryBar(Context context, FiterModeCallback callback) { 62 | super(context); 63 | mCallback = callback; 64 | init(); 65 | } 66 | 67 | public CategoryBar(Context context, AttributeSet attrs) { 68 | super(context, attrs); 69 | init(); 70 | } 71 | 72 | private void init() { 73 | 74 | LayoutInflater.from(getContext()).inflate(R.layout.layout_category_bar_mode_brand_list, this); 75 | mRegion = (CategoryButton) findViewById(R.id.btn_region_category_bar); 76 | 77 | if (mRegion != null) { 78 | mRegion.setText("区域"); 79 | mRegion.setOnCheckedChangedListener(mButtonOnChecked); 80 | mRegionContainer = new RegionContainer(getContext(), geFiterMode()); 81 | mRegionContainer.setController(this); 82 | mRegionContainer.setModeCallback(this); 83 | mBtnMap.put(mRegion, mRegionContainer); 84 | } 85 | 86 | mFilter = (CategoryButton) findViewById(R.id.btn_filter_category_bar); 87 | 88 | if (mFilter != null) { 89 | mFilter.setText("筛选"); 90 | mFilter.setOnCheckedChangedListener(mButtonOnChecked); 91 | mFilterContainer = new FilterContainer(getContext()); 92 | mFilterContainer.setController(this); 93 | mBtnMap.put(mFilter, mFilterContainer); 94 | } 95 | 96 | mSorter = (CategoryButton) findViewById(R.id.btn_sorter_category_bar); 97 | 98 | if (mSorter != null) { 99 | mSorter.setText("排序"); 100 | mSorter.setOnCheckedChangedListener(mButtonOnChecked); 101 | mSorterContainer = new SorterContainer(getContext()); 102 | mSorterContainer.setController(this); 103 | mBtnMap.put(mSorter, mSorterContainer); 104 | } 105 | if (!isShowSorter) { 106 | mSorter.setVisibility(GONE); 107 | } 108 | 109 | mPopup = new PopupWindow(); 110 | } 111 | 112 | public CategoryBar setRegionShow(boolean isShow) { 113 | 114 | if (isShow) { 115 | mRegion.setVisibility(VISIBLE); 116 | } else { 117 | mRegion.setVisibility(GONE); 118 | } 119 | return this; 120 | } 121 | 122 | public CategoryBar setFilterShow(boolean isShow) { 123 | 124 | if (isShow) { 125 | mFilter.setVisibility(VISIBLE); 126 | } else { 127 | mFilter.setVisibility(GONE); 128 | } 129 | return this; 130 | } 131 | 132 | public CategoryBar setSorterShow(boolean isShow) { 133 | 134 | if (isShow) { 135 | mSorter.setVisibility(VISIBLE); 136 | } else { 137 | mSorter.setVisibility(GONE); 138 | } 139 | return this; 140 | } 141 | 142 | public CategoryBar setFristTabText(String text) { 143 | if (mRegion != null) { 144 | mRegion.setText(text); 145 | } 146 | return this; 147 | } 148 | 149 | public CategoryBar setSecondTabText(String text) { 150 | if (mFilter != null) { 151 | mFilter.setText(text); 152 | } 153 | return this; 154 | } 155 | 156 | public CategoryBar setThreeTabText(String text) { 157 | if (mSorter != null) { 158 | mSorter.setText(text); 159 | } 160 | return this; 161 | } 162 | 163 | public void setFilterCallback(FiterModeCallback callback) { 164 | mCallback = callback; 165 | } 166 | 167 | private void cancelBtn(CategoryCheckable btn) { 168 | if (btn != null && btn.isChecked()) { 169 | btn.setChecked(false); 170 | } 171 | } 172 | 173 | final private CategoryCheckable.OnCheckedChangeListener mButtonOnChecked = 174 | new CategoryCheckable.OnCheckedChangeListener() { 175 | 176 | @Override 177 | public void onCheckedChanged(CategoryCheckable button, boolean isChecked) { 178 | if (isChecked) { 179 | CategoryCheckable[] checkables = new CategoryCheckable[] { 180 | mRegion, mFilter, mSorter 181 | }; 182 | for (CategoryCheckable checkable : checkables) { 183 | if (checkable != button) { 184 | cancelBtn(checkable); 185 | } 186 | } 187 | } 188 | ConditionContainer container = mBtnMap.get(button); 189 | if (container == null) { 190 | return; 191 | } 192 | if (isChecked) { 193 | if (mControllerListener != null) { 194 | mControllerListener.onOpen(container); 195 | } 196 | mActive = button; 197 | int[] location = new int[2]; 198 | getLocationInWindow(location); 199 | int winWidth = UIUtil.getWindowWidth(getContext()); 200 | int winHeight = UIUtil.getWindowHeight(getContext()); 201 | 202 | 203 | View empty = new View(getContext()); 204 | ViewGroup.LayoutParams lp = new LayoutParams( 205 | ViewGroup.LayoutParams.MATCH_PARENT, 206 | ViewGroup.LayoutParams.MATCH_PARENT); 207 | empty.setLayoutParams(lp); 208 | 209 | mPopup.setWidth(winWidth); 210 | mPopup.setHeight(winHeight - getHeight() - location[1]); 211 | mPopup.setContentView(container); 212 | mPopup.setBackgroundDrawable(new ColorDrawable(0xcc000000)); 213 | mPopup.showAsDropDown(CategoryBar.this); 214 | } else { 215 | mPopup.dismiss(); 216 | mActive = null; 217 | if (mControllerListener != null) { 218 | mControllerListener.onCancel(container); 219 | } 220 | } 221 | } 222 | }; 223 | 224 | private CategoryCheckable mActive; 225 | private ConditionContainer mRegionContainer; 226 | private ConditionContainer mFilterContainer; 227 | private ConditionContainer mSorterContainer; 228 | private ConditionContainer mDNAContainer; 229 | 230 | public static int getModeList() { 231 | return MODE_LIST; 232 | } 233 | 234 | public ConditionContainer getRegionContainer() { 235 | return mRegionContainer; 236 | } 237 | 238 | public void addFristContainer(ConditionContainer container) { 239 | this.mRegionContainer = container; 240 | mRegionContainer.setController(this); 241 | mRegionContainer.setModeCallback(this); 242 | mBtnMap.put(mRegion, mRegionContainer); 243 | } 244 | 245 | public ConditionContainer getFilterContainer() { 246 | return mFilterContainer; 247 | } 248 | 249 | public void addSecondContainer(ConditionContainer container) { 250 | this.mFilterContainer = container; 251 | mFilterContainer.setController(this); 252 | mFilterContainer.setModeCallback(this); 253 | mBtnMap.put(mFilter, mFilterContainer); 254 | } 255 | 256 | public void addThreeContainer(ConditionContainer container) { 257 | this.mSorterContainer = container; 258 | mSorterContainer.setController(this); 259 | mSorterContainer.setModeCallback(this); 260 | mBtnMap.put(mSorter, mSorterContainer); 261 | } 262 | 263 | public ConditionContainer getSorterContainer() { 264 | return mSorterContainer; 265 | } 266 | 267 | public ConditionContainer getDNAContainer() { 268 | return mDNAContainer; 269 | } 270 | 271 | public void setDNAContainer(ConditionContainer mDNAContainer) { 272 | this.mDNAContainer = mDNAContainer; 273 | } 274 | 275 | private void setHandle(ConditionContainer container) { 276 | container.setController(this); 277 | container.setModeCallback(this); 278 | } 279 | 280 | @Override 281 | public void confirm(ConditionContainer container) { 282 | if (mControllerListener != null) { 283 | mControllerListener.onConfirm(container); 284 | } 285 | if (mActive != null) { 286 | mActive.setChecked(false); 287 | } 288 | } 289 | 290 | @Override 291 | public void cancel(ConditionContainer container) { 292 | if (mActive != null) { 293 | mActive.setChecked(false); 294 | } 295 | } 296 | 297 | @Override 298 | public void onItemClick(ConditionContainer container, int id) { 299 | 300 | if (mControllerListener != null) { 301 | mControllerListener.onSpecial(container, id); 302 | } 303 | } 304 | 305 | public void setMode(FilterMode mode) { 306 | filterMode = mode; 307 | } 308 | 309 | public void setOnConfirmListener(ControllerListener listener) { 310 | mControllerListener = listener; 311 | } 312 | 313 | @Override 314 | public FilterMode geFiterMode() { 315 | if (mCallback == null) { 316 | return FilterMode.ENTER; 317 | } 318 | return mCallback.setFiterMode(); 319 | } 320 | 321 | public interface ControllerListener { 322 | 323 | void onConfirm(ConditionContainer container); 324 | 325 | void onOpen(ConditionContainer container); 326 | 327 | void onCancel(ConditionContainer container); 328 | 329 | void onSpecial(ConditionContainer container, int id); 330 | } 331 | 332 | public interface FiterModeCallback { 333 | 334 | FilterMode setFiterMode(); 335 | } 336 | 337 | @Override 338 | public void setVisibility(int visibility) { 339 | super.setVisibility(visibility); 340 | if (visibility != VISIBLE) { //需要关闭popup 341 | mPopup.dismiss(); 342 | } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/CategoryButton.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.Gravity; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.FrameLayout; 9 | import android.widget.TextView; 10 | 11 | import widget.tamic.com.filterviewsipmle.R; 12 | 13 | 14 | /** 15 | * Created by Tamic on 2016-08-24. 16 | */ 17 | public class CategoryButton extends FrameLayout implements CategoryCheckable { 18 | 19 | public CategoryButton(Context context) { 20 | super(context); 21 | init(); 22 | } 23 | 24 | public CategoryButton(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | init(); 27 | } 28 | 29 | static private final int COLOR_TEXT_CHECK = 0xff6281c2; 30 | static private final int COLOR_TEXT_UNCHECK = 0xff555555; 31 | static private final int COLOR_ICON_CHECK = 0xff6281c2; 32 | static private final int COLOR_ICON_UNCHECK = 0xff555555; 33 | static private final int TEXT_SIZE = 14; //sp 34 | 35 | private View mContent; 36 | private TextView mTvText; 37 | private TextView mTvIcon; 38 | private boolean mChecked = false; 39 | private String mText; 40 | private OnCheckedChangeListener mOnCheckedChangedListener; 41 | private OnClickListener mOnClickListener = new OnClickListener() { 42 | 43 | @Override 44 | public void onClick(View v) { 45 | setChecked(!mChecked); 46 | 47 | } 48 | }; 49 | 50 | private void init() { 51 | LayoutInflater.from(getContext()).inflate(R.layout.layout_category_button, this); 52 | mContent = findViewById(R.id.content_layout_category_button); 53 | mTvText = (TextView) findViewById(R.id.tv_text_layout_category_button); 54 | mTvIcon = (TextView) findViewById(R.id.tv_icon_layout_category_button); 55 | mTvText.setTextSize(TEXT_SIZE); 56 | mTvIcon.setTextSize(TEXT_SIZE); 57 | mContent.setOnClickListener(mOnClickListener); 58 | update(); 59 | 60 | } 61 | 62 | public void setChecked(boolean checked) { 63 | if (mChecked != checked) { 64 | mChecked = checked; 65 | update(); 66 | if (mOnCheckedChangedListener != null) { 67 | mOnCheckedChangedListener.onCheckedChanged(this, mChecked); 68 | } 69 | } 70 | } 71 | 72 | public boolean isChecked() { 73 | return mChecked; 74 | } 75 | 76 | public void setText(String text) { 77 | mText = text; 78 | mTvText.setText(mText); 79 | } 80 | 81 | public String getText() { 82 | return mTvText.getText().toString(); 83 | } 84 | 85 | public void setOnCheckedChangedListener(OnCheckedChangeListener listener) { 86 | mOnCheckedChangedListener = listener; 87 | } 88 | 89 | private void update() { 90 | if (mChecked) { 91 | mTvText.setTextColor(COLOR_TEXT_CHECK); 92 | mTvIcon.setTextColor(COLOR_ICON_CHECK); 93 | mTvIcon.setBackground(getContext().getResources().getDrawable(R.drawable.more_unfold)); 94 | } else { 95 | mTvText.setTextColor(COLOR_TEXT_UNCHECK); 96 | mTvIcon.setTextColor(COLOR_ICON_UNCHECK); 97 | mTvIcon.setBackground(getContext().getResources().getDrawable(R.drawable.less)); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/CategoryCheckable.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | /** 4 | * Created by tamic on 2016-08-26. 5 | * 6 | * @link {https://github.com/Tamicer/FilterBar} 7 | */ 8 | public interface CategoryCheckable { 9 | 10 | interface OnCheckedChangeListener { 11 | 12 | void onCheckedChanged(CategoryCheckable button, boolean isChecked); 13 | } 14 | 15 | void setChecked(boolean checked); 16 | 17 | boolean isChecked(); 18 | 19 | void setText(String text); 20 | 21 | void setOnCheckedChangedListener(OnCheckedChangeListener listener); 22 | } 23 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/ConditionContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.widget.FrameLayout; 7 | 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * update by Tamic on 2017-07-26. 13 | * ---------------------- 14 | */ 15 | public abstract class ConditionContainer extends FrameLayout { 16 | 17 | public interface Controller { 18 | 19 | void confirm(ConditionContainer container); 20 | 21 | void cancel(ConditionContainer container); 22 | 23 | void onItemClick(ConditionContainer container, int id); 24 | } 25 | 26 | public ConditionContainer(Context context) { 27 | super(context); 28 | } 29 | 30 | public ConditionContainer(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | } 33 | 34 | abstract public NewConditionItem getConditionItem(); 35 | 36 | abstract public void setConditionItem(NewConditionItem item); 37 | 38 | abstract public NewConditionItem create(List itemList); 39 | 40 | abstract public void addContentView(View view); 41 | 42 | protected Controller mController; 43 | protected FiterModeCallback mCallback; 44 | 45 | public void setController(Controller controller) { 46 | mController = controller; 47 | } 48 | 49 | public void setModeCallback(FiterModeCallback controller) { 50 | mCallback = controller; 51 | } 52 | 53 | public void onItemClick(ConditionContainer container) { 54 | 55 | } 56 | 57 | public interface FiterModeCallback { 58 | 59 | FilterMode geFiterMode(); 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/DataSelectContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.LayoutRes; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.widget.CompoundButton; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | 12 | import java.util.List; 13 | 14 | import widget.tamic.com.filterviewsipmle.R; 15 | 16 | /** 17 | * Created by Tamic on 2017-07-28 18 | */ 19 | public class DataSelectContainer extends ConditionContainer { 20 | 21 | public DataSelectContainer(Context context) { 22 | super(context); 23 | init(); 24 | } 25 | 26 | private TextView mTvTitle; 27 | private CompoundButton mTvDateStart; 28 | private CompoundButton mTvDateEnd; 29 | private int mLastStart; 30 | private int mLastEnd; 31 | private static final String DATA_FORMAT = "yyyy/MM/dd"; 32 | 33 | private void init() { 34 | setContentView(R.layout.layout_condition_data_select); 35 | mTvTitle = (TextView) findViewById(R.id.tv_info_condition_tilte); 36 | mTvDateStart = (CompoundButton) findViewById(R.id.tv_title_condition_data_start); 37 | mTvDateEnd = (CompoundButton) findViewById(R.id.tv_info_condition_data_end); 38 | 39 | 40 | mTvDateEnd.setOnClickListener(new OnClickListener() { 41 | 42 | @Override 43 | public void onClick(View v) { 44 | mTvDateStart.setText("6546465465"); 45 | mItem.end = 43434343; 46 | Toast.makeText(getContext(), "DateEnd", Toast.LENGTH_SHORT).show(); 47 | ((GridContainer) getParent().getParent().getParent()).reSet(); 48 | } 49 | }); 50 | mTvDateStart.setOnClickListener(new OnClickListener() { 51 | 52 | @Override 53 | public void onClick(View v) { 54 | Toast.makeText(getContext(), "DateStart", Toast.LENGTH_SHORT).show(); 55 | mTvDateEnd.setText("454545"); 56 | mItem.start = 787834; 57 | ((GridContainer) getParent().getParent().getParent()).reSet(); 58 | } 59 | }); 60 | 61 | 62 | } 63 | 64 | public View setContentView(@LayoutRes int id) { 65 | return LayoutInflater.from(getContext()).inflate(id, this); 66 | } 67 | 68 | public void reSet() { 69 | mTvDateStart.setText("开始时间"); 70 | mTvDateEnd.setText("结束时间"); 71 | } 72 | 73 | private DateSelectConditionItem mItem; 74 | 75 | @Override 76 | public NewConditionItem getConditionItem() { 77 | return mItem; 78 | } 79 | 80 | @Override 81 | public void setConditionItem(NewConditionItem item) { 82 | mItem = (DateSelectConditionItem) item; 83 | mTvTitle.setText(mItem.name); 84 | mLastStart = mItem.start; 85 | mLastEnd = mItem.end; 86 | if (mLastStart == 0 && mLastEnd == 0) { 87 | reSet(); 88 | return; 89 | } 90 | if (mLastStart == 0) { 91 | mTvDateStart.setText("开始时间"); 92 | } else { 93 | mTvDateStart.setText("" + mLastStart); 94 | } 95 | if (mLastEnd == 0) { 96 | mTvDateEnd.setText("结束时间"); 97 | } else { 98 | mTvDateEnd.setText("" + mLastEnd); 99 | } 100 | } 101 | 102 | @Override 103 | public NewConditionItem create(List itemList) { 104 | //todo create you ConditionItem, Usually not youself implement it! 105 | return null; 106 | } 107 | 108 | @Override 109 | public void addContentView(View view) { 110 | //todo add you childView to rootView , Usually not youself layout the View! 111 | addView(view); 112 | } 113 | 114 | static private int adjust(int base, int step, int value) { 115 | int steps = (value - base) / step; 116 | float r = (float) ((value - base) % step) / (float) step; 117 | return base + steps * step + Math.round(r) * step; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/DateSelectConditionItem.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | /** 4 | * Created by Tamic on 2017-07-28. 5 | */ 6 | public class DateSelectConditionItem extends NewConditionItem { 7 | 8 | public interface DateRangeInfo { 9 | 10 | String getInfo(DateSelectConditionItem item); 11 | } 12 | 13 | static public final DateRangeInfo DATE_INFO = new DateRangeInfo() { 14 | 15 | @Override 16 | public String getInfo(DateSelectConditionItem item) { 17 | if (item.start == 0 || item.end == 0) { 18 | return " "; 19 | } 20 | return item.start + ":" + item.end; 21 | } 22 | }; 23 | 24 | public int start, end; 25 | public int from, to; 26 | public boolean isRefresh = true; 27 | public DateRangeInfo rangeInfo = DATE_INFO; 28 | 29 | 30 | public DateSelectConditionItem(int type) { 31 | super(type); 32 | } 33 | 34 | public DateSelectConditionItem(NewConditionItem parent, int id, String name, 35 | int start, int end) { 36 | super(parent, id, name, true, TYPE_DATA); 37 | this.start = start; 38 | this.end = end; 39 | /* if (this.parent != null) { 40 | this.parent.subItems.add(this); 41 | }*/ 42 | } 43 | 44 | 45 | public NewConditionItem createConditionItem(int type) { 46 | return new DateSelectConditionItem(type); 47 | } 48 | 49 | @Override 50 | public void clear() { 51 | super.clear(); 52 | if (isRefresh) { 53 | start = 0; 54 | end = 0; 55 | } 56 | isRefresh = false; 57 | 58 | } 59 | 60 | @Override 61 | public void reset() { 62 | super.reset(); 63 | from = -1; 64 | to = -1; 65 | } 66 | 67 | public String getInfo() { 68 | if (rangeInfo == null) { 69 | return ""; 70 | } else { 71 | return rangeInfo.getInfo(this); 72 | } 73 | } 74 | 75 | @Override 76 | public NewConditionItem clone() { 77 | DateSelectConditionItem cloned = new DateSelectConditionItem(type); 78 | cloned.id = this.id; 79 | cloned.name = this.name; 80 | cloned.selected = this.selected; 81 | cloned.canReset = this.canReset; 82 | cloned.type = this.type; 83 | cloned.start = this.start; 84 | cloned.end = this.end; 85 | cloned.processor = this.processor; 86 | cloned.isRefresh = this.isRefresh; 87 | for (NewConditionItem subItem : this.subItems) { 88 | NewConditionItem clonedSubItem = subItem.clone(); 89 | clonedSubItem.parent = cloned; 90 | cloned.subItems.add(clonedSubItem); 91 | } 92 | cloned.start = this.start; 93 | cloned.end = this.end; 94 | cloned.from = this.from; 95 | cloned.to = this.to; 96 | cloned.rangeInfo = this.rangeInfo; 97 | return cloned; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/DateView.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.support.annotation.RequiresApi; 6 | import android.util.AttributeSet; 7 | import android.view.LayoutInflater; 8 | import android.widget.CompoundButton; 9 | 10 | import widget.tamic.com.filterviewsipmle.R; 11 | 12 | /** 13 | * Created by Tamic on 2017-08-02. 14 | */ 15 | 16 | public class DateView extends CompoundButton { 17 | 18 | public DateView(Context context) { 19 | super(context); 20 | init(); 21 | } 22 | 23 | 24 | public DateView(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | init(); 27 | } 28 | 29 | public DateView(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | init(); 32 | } 33 | 34 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 35 | public DateView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 36 | super(context, attrs, defStyleAttr, defStyleRes); 37 | init(); 38 | } 39 | 40 | private void init() { 41 | LayoutInflater.from(getContext()) 42 | .inflate(R.layout.item_condition_grid, null); 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/FilterBean.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | 4 | import java.util.ArrayList; 5 | 6 | /** 7 | * FilterBean 8 | */ 9 | public class FilterBean { 10 | 11 | private int id; 12 | private String name; 13 | private String sName; 14 | private ArrayList list; 15 | 16 | public FilterBean() { 17 | } 18 | 19 | public int getId() { 20 | return id; 21 | } 22 | 23 | public void setId(int id) { 24 | this.id = id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public String getsName() { 36 | return sName; 37 | } 38 | 39 | public void setsName(String sName) { 40 | this.sName = sName; 41 | } 42 | 43 | public ArrayList getList() { 44 | return list; 45 | } 46 | 47 | public void setList(ArrayList list) { 48 | this.list = list; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/FilterContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.LinearLayout; 9 | import android.widget.RelativeLayout; 10 | import android.widget.TextView; 11 | 12 | 13 | import java.util.List; 14 | 15 | import widget.tamic.com.filterviewsipmle.R; 16 | 17 | /** 18 | * Created by SUNTAIYI on 2016-08-24. 19 | */ 20 | public class FilterContainer extends ConditionContainer implements View.OnClickListener { 21 | public FilterContainer(Context context) { 22 | super(context); 23 | init(); 24 | } 25 | 26 | public FilterContainer(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | init(); 29 | } 30 | 31 | private LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 32 | ViewGroup.LayoutParams.MATCH_PARENT, 33 | ViewGroup.LayoutParams.WRAP_CONTENT 34 | ); 35 | 36 | private LinearLayout mList; 37 | private TextView mTvReset; 38 | private TextView mTvConfirm; 39 | 40 | private void init() { 41 | LayoutInflater.from(getContext()).inflate(R.layout.layout_condition_filter, this); 42 | mList = (LinearLayout) findViewById(R.id.list_condition_filter); 43 | mTvReset = (TextView) findViewById(R.id.tv_reset_condition); 44 | mTvConfirm = (TextView) findViewById(R.id.tv_confirm_condition); 45 | mTvReset.setOnClickListener(this); 46 | mTvConfirm.setOnClickListener(this); 47 | findViewById(R.id.blank_condition).setOnClickListener(this); 48 | } 49 | 50 | private NewConditionItem mRoot; 51 | 52 | @Override 53 | public NewConditionItem getConditionItem() { 54 | return mRoot; 55 | } 56 | 57 | @Override 58 | public void setConditionItem(NewConditionItem item) { 59 | if (mRoot != item) { 60 | mRoot = item; 61 | mList.removeAllViews(); 62 | if (item == null) { 63 | return; 64 | } 65 | LayoutInflater inflater = LayoutInflater.from(getContext()); 66 | for (NewConditionItem subItem : item.subItems) { 67 | 68 | if (subItem.type == NewConditionItem.TYPE_GRID) { 69 | ConditionContainer container = new GridContainer(getContext()); 70 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 71 | ViewGroup.LayoutParams.MATCH_PARENT, 72 | ViewGroup.LayoutParams.WRAP_CONTENT 73 | ); 74 | container.setLayoutParams(lp); 75 | container.setTag(subItem); 76 | addContentView(container); 77 | container.setConditionItem(subItem); 78 | 79 | } /*else if (subItem.type == NewConditionItem.TYPE_GRID) { 80 | ConditionContainer container = null; 81 | container = new GridContainer(getContext()); 82 | // RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 83 | 84 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 85 | ViewGroup.LayoutParams.WRAP_CONTENT, 86 | ViewGroup.LayoutParams.WRAP_CONTENT 87 | ); 88 | container.setLayoutParams(lp); 89 | *//* if(subItem.type != NewConditionItem.TYPE_DATA) { 90 | for (NewConditionItem subItem2 : subItem.subItems) { 91 | if(subItem2.type == NewConditionItem.TYPE_DATA) { 92 | ConditionContainer container2 = new DataSelectContainer(getContext()); 93 | LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams( 94 | ViewGroup.LayoutParams.MATCH_PARENT, 95 | ViewGroup.LayoutParams.WRAP_CONTENT 96 | ); 97 | container2.setLayoutParams(lp2); 98 | container2.setTag(subItem2); 99 | container2.setConditionItem(subItem2); 100 | 101 | if (container != null) { 102 | container.addContentView(container2); 103 | } 104 | 105 | } else { 106 | container.setTag(subItem); 107 | container.setConditionItem(subItem); 108 | } 109 | 110 | container.setTag(subItem); 111 | // container.setConditionItem(subItem); 112 | } 113 | }*//* 114 | 115 | mList.addView(container); 116 | container.setConditionItem(subItem); 117 | 118 | } */else if (subItem.type == NewConditionItem.TYPE_RANGE) { 119 | ConditionContainer container = new RangeContainer(getContext()); 120 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 121 | ViewGroup.LayoutParams.MATCH_PARENT, 122 | ViewGroup.LayoutParams.WRAP_CONTENT 123 | ); 124 | container.setLayoutParams(lp); 125 | container.setTag(subItem); 126 | addContentView(container); 127 | container.setConditionItem(subItem); 128 | 129 | } else if (subItem.type == NewConditionItem.TYPE_DATA) { 130 | ConditionContainer container = new DataSelectContainer(getContext()); 131 | container.setLayoutParams(lp); 132 | container.setTag(subItem); 133 | addContentView(container); 134 | container.setConditionItem(subItem); 135 | } else { 136 | continue; 137 | } 138 | if (subItem != item.subItems.get(item.subItems.size() - 1)) { 139 | inflater.inflate(R.layout.layout_condition_divider, mList); 140 | } 141 | } 142 | View bottomBlank = new View(getContext()); 143 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 144 | ViewGroup.LayoutParams.MATCH_PARENT, UIUtil.dip2px(getContext(), 30)); 145 | bottomBlank.setLayoutParams(lp); 146 | mList.addView(bottomBlank); 147 | } else if (mRoot != null) { 148 | for (int i = 0; i < mRoot.subItems.size(); ++i) { 149 | NewConditionItem subItem = mRoot.subItems.get(i); 150 | ConditionContainer container = (ConditionContainer) mList.findViewWithTag(subItem); 151 | container.setConditionItem(subItem); 152 | } 153 | } 154 | } 155 | 156 | @Override 157 | public NewConditionItem create(List itemList) { 158 | return null; 159 | } 160 | 161 | @Override 162 | public void addContentView(View v) { 163 | v.setLayoutParams(lp); 164 | mList.addView(v); 165 | } 166 | 167 | 168 | @Override 169 | public void onClick(View v) { 170 | int i = v.getId(); 171 | if (i == R.id.tv_reset_condition) { 172 | if (mRoot != null) { 173 | mRoot.clear(); 174 | mRoot.reset(); 175 | setConditionItem(mRoot); 176 | } 177 | } else if (i == R.id.tv_confirm_condition) { 178 | mController.confirm(FilterContainer.this); 179 | } else if (i == R.id.blank_condition) { 180 | mController.cancel(FilterContainer.this); 181 | } else { 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/FilterMode.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | /** 4 | * Created by tamic on 2017-08-03. 5 | */ 6 | public enum FilterMode { 7 | ITEM, 8 | ENTER 9 | } 10 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/FliterResultItem.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | 4 | import java.util.ArrayList; 5 | 6 | /** 7 | * Created by LIUYONGKUI726 on 2018-03-06. 8 | */ 9 | 10 | public class FliterResultItem { 11 | 12 | public int id; 13 | public String name; 14 | private ArrayList subItems; 15 | 16 | 17 | public void setSubItems(ArrayList subItems) { 18 | this.subItems = subItems; 19 | } 20 | 21 | public boolean isEmpty() { 22 | return subItems == null || subItems.size() == 0; 23 | } 24 | 25 | public ArrayList subItems() { 26 | return subItems; 27 | } 28 | 29 | public FliterResultItem createResult(NewConditionItem item) { 30 | FliterResultItem fliterResultItem = null; 31 | if (item.selected) { 32 | fliterResultItem = new FliterResultItem(); 33 | fliterResultItem.id = item.id; 34 | fliterResultItem.name = item.name; 35 | fliterResultItem.subItems = new ArrayList<>(); 36 | for (NewConditionItem subItem : item.subItems) { 37 | if (!subItem.selected) { 38 | continue; 39 | } 40 | fliterResultItem.subItems.add(fliterResultItem.createResult(subItem)); 41 | } 42 | } 43 | return fliterResultItem; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/GridContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.CompoundButton; 8 | import android.widget.GridLayout; 9 | import android.widget.LinearLayout; 10 | import android.widget.RelativeLayout; 11 | import android.widget.TextView; 12 | 13 | 14 | import java.util.List; 15 | 16 | import widget.tamic.com.filterviewsipmle.R; 17 | 18 | /** 19 | * Update by Tamic on 2017-07-27 20 | * 21 | * @link {https://github.com/Tamicer/FilterBar} 22 | */ 23 | public class GridContainer extends ConditionContainer implements CompoundButton.OnCheckedChangeListener, View.OnLayoutChangeListener { 24 | 25 | 26 | public GridContainer(Context context) { 27 | super(context); 28 | init(); 29 | } 30 | 31 | private TextView mTvTitle; 32 | private GridLayout mGrid; 33 | private LinearLayout mLayout; 34 | private CompoundButton btn; 35 | private int columnCount = 4; 36 | private View view; 37 | private TextView mTvDateStart; 38 | private TextView mTvDateEnd; 39 | private RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( 40 | LayoutParams.MATCH_PARENT, 41 | LayoutParams.WRAP_CONTENT); 42 | 43 | private void init() { 44 | LayoutInflater.from(getContext()).inflate(R.layout.layout_condition_grid, this); 45 | mTvTitle = (TextView) findViewById(R.id.tv_title_condition_grid); 46 | mGrid = (GridLayout) findViewById(R.id.grid_condition_grid); 47 | mGrid.setColumnCount(columnCount); 48 | mLayout = (LinearLayout) findViewById(R.id.grid_condition_custom_view); 49 | mGrid.addOnLayoutChangeListener(this); 50 | 51 | } 52 | 53 | 54 | public void addFooterView(View view) { 55 | this.view = view; 56 | mLayout.removeAllViews(); 57 | mLayout.addView(view); 58 | } 59 | 60 | public void addHeaderView(View view) { 61 | addView(view); 62 | } 63 | 64 | private NewConditionItem mRoot; 65 | private DateSelectConditionItem item; 66 | 67 | @Override 68 | public NewConditionItem getConditionItem() { 69 | return mRoot; 70 | } 71 | 72 | @Override 73 | public void setConditionItem(NewConditionItem item) { 74 | mRoot = item; 75 | mTvTitle.setText(item.name); 76 | mGrid.removeAllViews(); 77 | for (NewConditionItem subItem : mRoot.subItems) { 78 | if (subItem.type != NewConditionItem.TYPE_LIST) { 79 | if (subItem.type == NewConditionItem.TYPE_DATA) { 80 | this.item = (DateSelectConditionItem) subItem; 81 | DataSelectContainer container2 = new DataSelectContainer(getContext()); 82 | LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams( 83 | ViewGroup.LayoutParams.MATCH_PARENT, 84 | ViewGroup.LayoutParams.WRAP_CONTENT 85 | ); 86 | container2.setLayoutParams(lp2); 87 | container2.setTag(subItem); 88 | container2.setConditionItem(subItem); 89 | addFooterView(container2); 90 | } 91 | continue; 92 | 93 | } 94 | btn = (CompoundButton) LayoutInflater.from(getContext()) 95 | .inflate(R.layout.item_condition_grid, mGrid, false); 96 | btn.setTag(subItem); 97 | btn.setText(subItem.name); 98 | btn.setChecked(subItem.selected); 99 | btn.setOnCheckedChangeListener(this); 100 | addToGrid(btn); 101 | } 102 | } 103 | 104 | @Override 105 | public NewConditionItem create(List itemList) { 106 | return null; 107 | } 108 | 109 | @Override 110 | public void addContentView(View view) { 111 | addFooterView(view); 112 | } 113 | 114 | private void addToGrid(View v) { 115 | if (mGrid.getWidth() > 0) { 116 | ViewGroup.LayoutParams lp = v.getLayoutParams(); 117 | lp.width = mGrid.getWidth() / columnCount; 118 | v.setLayoutParams(lp); 119 | } 120 | mGrid.addView(v); 121 | } 122 | 123 | public void clear() { 124 | for (int i = 0; i < mGrid.getChildCount(); i++) { 125 | CompoundButton btn = (CompoundButton) mGrid.getChildAt(i); 126 | NewConditionItem item = (NewConditionItem) btn.getTag(); 127 | item.reset(); 128 | setConditionItem(item); 129 | } 130 | } 131 | 132 | public void reSet() { 133 | if (item != null) { 134 | item.isRefresh = false; 135 | } 136 | if (mRoot != null) { 137 | mRoot.clear(); 138 | mRoot.reset(); 139 | setConditionItem(mRoot); 140 | } 141 | } 142 | 143 | @Override 144 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 145 | 146 | NewConditionItem item = (NewConditionItem) buttonView.getTag(); 147 | boolean refresh = item.parent.processSubItems(item, buttonView.isChecked()); 148 | buttonView.setChecked(item.selected); 149 | if (refresh) { 150 | setConditionItem(item.parent); 151 | } 152 | 153 | } 154 | 155 | @Override 156 | public void onLayoutChange(View v, int left, int top, int right, int bottom, 157 | int oldLeft, int oldTop, int oldRight, int oldBottom) { 158 | if (right - left == oldRight - oldLeft) { 159 | return; 160 | } 161 | int cellWidth = mGrid.getWidth() / columnCount; 162 | for (int i = 0; i < mGrid.getChildCount(); ++i) { 163 | View cell = mGrid.getChildAt(i); 164 | GridLayout.LayoutParams lp = (GridLayout.LayoutParams) cell.getLayoutParams(); 165 | lp.width = cellWidth; 166 | cell.setLayoutParams(lp); 167 | } 168 | } 169 | 170 | 171 | } 172 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/GroupFilterItem.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * GroupFilterItem 11 | */ 12 | public class GroupFilterItem extends BaseFilterItem implements Parcelable { 13 | 14 | private ArrayList children; 15 | 16 | public ArrayList getChildren() { 17 | return children; 18 | } 19 | 20 | public void setChildren(ArrayList children) { 21 | this.children = children; 22 | } 23 | 24 | @Override 25 | public void writeToParcel(Parcel dest, int flags) { 26 | super.writeToParcel(dest, flags); 27 | if (null == children || 0 == children.size()) { 28 | dest.writeInt(0); 29 | } else { 30 | dest.writeInt(children.size()); 31 | for (BaseFilterItem item : children) { 32 | dest.writeParcelable(item, flags); 33 | } 34 | } 35 | } 36 | 37 | public static Creator CREATOR = new Creator() { 38 | 39 | @Override 40 | public GroupFilterItem createFromParcel(Parcel source) { 41 | GroupFilterItem item = new GroupFilterItem(); 42 | item.setCode(source.readInt()); 43 | item.setName(source.readString()); 44 | int size = source.readInt(); 45 | ArrayList items = new ArrayList<>(); 46 | if (0 != size) { 47 | for (int i = 0; i < size; i++) { 48 | items.add((BaseFilterItem) source 49 | .readParcelable(BaseFilterItem.class 50 | .getClassLoader())); 51 | } 52 | } 53 | item.setChildren(items); 54 | return item; 55 | } 56 | 57 | @Override 58 | public GroupFilterItem[] newArray(int size) { 59 | return new GroupFilterItem[size]; 60 | } 61 | }; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/ListFilterBean.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * Created by suntaiyi on 2016-09-21. 9 | */ 10 | public class ListFilterBean { 11 | 12 | static public class Item { 13 | 14 | public int id; 15 | public String name; 16 | public String value; 17 | public List children = new ArrayList<>(); 18 | } 19 | 20 | static public class ItemList { 21 | 22 | public String name; 23 | public List list = new ArrayList<>(); 24 | } 25 | 26 | static public class Range { 27 | 28 | public String name; 29 | public int min; 30 | public int max; 31 | public int step; 32 | public String unit; 33 | } 34 | 35 | 36 | public ItemList region; 37 | public ItemList layout; 38 | public ItemList sort; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/NewConditionItem.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by SUNTAIYI on 2016-08-22. 8 | */ 9 | public class NewConditionItem extends BaseBean { 10 | 11 | public interface SubItemsProcessor { 12 | 13 | boolean check(List items, NewConditionItem selectChanged); 14 | 15 | boolean uncheck(List items, NewConditionItem selectChanged); 16 | } 17 | 18 | static public final SubItemsProcessor DEFAULT = new SubItemsProcessor() { 19 | 20 | @Override 21 | public boolean check(List items, NewConditionItem selectChanged) { 22 | if (selectChanged.isSingled) { //当item的只能独立选中的 23 | for (NewConditionItem item : items) { //将其他选中的item都clear 24 | item.selected = false; 25 | item.clear(); 26 | } 27 | } else { 28 | for (NewConditionItem item : items) { 29 | if (item.isSingled) { 30 | item.selected = false; 31 | item.clear(); 32 | } 33 | } 34 | } 35 | selectChanged.selected = true; 36 | selectChanged.reset(); 37 | return true; 38 | } 39 | 40 | @Override 41 | public boolean uncheck(List items, NewConditionItem selectChanged) { 42 | selectChanged.selected = false; 43 | selectChanged.clear(); 44 | return true; 45 | } 46 | }; 47 | static public final SubItemsProcessor RADIO_LIST = new SubItemsProcessor() { 48 | 49 | @Override 50 | public boolean check(List items, NewConditionItem selectChanged) { 51 | if (items.size() == 0) { 52 | return false; 53 | } 54 | if (selectChanged.selected) { 55 | return false; 56 | } 57 | for (NewConditionItem item : items) { 58 | item.selected = false; 59 | item.clear(); 60 | } 61 | selectChanged.selected = true; 62 | selectChanged.reset(); 63 | return true; 64 | } 65 | 66 | @Override 67 | public boolean uncheck(List items, NewConditionItem selectChanged) { 68 | return false; 69 | } 70 | }; 71 | static public final SubItemsProcessor RADIO_LIST_NO_CLEAR = new SubItemsProcessor() { 72 | 73 | @Override 74 | public boolean check(List items, NewConditionItem selectChanged) { 75 | if (items.size() == 0) { 76 | return false; 77 | } 78 | if (selectChanged.selected) { 79 | return false; 80 | } 81 | for (NewConditionItem item : items) { 82 | item.selected = false; 83 | } 84 | selectChanged.selected = true; 85 | selectChanged.reset(); 86 | return true; 87 | } 88 | 89 | @Override 90 | public boolean uncheck(List items, NewConditionItem selectChanged) { 91 | return false; 92 | } 93 | }; 94 | static public final SubItemsProcessor RADIO_LIST_UNCHECKABLE = new SubItemsProcessor() { 95 | 96 | @Override 97 | public boolean check(List items, NewConditionItem selectChanged) { 98 | if (items.size() == 0) { 99 | return false; 100 | } 101 | if (selectChanged.selected) { 102 | return false; 103 | } 104 | for (NewConditionItem item : items) { 105 | item.selected = false; 106 | item.clear(); 107 | } 108 | selectChanged.selected = true; 109 | selectChanged.reset(); 110 | return true; 111 | } 112 | 113 | @Override 114 | public boolean uncheck(List items, NewConditionItem selectChanged) { 115 | selectChanged.selected = false; 116 | selectChanged.clear(); 117 | return true; 118 | } 119 | }; 120 | static public final SubItemsProcessor ALL_AT_ZERO = new SubItemsProcessor() { 121 | 122 | @Override 123 | public boolean check(List items, NewConditionItem selectChanged) { 124 | if (items.size() == 0) { 125 | return false; 126 | } 127 | if (items.get(0) == selectChanged) { 128 | for (NewConditionItem item : items) { 129 | item.selected = false; 130 | item.clear(); 131 | } 132 | selectChanged.selected = true; 133 | selectChanged.reset(); 134 | return true; 135 | } else { 136 | items.get(0).selected = false; 137 | items.get(0).clear(); 138 | selectChanged.selected = true; 139 | selectChanged.reset(); 140 | return true; 141 | } 142 | } 143 | 144 | @Override 145 | public boolean uncheck(List items, NewConditionItem selectChanged) { 146 | if (items.size() == 0) { 147 | return false; 148 | } 149 | 150 | if (items.get(0) != selectChanged) { 151 | selectChanged.selected = false; 152 | selectChanged.clear(); 153 | boolean all = true; 154 | for (NewConditionItem item : items) { 155 | if (item != items.get(0)) { 156 | all = all && !item.selected; 157 | } 158 | } 159 | if (all) { 160 | items.get(0).selected = true; 161 | items.get(0).reset(); 162 | } 163 | return true; 164 | } 165 | return false; 166 | } 167 | }; 168 | static public final SubItemsProcessor CLEAR_UNCLE_ON_CHECK = new SubItemsProcessor() { 169 | 170 | @Override 171 | public boolean check(List items, NewConditionItem selectChanged) { 172 | selectChanged.selected = true; 173 | selectChanged.reset(); 174 | NewConditionItem parent = selectChanged.parent; 175 | if (parent == null) { 176 | return true; 177 | } 178 | NewConditionItem grand = parent.parent; 179 | if (grand == null) { 180 | return true; 181 | } 182 | for (NewConditionItem uncle : grand.subItems) { 183 | if (uncle == parent) { 184 | continue; 185 | } 186 | uncle.clear(); 187 | } 188 | return true; 189 | } 190 | 191 | @Override 192 | public boolean uncheck(List items, NewConditionItem selectChanged) { 193 | selectChanged.selected = false; 194 | selectChanged.clear(); 195 | return true; 196 | } 197 | }; 198 | static public final int TYPE_LIST = 0; 199 | static public final int TYPE_GRID = 1; 200 | static public final int TYPE_RANGE = 2; 201 | static public final int TYPE_DATA = 3; 202 | static public final int TYPE_DNA_GRID_STYLE1 = 10; 203 | static public final int TYPE_DNA_GRID_STYLE2 = 11; 204 | static public final int TYPE_DNA_GRID_STYLE3 = 12; 205 | static public final int TYPE_DNA_GRID_STYLE4 = 13; 206 | static public final int TYPE_DNA_TAB_GRID = 20; 207 | static public final int TYPE_DNA_MUTUAL_TAB_GRID = 21; 208 | static public final int TYPE_DNA_ONE_ROW = 22; 209 | static public final int TYPE_DNA_FAVOR_GRID = 23; 210 | static public final int TYPE_EMPTY = -1; 211 | 212 | public NewConditionItem parent; 213 | public int id; 214 | public String name; 215 | public boolean selected; 216 | public boolean canReset = true; 217 | public int type = TYPE_LIST; 218 | public final List subItems = new ArrayList<>(); 219 | public SubItemsProcessor processor = DEFAULT; 220 | public boolean isSingled = false; //此时的item是否是独立选择 221 | 222 | public NewConditionItem() { 223 | type = TYPE_EMPTY; 224 | } 225 | 226 | public NewConditionItem(int type) { 227 | this.type = type; 228 | } 229 | 230 | public NewConditionItem createConditionItem(int type) { 231 | return new NewConditionItem(type); 232 | } 233 | 234 | public NewConditionItem(NewConditionItem parent, int id, String name) { 235 | this(parent, id, name, false); 236 | } 237 | 238 | public NewConditionItem(NewConditionItem parent, int id, String name, boolean selected) { 239 | this(parent, id, name, selected, TYPE_LIST); 240 | } 241 | 242 | public NewConditionItem(NewConditionItem parent, int id, String name, boolean selected, int type) { 243 | this.parent = parent; 244 | this.id = id; 245 | this.name = name; 246 | this.selected = selected; 247 | this.type = type; 248 | this.isSingled = (id == 0); 249 | if (this.parent != null) { 250 | this.parent.subItems.add(this); 251 | } 252 | } 253 | 254 | public NewConditionItem subItemById(int id) { 255 | if (this.type == TYPE_EMPTY) { 256 | return createConditionItem(TYPE_EMPTY); 257 | } 258 | for (NewConditionItem item : subItems) { 259 | if (item.id == id) { 260 | return item; 261 | } 262 | } 263 | return createConditionItem(TYPE_EMPTY); 264 | } 265 | 266 | public List selectedSubItems() { 267 | List list = new ArrayList<>(); 268 | for (NewConditionItem item : subItems) { 269 | if (item.selected) { 270 | list.add(item); 271 | } 272 | } 273 | return list; 274 | } 275 | 276 | public List unselectedSubItems() { 277 | List list = new ArrayList<>(); 278 | for (NewConditionItem item : subItems) { 279 | if (!item.selected) { 280 | list.add(item); 281 | } 282 | } 283 | return list; 284 | } 285 | 286 | public NewConditionItem firstSelectedSubItem() { 287 | for (NewConditionItem item : subItems) { 288 | if (item.selected) { 289 | return item; 290 | } 291 | } 292 | return null; 293 | } 294 | 295 | public NewConditionItem root() { 296 | NewConditionItem item = this; 297 | while (item.parent != null) { 298 | item = item.parent; 299 | } 300 | return item; 301 | } 302 | 303 | public void rootClear() { 304 | NewConditionItem root = this.root(); 305 | for (NewConditionItem top : root.subItems) { 306 | if (!top.selected) { 307 | top.clear(); 308 | } 309 | } 310 | } 311 | 312 | public boolean isEmpty() { 313 | return type == TYPE_EMPTY; 314 | } 315 | 316 | public boolean isLeaf() { 317 | return subItems.size() == 0; 318 | } 319 | 320 | public boolean processSubItems(NewConditionItem selectedChanged, boolean checked) { 321 | if (processor != null) { 322 | if (checked) { 323 | return processor.check(subItems, selectedChanged); 324 | } else { 325 | return processor.uncheck(subItems, selectedChanged); 326 | } 327 | } else { 328 | return false; 329 | } 330 | } 331 | 332 | public void clear() { 333 | for (NewConditionItem subItem : subItems) { 334 | subItem.selected = false; 335 | subItem.clear(); 336 | } 337 | } 338 | 339 | public void reset() { 340 | NewConditionItem selected = this.firstSelectedSubItem(); 341 | if (canReset && selected == null && subItems.size() > 0) { 342 | 343 | if (subItems.get(0).type != NewConditionItem.TYPE_DATA) { 344 | subItems.get(0).selected = true; 345 | } else { 346 | 347 | } 348 | //subItems.subItemById(0).reset(); 349 | for (NewConditionItem subItem : subItems) { 350 | if (subItem.type == NewConditionItem.TYPE_DATA) { 351 | continue; 352 | } 353 | subItem.reset(); 354 | } 355 | } 356 | } 357 | 358 | public NewConditionItem clone() { 359 | NewConditionItem cloned = createConditionItem(this.type); 360 | cloned.id = this.id; 361 | cloned.name = this.name; 362 | cloned.selected = this.selected; 363 | cloned.canReset = this.canReset; 364 | cloned.type = this.type; 365 | cloned.processor = this.processor; 366 | cloned.isSingled = this.isSingled; 367 | for (NewConditionItem subItem : this.subItems) { 368 | NewConditionItem clonedSubItem = subItem.clone(); 369 | clonedSubItem.parent = cloned; 370 | cloned.subItems.add(clonedSubItem); 371 | } 372 | return cloned; 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/RangeConditionItem.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | /** 4 | * Created by SUNTAIYI on 2016-08-24. 5 | */ 6 | public class RangeConditionItem extends NewConditionItem { 7 | 8 | public interface RangeInfo { 9 | String getInfo(RangeConditionItem item); 10 | } 11 | 12 | static public final RangeInfo PRICE_INFO = new RangeInfo() { 13 | @Override 14 | public String getInfo(RangeConditionItem item) { 15 | if (item.from == item.min && item.to == item.max) { 16 | return "不限"; 17 | } 18 | if (item.from > item.min && item.to == item.max) { 19 | return String.format("高于%.1f 万元", (float)item.from/10); 20 | } 21 | if (item.from == item.min && item.to < item.max) { 22 | return String.format("低于%.1f 万元", (float)item.to/10); 23 | } 24 | 25 | return String.format("%.1f 万元 — %.1f 万元", (float)item.from/10, (float)item.to/10); 26 | } 27 | }; 28 | static public final RangeInfo AREA_INFO = new RangeInfo() { 29 | @Override 30 | public String getInfo(RangeConditionItem item) { 31 | if (item.from == item.min && item.to == item.max) { 32 | return "不限"; 33 | } 34 | if (item.from > item.min && item.to == item.max) { 35 | return String.format("大于%d㎡", item.from); 36 | } 37 | if (item.from == item.min && item.to < item.max) { 38 | return String.format("小于%d㎡", item.to); 39 | } 40 | 41 | return String.format("%d㎡ — %d㎡", item.from, item.to); 42 | } 43 | }; 44 | static public final RangeInfo RENT_PRICE_INFO = new RangeInfo() { 45 | @Override 46 | public String getInfo(RangeConditionItem item) { 47 | if (item.from == item.min && item.to == item.max) { 48 | return "不限"; 49 | } 50 | if (item.from > item.min && item.to == item.max) { 51 | return String.format("高于%.1f 元", (float)item.from/10); 52 | } 53 | if (item.from == item.min && item.to < item.max) { 54 | return String.format("低于%.1f 元", (float)item.to/10); 55 | } 56 | 57 | return String.format("%.1f 元 — %.1f 元", (float)item.from/10, (float)item.to/10); 58 | } 59 | }; 60 | 61 | public int min, max, step; 62 | public int from, to; 63 | public RangeInfo rangeInfo = null; 64 | 65 | public RangeConditionItem() { 66 | super(); 67 | } 68 | 69 | public RangeConditionItem(NewConditionItem parent, int id, String name, 70 | int min, int max, int step, int from, int to) { 71 | super(parent, id, name, true, TYPE_RANGE); 72 | this.min = min; 73 | this.max = max; 74 | this.step = step; 75 | this.from = from; 76 | this.to = to; 77 | } 78 | 79 | @Override 80 | public void clear() { 81 | super.clear(); 82 | from = min; 83 | to = max; 84 | } 85 | 86 | public String getInfo() { 87 | if (rangeInfo == null) { 88 | return ""; 89 | } else { 90 | return rangeInfo.getInfo(this); 91 | } 92 | } 93 | 94 | @Override 95 | public NewConditionItem clone() { 96 | RangeConditionItem cloned = new RangeConditionItem(); 97 | cloned.id = this.id; 98 | cloned.name = this.name; 99 | cloned.selected = this.selected; 100 | cloned.canReset = this.canReset; 101 | cloned.type = this.type; 102 | cloned.processor = this.processor; 103 | for (NewConditionItem subItem : this.subItems) { 104 | NewConditionItem clonedSubItem = subItem.clone(); 105 | clonedSubItem.parent = cloned; 106 | cloned.subItems.add(clonedSubItem); 107 | } 108 | cloned.min = this.min; 109 | cloned.max = this.max; 110 | cloned.step = this.step; 111 | cloned.from = this.from; 112 | cloned.to = this.to; 113 | cloned.rangeInfo = this.rangeInfo; 114 | return cloned; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/RangeContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | 9 | import java.util.List; 10 | 11 | import widget.tamic.com.filterviewsipmle.R; 12 | 13 | /** 14 | * Update by Tamic on 2017-07-27 15 | * https://github.com/Tamicer/FilterBar 16 | */ 17 | public class RangeContainer extends ConditionContainer implements RangeSeekBar.OnRangeSeekBarChangeListener { 18 | 19 | public RangeContainer(Context context) { 20 | super(context); 21 | init(); 22 | } 23 | 24 | private TextView mTvTitle; 25 | private TextView mTvInfo; 26 | private RangeSeekBar mBar; 27 | private int mLastFrom; 28 | private int mLastTo; 29 | 30 | private void init() { 31 | LayoutInflater.from(getContext()).inflate(R.layout.layout_condition_range, this); 32 | 33 | mTvTitle = (TextView) findViewById(R.id.tv_title_condition_range); 34 | mTvInfo = (TextView) findViewById(R.id.tv_info_condition_range); 35 | mBar = (RangeSeekBar) findViewById(R.id.bar_range); 36 | mBar.setOnRangeSeekBarChangeListener(this); 37 | mBar.setNotifyWhileDragging(true); 38 | 39 | } 40 | 41 | private RangeConditionItem mItem; 42 | 43 | @Override 44 | public NewConditionItem getConditionItem() { 45 | return mItem; 46 | } 47 | 48 | @Override 49 | public void setConditionItem(NewConditionItem item) { 50 | mItem = (RangeConditionItem) item; 51 | mTvTitle.setText(mItem.name); 52 | mTvInfo.setText(mItem.getInfo()); 53 | mBar.setRangeValues(mItem.min, mItem.max); 54 | mBar.setSelectedMinValue(mItem.from); 55 | mBar.setSelectedMaxValue(mItem.to); 56 | mLastFrom = mItem.from; 57 | mLastTo = mItem.to; 58 | } 59 | 60 | @Override 61 | public NewConditionItem create(List itemList) { 62 | return null; 63 | } 64 | 65 | @Override 66 | public void addContentView(View view) { 67 | addView(view); 68 | } 69 | 70 | @Override 71 | public void onRangeSeekBarValuesChanged(RangeSeekBar bar, Integer minValue, Integer maxValue) { 72 | int adjustMin = adjust(mItem.min, mItem.step, minValue); 73 | int adjustMax = adjust(mItem.min, mItem.step, maxValue); 74 | if (adjustMin == adjustMax) { 75 | if (mLastFrom == adjustMin) { 76 | adjustMax += mItem.step; 77 | } else if (mLastTo == adjustMax) { 78 | adjustMin -= mItem.step; 79 | } 80 | } 81 | mItem.from = adjustMin; 82 | mItem.to = adjustMax; 83 | mTvInfo.setText(mItem.getInfo()); 84 | mBar.setSelectedMinValue(mItem.from); 85 | mBar.setSelectedMaxValue(mItem.to); 86 | mLastFrom = mItem.from; 87 | mLastTo = mItem.to; 88 | } 89 | 90 | static private int adjust(int base, int step, int value) { 91 | int steps = (value - base) / step; 92 | float r = (float) ((value - base) % step) / (float) step; 93 | return base + steps * step + Math.round(r) * step; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/RangeSeekBar.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Stephan Tittel and Yahoo Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package com.tamic.widget.filter; 18 | 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.graphics.Bitmap; 22 | import android.graphics.BitmapFactory; 23 | import android.graphics.Canvas; 24 | import android.graphics.Color; 25 | import android.graphics.Paint; 26 | import android.graphics.Paint.Style; 27 | import android.graphics.RectF; 28 | import android.os.Bundle; 29 | import android.os.Parcelable; 30 | import android.util.AttributeSet; 31 | import android.util.DisplayMetrics; 32 | import android.util.TypedValue; 33 | import android.view.MotionEvent; 34 | import android.view.ViewConfiguration; 35 | import android.widget.ImageView; 36 | 37 | import java.math.BigDecimal; 38 | 39 | import widget.tamic.com.filterviewsipmle.R; 40 | 41 | /** 42 | * Widget that lets users select a minimum and maximum value on a given numerical range. 43 | * The range value types can be one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
44 | *
45 | * Improved {@link MotionEvent} handling for smoother use, anti-aliased painting for improved aesthetics. 46 | * 47 | * @param The Number type of the range values. One of Long, Double, Integer, Float, Short, Byte or BigDecimal. 48 | * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de) 49 | * @author Peter Sinnott (psinnott@gmail.com) 50 | * @author Thomas Barrasso (tbarrasso@sevenplusandroid.org) 51 | * @author Alex Florescu (florescu@yahoo-inc.com) 52 | * @author Michael Keppler (bananeweizen@gmx.de) 53 | */ 54 | public class RangeSeekBar extends ImageView { 55 | 56 | public static final Integer DEFAULT_MINIMUM = 0; 57 | public static final Integer DEFAULT_MAXIMUM = 100; 58 | public static final int HEIGHT_IN_DP = 10; 59 | public static final int TEXT_LATERAL_PADDING_IN_DP = 3; 60 | private static final int INITIAL_PADDING_IN_DP = 8; 61 | private static final int COLOR_OUTSIDE_RANGE = 0xffcccccc; 62 | private static final int COLOR_INSIDE_RANGE = 0xffff8447; 63 | 64 | private final int LINE_HEIGHT_IN_DP = 3; 65 | private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 66 | private final Bitmap thumbImage = BitmapFactory.decodeResource( 67 | getResources(), R.drawable.range_seek_bar_thumb); 68 | /*private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), 69 | R.drawable.seek_thumb_pressed); 70 | private final Bitmap thumbDisabledImage = BitmapFactory.decodeResource(getResources(), 71 | R.drawable.seek_thumb_disabled);*/ 72 | private final float thumbWidth = thumbImage.getWidth(); 73 | private final float thumbHalfWidth = 0.5f * thumbWidth; 74 | private final float thumbHalfHeight = 0.5f * thumbImage.getHeight(); 75 | private float INITIAL_PADDING; 76 | private T absoluteMinValue, absoluteMaxValue; 77 | private NumberType numberType; 78 | private double absoluteMinValuePrim, absoluteMaxValuePrim; 79 | private double normalizedMinValue = 0d; 80 | private double normalizedMaxValue = 1d; 81 | private Thumb pressedThumb = null; 82 | private boolean notifyWhileDragging = false; 83 | private OnRangeSeekBarChangeListener listener; 84 | /** 85 | * Default color of a {@link RangeSeekBar}, #FF33B5E5. This is also known as "Ice Cream Sandwich" blue. 86 | */ 87 | public static final int DEFAULT_COLOR = Color.argb(0xFF, 0x33, 0xB5, 0xE5); 88 | /** 89 | * An invalid pointer id. 90 | */ 91 | public static final int INVALID_POINTER_ID = 255; 92 | 93 | // Localized constants from MotionEvent for compatibility 94 | // with API < 8 "Froyo". 95 | public static final int ACTION_POINTER_UP = 0x6, ACTION_POINTER_INDEX_MASK = 0x0000ff00, ACTION_POINTER_INDEX_SHIFT = 8; 96 | 97 | private float mDownMotionX; 98 | 99 | private int mActivePointerId = INVALID_POINTER_ID; 100 | 101 | private int mScaledTouchSlop; 102 | 103 | private boolean mIsDragging; 104 | 105 | //private int mTextOffset; 106 | private int mTextSize; 107 | private int mDistanceToTop; 108 | private RectF mRect; 109 | 110 | private static final int DEFAULT_TEXT_SIZE_IN_DP = 14; 111 | private static final int DEFAULT_TEXT_DISTANCE_TO_BUTTON_IN_DP = 8; 112 | private static final int DEFAULT_TEXT_DISTANCE_TO_TOP_IN_DP = 8; 113 | private boolean mSingleThumb; 114 | 115 | public RangeSeekBar(Context context) { 116 | super(context); 117 | init(context, null); 118 | } 119 | 120 | public RangeSeekBar(Context context, AttributeSet attrs) { 121 | super(context, attrs); 122 | init(context, attrs); 123 | } 124 | 125 | public RangeSeekBar(Context context, AttributeSet attrs, int defStyle) { 126 | super(context, attrs, defStyle); 127 | init(context, attrs); 128 | } 129 | 130 | private T extractNumericValueFromAttributes(TypedArray a, int attribute, int defaultValue) { 131 | TypedValue tv = a.peekValue(attribute); 132 | if (tv == null) { 133 | return (T) Integer.valueOf(defaultValue); 134 | } 135 | 136 | int type = tv.type; 137 | if (type == TypedValue.TYPE_FLOAT) { 138 | return (T) Float.valueOf(a.getFloat(attribute, defaultValue)); 139 | } else { 140 | return (T) Integer.valueOf(a.getInteger(attribute, defaultValue)); 141 | } 142 | } 143 | 144 | private void init(Context context, AttributeSet attrs) { 145 | if (attrs == null) { 146 | setRangeToDefaultValues(); 147 | } else { 148 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RangeSeekBar, 0, 0); 149 | setRangeValues( 150 | extractNumericValueFromAttributes(a, R.styleable.RangeSeekBar_absoluteMinValue, DEFAULT_MINIMUM), 151 | extractNumericValueFromAttributes(a, R.styleable.RangeSeekBar_absoluteMaxValue, DEFAULT_MAXIMUM)); 152 | mSingleThumb = a.getBoolean(R.styleable.RangeSeekBar_singleThumb, false); 153 | a.recycle(); 154 | } 155 | 156 | setValuePrimAndNumberType(); 157 | 158 | INITIAL_PADDING = PixelUtil.dpToPx(context, INITIAL_PADDING_IN_DP); 159 | 160 | mTextSize = PixelUtil.dpToPx(context, DEFAULT_TEXT_SIZE_IN_DP); 161 | mDistanceToTop = PixelUtil.dpToPx(context, DEFAULT_TEXT_DISTANCE_TO_TOP_IN_DP); 162 | /* mTextOffset = this.mTextSize + 163 | PixelUtil.dpToPx(context,DEFAULT_TEXT_DISTANCE_TO_BUTTON_IN_DP) + 164 | this.mDistanceToTop;*/ 165 | 166 | float lineHeight = PixelUtil.dpToPx(context, LINE_HEIGHT_IN_DP); 167 | /*mRect = new RectF(getPaddingLeft(), 168 | mTextOffset + thumbHalfHeight - lineHeight / 2, 169 | getWidth() - getPaddingRight(), 170 | mTextOffset + thumbHalfHeight + lineHeight / 2);*/ 171 | mRect = new RectF(getPaddingLeft(), 172 | thumbHalfHeight - lineHeight / 2, 173 | getWidth() - getPaddingRight(), 174 | thumbHalfHeight + lineHeight / 2); 175 | 176 | // make RangeSeekBar focusable. This solves focus handling issues in case EditText widgets are being used along with the RangeSeekBar within ScollViews. 177 | setFocusable(true); 178 | setFocusableInTouchMode(true); 179 | mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 180 | } 181 | 182 | public void setRangeValues(T minValue, T maxValue) { 183 | this.absoluteMinValue = minValue; 184 | this.absoluteMaxValue = maxValue; 185 | setValuePrimAndNumberType(); 186 | } 187 | 188 | @SuppressWarnings("unchecked") 189 | // only used to set default values when initialised from XML without any values specified 190 | private void setRangeToDefaultValues() { 191 | this.absoluteMinValue = (T) DEFAULT_MINIMUM; 192 | this.absoluteMaxValue = (T) DEFAULT_MAXIMUM; 193 | setValuePrimAndNumberType(); 194 | } 195 | 196 | private void setValuePrimAndNumberType() { 197 | absoluteMinValuePrim = absoluteMinValue.doubleValue(); 198 | absoluteMaxValuePrim = absoluteMaxValue.doubleValue(); 199 | numberType = NumberType.fromNumber(absoluteMinValue); 200 | } 201 | 202 | public void resetSelectedValues() { 203 | setSelectedMinValue(absoluteMinValue); 204 | setSelectedMaxValue(absoluteMaxValue); 205 | } 206 | 207 | public boolean isNotifyWhileDragging() { 208 | return notifyWhileDragging; 209 | } 210 | 211 | /** 212 | * Should the widget notify the listener callback while the user is still dragging a thumb? Default is false. 213 | * 214 | * @param flag 215 | */ 216 | public void setNotifyWhileDragging(boolean flag) { 217 | this.notifyWhileDragging = flag; 218 | } 219 | 220 | /** 221 | * Returns the absolute minimum value of the range that has been set at construction time. 222 | * 223 | * @return The absolute minimum value of the range. 224 | */ 225 | public T getAbsoluteMinValue() { 226 | return absoluteMinValue; 227 | } 228 | 229 | /** 230 | * Returns the absolute maximum value of the range that has been set at construction time. 231 | * 232 | * @return The absolute maximum value of the range. 233 | */ 234 | public T getAbsoluteMaxValue() { 235 | return absoluteMaxValue; 236 | } 237 | 238 | /** 239 | * Returns the currently selected min value. 240 | * 241 | * @return The currently selected min value. 242 | */ 243 | public T getSelectedMinValue() { 244 | return normalizedToValue(normalizedMinValue); 245 | } 246 | 247 | /** 248 | * Sets the currently selected minimum value. The widget will be invalidated and redrawn. 249 | * 250 | * @param value The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range. 251 | */ 252 | public void setSelectedMinValue(T value) { 253 | // in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing. 254 | if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) { 255 | setNormalizedMinValue(0d); 256 | } else { 257 | setNormalizedMinValue(valueToNormalized(value)); 258 | } 259 | } 260 | 261 | /** 262 | * Returns the currently selected max value. 263 | * 264 | * @return The currently selected max value. 265 | */ 266 | public T getSelectedMaxValue() { 267 | return normalizedToValue(normalizedMaxValue); 268 | } 269 | 270 | /** 271 | * Sets the currently selected maximum value. The widget will be invalidated and redrawn. 272 | * 273 | * @param value The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range. 274 | */ 275 | public void setSelectedMaxValue(T value) { 276 | // in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing. 277 | if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) { 278 | setNormalizedMaxValue(1d); 279 | } else { 280 | setNormalizedMaxValue(valueToNormalized(value)); 281 | } 282 | } 283 | 284 | /** 285 | * Registers given listener callback to notify about changed selected values. 286 | * 287 | * @param listener The listener to notify about changed selected values. 288 | */ 289 | public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener listener) { 290 | this.listener = listener; 291 | } 292 | 293 | /** 294 | * Handles thumb selection and movement. Notifies listener callback on certain events. 295 | */ 296 | @Override 297 | public boolean onTouchEvent(MotionEvent event) { 298 | 299 | if (!isEnabled()) { 300 | return false; 301 | } 302 | 303 | int pointerIndex; 304 | 305 | final int action = event.getAction(); 306 | switch (action & MotionEvent.ACTION_MASK) { 307 | 308 | case MotionEvent.ACTION_DOWN: 309 | // Remember where the motion event started 310 | mActivePointerId = event.getPointerId(event.getPointerCount() - 1); 311 | pointerIndex = event.findPointerIndex(mActivePointerId); 312 | mDownMotionX = event.getX(pointerIndex); 313 | 314 | pressedThumb = evalPressedThumb(mDownMotionX); 315 | 316 | // Only handle thumb presses. 317 | if (pressedThumb == null) { 318 | return super.onTouchEvent(event); 319 | } 320 | 321 | setPressed(true); 322 | invalidate(); 323 | onStartTrackingTouch(); 324 | trackTouchEvent(event); 325 | attemptClaimDrag(); 326 | 327 | break; 328 | case MotionEvent.ACTION_MOVE: 329 | if (pressedThumb != null) { 330 | 331 | if (mIsDragging) { 332 | trackTouchEvent(event); 333 | } else { 334 | // Scroll to follow the motion event 335 | pointerIndex = event.findPointerIndex(mActivePointerId); 336 | final float x = event.getX(pointerIndex); 337 | 338 | if (Math.abs(x - mDownMotionX) > mScaledTouchSlop) { 339 | setPressed(true); 340 | invalidate(); 341 | onStartTrackingTouch(); 342 | trackTouchEvent(event); 343 | attemptClaimDrag(); 344 | } 345 | } 346 | 347 | if (notifyWhileDragging && listener != null) { 348 | listener.onRangeSeekBarValuesChanged(this, getSelectedMinValue(), getSelectedMaxValue()); 349 | } 350 | } 351 | break; 352 | case MotionEvent.ACTION_UP: 353 | if (mIsDragging) { 354 | trackTouchEvent(event); 355 | onStopTrackingTouch(); 356 | setPressed(false); 357 | } else { 358 | // Touch up when we never crossed the touch slop threshold 359 | // should be interpreted as a tap-seek to that location. 360 | onStartTrackingTouch(); 361 | trackTouchEvent(event); 362 | onStopTrackingTouch(); 363 | } 364 | 365 | pressedThumb = null; 366 | invalidate(); 367 | if (listener != null) { 368 | listener.onRangeSeekBarValuesChanged(this, getSelectedMinValue(), getSelectedMaxValue()); 369 | } 370 | break; 371 | case MotionEvent.ACTION_POINTER_DOWN: { 372 | final int index = event.getPointerCount() - 1; 373 | // final int index = ev.getActionIndex(); 374 | mDownMotionX = event.getX(index); 375 | mActivePointerId = event.getPointerId(index); 376 | invalidate(); 377 | break; 378 | } 379 | case MotionEvent.ACTION_POINTER_UP: 380 | onSecondaryPointerUp(event); 381 | invalidate(); 382 | break; 383 | case MotionEvent.ACTION_CANCEL: 384 | if (mIsDragging) { 385 | onStopTrackingTouch(); 386 | setPressed(false); 387 | } 388 | invalidate(); // see above explanation 389 | break; 390 | } 391 | return true; 392 | } 393 | 394 | private final void onSecondaryPointerUp(MotionEvent ev) { 395 | final int pointerIndex = (ev.getAction() & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; 396 | 397 | final int pointerId = ev.getPointerId(pointerIndex); 398 | if (pointerId == mActivePointerId) { 399 | // This was our active pointer going up. Choose 400 | // a new active pointer and adjust accordingly. 401 | // TODO: Make this decision more intelligent. 402 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 403 | mDownMotionX = ev.getX(newPointerIndex); 404 | mActivePointerId = ev.getPointerId(newPointerIndex); 405 | } 406 | } 407 | 408 | private final void trackTouchEvent(MotionEvent event) { 409 | final int pointerIndex = event.findPointerIndex(mActivePointerId); 410 | final float x = event.getX(pointerIndex); 411 | 412 | if (Thumb.MIN.equals(pressedThumb) && !mSingleThumb) { 413 | setNormalizedMinValue(screenToNormalized(x)); 414 | } else if (Thumb.MAX.equals(pressedThumb)) { 415 | setNormalizedMaxValue(screenToNormalized(x)); 416 | } 417 | } 418 | 419 | /** 420 | * Tries to claim the user's drag motion, and requests disallowing any ancestors from stealing events in the drag. 421 | */ 422 | private void attemptClaimDrag() { 423 | if (getParent() != null) { 424 | getParent().requestDisallowInterceptTouchEvent(true); 425 | } 426 | } 427 | 428 | /** 429 | * This is called when the user has started touching this widget. 430 | */ 431 | void onStartTrackingTouch() { 432 | mIsDragging = true; 433 | } 434 | 435 | /** 436 | * This is called when the user either releases his touch or the touch is canceled. 437 | */ 438 | void onStopTrackingTouch() { 439 | mIsDragging = false; 440 | } 441 | 442 | /** 443 | * Ensures correct size of the widget. 444 | */ 445 | @Override 446 | protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 447 | int width = 200; 448 | if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) { 449 | width = MeasureSpec.getSize(widthMeasureSpec); 450 | } 451 | //width -= getPaddingLeft() + getPaddingRight(); 452 | 453 | int height = thumbImage.getHeight() + PixelUtil.dpToPx(getContext(), HEIGHT_IN_DP); 454 | if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) { 455 | height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec)); 456 | } 457 | height += getPaddingBottom() + getPaddingTop(); 458 | setMeasuredDimension(width, height); 459 | } 460 | 461 | /** 462 | * Draws the widget on the given canvas. 463 | */ 464 | @Override 465 | protected synchronized void onDraw(Canvas canvas) { 466 | super.onDraw(canvas); 467 | 468 | paint.setTextSize(mTextSize); 469 | paint.setStyle(Style.FILL); 470 | //paint.setColor(Color.GRAY); 471 | paint.setColor(COLOR_OUTSIDE_RANGE); 472 | paint.setAntiAlias(true); 473 | 474 | // draw min and max labels 475 | /*String minLabel = getContext().getString(R.string.demo_min_label); 476 | String maxLabel = getContext().getString(R.string.demo_max_label); 477 | float minMaxLabelSize = Math.max(paint.measureText(minLabel), paint.measureText(maxLabel)); 478 | float minMaxHeight = mTextOffset + thumbHalfHeight + mTextSize / 3; 479 | canvas.drawText(minLabel, 0, minMaxHeight, paint); 480 | canvas.drawText(maxLabel, getWidth() - minMaxLabelSize, minMaxHeight, paint); 481 | padding = INITIAL_PADDING + minMaxLabelSize + thumbHalfWidth;*/ 482 | 483 | // draw seek bar background line 484 | mRect.left = getPaddingLeft(); 485 | mRect.right = getWidth() - getPaddingRight(); 486 | canvas.drawRect(mRect, paint); 487 | 488 | boolean selectedValuesAreDefault = (getSelectedMinValue().equals(getAbsoluteMinValue()) && 489 | getSelectedMaxValue().equals(getAbsoluteMaxValue())); 490 | 491 | /*int colorToUseForButtonsAndHighlightedLine = selectedValuesAreDefault ? 492 | Color.GRAY : // default values 493 | DEFAULT_COLOR; //non default, filter is active*/ 494 | 495 | // draw seek bar active range line 496 | mRect.left = normalizedToScreen(normalizedMinValue); 497 | mRect.right = normalizedToScreen(normalizedMaxValue); 498 | 499 | paint.setColor(getResources().getColor(R.color.material_blue_grey_800)); 500 | canvas.drawRect(mRect, paint); 501 | 502 | // draw minimum thumb if not a single thumb control 503 | if (!mSingleThumb) { 504 | drawThumb(normalizedToScreen(normalizedMinValue), Thumb.MIN.equals(pressedThumb), canvas, 505 | selectedValuesAreDefault); 506 | } 507 | 508 | // draw maximum thumb 509 | drawThumb(normalizedToScreen(normalizedMaxValue), Thumb.MAX.equals(pressedThumb), canvas, 510 | selectedValuesAreDefault); 511 | 512 | // draw the text if sliders have moved from default edges 513 | if (!selectedValuesAreDefault) { 514 | paint.setTextSize(mTextSize); 515 | paint.setColor(Color.WHITE); 516 | // give text a bit more space here so it doesn't subItemById cut off 517 | int offset = PixelUtil.dpToPx(getContext(), TEXT_LATERAL_PADDING_IN_DP); 518 | 519 | String minText = String.valueOf(getSelectedMinValue()); 520 | String maxText = String.valueOf(getSelectedMaxValue()); 521 | float minTextWidth = paint.measureText(minText) + offset; 522 | float maxTextWidth = paint.measureText(maxText) + offset; 523 | 524 | /*if (!mSingleThumb) { 525 | canvas.drawText(minText, 526 | normalizedToScreen(normalizedMinValue) - minTextWidth * 0.5f, 527 | mDistanceToTop + mTextSize, 528 | paint); 529 | 530 | } 531 | 532 | canvas.drawText(maxText, 533 | normalizedToScreen(normalizedMaxValue) - maxTextWidth * 0.5f, 534 | mDistanceToTop + mTextSize, 535 | paint);*/ 536 | } 537 | 538 | } 539 | 540 | /** 541 | * Overridden to save instance state when device orientation changes. This method is called automatically if you assign an id to the RangeSeekBar widget using the {@link #setId(int)} method. Other members of this class than the normalized min and max values don't need to be saved. 542 | */ 543 | @Override 544 | protected Parcelable onSaveInstanceState() { 545 | final Bundle bundle = new Bundle(); 546 | bundle.putParcelable("SUPER", super.onSaveInstanceState()); 547 | bundle.putDouble("MIN", normalizedMinValue); 548 | bundle.putDouble("MAX", normalizedMaxValue); 549 | return bundle; 550 | } 551 | 552 | /** 553 | * Overridden to restore instance state when device orientation changes. This method is called automatically if you assign an id to the RangeSeekBar widget using the {@link #setId(int)} method. 554 | */ 555 | @Override 556 | protected void onRestoreInstanceState(Parcelable parcel) { 557 | final Bundle bundle = (Bundle) parcel; 558 | super.onRestoreInstanceState(bundle.getParcelable("SUPER")); 559 | normalizedMinValue = bundle.getDouble("MIN"); 560 | normalizedMaxValue = bundle.getDouble("MAX"); 561 | } 562 | 563 | /** 564 | * Draws the "normal" resp. "pressed" thumb image on specified x-coordinate. 565 | * 566 | * @param screenCoord The x-coordinate in screen space where to draw the image. 567 | * @param pressed Is the thumb currently in "pressed" state? 568 | * @param canvas The canvas to draw upon. 569 | */ 570 | private void drawThumb(float screenCoord, boolean pressed, Canvas canvas, boolean areSelectedValuesDefault) { 571 | Bitmap buttonToDraw; 572 | /*if (areSelectedValuesDefault) { 573 | buttonToDraw = thumbDisabledImage; 574 | } else { 575 | buttonToDraw = pressed ? thumbPressedImage : thumbImage; 576 | }*/ 577 | buttonToDraw = thumbImage; 578 | 579 | canvas.drawBitmap(buttonToDraw, screenCoord - thumbHalfWidth, 580 | 0, 581 | paint); 582 | } 583 | 584 | /** 585 | * Decides which (if any) thumb is touched by the given x-coordinate. 586 | * 587 | * @param touchX The x-coordinate of a touch event in screen space. 588 | * @return The pressed thumb or null if none has been touched. 589 | */ 590 | private Thumb evalPressedThumb(float touchX) { 591 | Thumb result = null; 592 | boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue); 593 | boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue); 594 | if (minThumbPressed && maxThumbPressed) { 595 | // if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore. 596 | result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX; 597 | } else if (minThumbPressed) { 598 | result = Thumb.MIN; 599 | } else if (maxThumbPressed) { 600 | result = Thumb.MAX; 601 | } 602 | return result; 603 | } 604 | 605 | /** 606 | * Decides if given x-coordinate in screen space needs to be interpreted as "within" the normalized thumb x-coordinate. 607 | * 608 | * @param touchX The x-coordinate in screen space to check. 609 | * @param normalizedThumbValue The normalized x-coordinate of the thumb to check. 610 | * @return true if x-coordinate is in thumb range, false otherwise. 611 | */ 612 | private boolean isInThumbRange(float touchX, double normalizedThumbValue) { 613 | return Math.abs(touchX - normalizedToScreen(normalizedThumbValue)) <= thumbHalfWidth; 614 | } 615 | 616 | /** 617 | * Sets normalized min value to value so that 0 <= value <= normalized max value <= 1. The View will subItemById invalidated when calling this method. 618 | * 619 | * @param value The new normalized min value to set. 620 | */ 621 | private void setNormalizedMinValue(double value) { 622 | normalizedMinValue = Math.max(0d, Math.min(1d, Math.min(value, normalizedMaxValue))); 623 | invalidate(); 624 | } 625 | 626 | /** 627 | * Sets normalized max value to value so that 0 <= normalized min value <= value <= 1. The View will subItemById invalidated when calling this method. 628 | * 629 | * @param value The new normalized max value to set. 630 | */ 631 | private void setNormalizedMaxValue(double value) { 632 | normalizedMaxValue = Math.max(0d, Math.min(1d, Math.max(value, normalizedMinValue))); 633 | invalidate(); 634 | } 635 | 636 | /** 637 | * Converts a normalized value to a Number object in the value space between absolute minimum and maximum. 638 | * 639 | * @param normalized 640 | * @return 641 | */ 642 | @SuppressWarnings("unchecked") 643 | private T normalizedToValue(double normalized) { 644 | double v = absoluteMinValuePrim + normalized * (absoluteMaxValuePrim - absoluteMinValuePrim); 645 | // TODO parameterize this rounding to allow variable decimal points 646 | return (T) numberType.toNumber(Math.round(v * 100) / 100d); 647 | } 648 | 649 | /** 650 | * Converts the given Number value to a normalized double. 651 | * 652 | * @param value The Number value to normalize. 653 | * @return The normalized double. 654 | */ 655 | private double valueToNormalized(T value) { 656 | if (0 == absoluteMaxValuePrim - absoluteMinValuePrim) { 657 | // prevent division by zero, simply return 0. 658 | return 0d; 659 | } 660 | return (value.doubleValue() - absoluteMinValuePrim) / (absoluteMaxValuePrim - absoluteMinValuePrim); 661 | } 662 | 663 | /** 664 | * Converts a normalized value into screen space. 665 | * 666 | * @param normalizedCoord The normalized value to convert. 667 | * @return The converted value in screen space. 668 | */ 669 | private float normalizedToScreen(double normalizedCoord) { 670 | return (float) (getPaddingLeft() + normalizedCoord * (getWidth() - getPaddingLeft() - getPaddingRight())); 671 | } 672 | 673 | /** 674 | * Converts screen space x-coordinates into normalized values. 675 | * 676 | * @param screenCoord The x-coordinate in screen space to convert. 677 | * @return The normalized value. 678 | */ 679 | private double screenToNormalized(float screenCoord) { 680 | int padding = getPaddingLeft() + getPaddingRight(); 681 | int width = getWidth(); 682 | if (width <= 2 * padding) { 683 | // prevent division by zero, simply return 0. 684 | return 0d; 685 | } else { 686 | double result = (screenCoord - padding) / (width - 2 * padding); 687 | return Math.min(1d, Math.max(0d, result)); 688 | } 689 | } 690 | 691 | /** 692 | * Callback listener interface to notify about changed range values. 693 | * 694 | * @param The Number type the RangeSeekBar has been declared with. 695 | * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de) 696 | */ 697 | public interface OnRangeSeekBarChangeListener { 698 | 699 | public void onRangeSeekBarValuesChanged(RangeSeekBar bar, T minValue, T maxValue); 700 | } 701 | 702 | /** 703 | * Thumb constants (min and max). 704 | */ 705 | private static enum Thumb { 706 | MIN, MAX 707 | } 708 | 709 | ; 710 | 711 | /** 712 | * Utility enumeration used to convert between Numbers and doubles. 713 | * 714 | * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de) 715 | */ 716 | private static enum NumberType { 717 | LONG, DOUBLE, INTEGER, FLOAT, SHORT, BYTE, BIG_DECIMAL; 718 | 719 | public static NumberType fromNumber(E value) throws IllegalArgumentException { 720 | if (value instanceof Long) { 721 | return LONG; 722 | } 723 | if (value instanceof Double) { 724 | return DOUBLE; 725 | } 726 | if (value instanceof Integer) { 727 | return INTEGER; 728 | } 729 | if (value instanceof Float) { 730 | return FLOAT; 731 | } 732 | if (value instanceof Short) { 733 | return SHORT; 734 | } 735 | if (value instanceof Byte) { 736 | return BYTE; 737 | } 738 | if (value instanceof BigDecimal) { 739 | return BIG_DECIMAL; 740 | } 741 | throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported"); 742 | } 743 | 744 | public Number toNumber(double value) { 745 | switch (this) { 746 | case LONG: 747 | return Long.valueOf((long) value); 748 | case DOUBLE: 749 | return value; 750 | case INTEGER: 751 | return Integer.valueOf((int) value); 752 | case FLOAT: 753 | return Float.valueOf((float)value); 754 | case SHORT: 755 | return Short.valueOf((short) value); 756 | case BYTE: 757 | return Byte.valueOf((byte) value); 758 | case BIG_DECIMAL: 759 | return BigDecimal.valueOf(value); 760 | } 761 | throw new InstantiationError("can't convert " + this + " to a Number object"); 762 | } 763 | } 764 | 765 | /** 766 | * Util class for converting between dp, px and other magical pixel units 767 | */ 768 | public static class PixelUtil { 769 | 770 | private PixelUtil() { 771 | } 772 | 773 | public static int dpToPx(Context context, int dp) { 774 | int px = Math.round(dp * getPixelScaleFactor(context)); 775 | return px; 776 | } 777 | 778 | public static int pxToDp(Context context, int px) { 779 | int dp = Math.round(px / getPixelScaleFactor(context)); 780 | return dp; 781 | } 782 | 783 | private static float getPixelScaleFactor(Context context) { 784 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 785 | return (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT); 786 | } 787 | 788 | } 789 | } -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/RegionContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.animation.Animator; 4 | import android.animation.ValueAnimator; 5 | import android.content.Context; 6 | import android.graphics.Canvas; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v4.content.ContextCompat; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.util.AttributeSet; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.CompoundButton; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import widget.tamic.com.filterviewsipmle.R; 22 | 23 | /** 24 | * 25 | * Created by Tamic on 2017-07-27 26 | */ 27 | public class RegionContainer extends ConditionContainer implements View.OnClickListener { 28 | 29 | private FilterMode mFilterMode; 30 | 31 | public static int TYPE_NORMAL = 1000; 32 | public static int TYPE_EDIT = 1001; 33 | public static int TYPE_ALL = 1002; 34 | 35 | 36 | public RegionContainer(Context context, FilterMode filterMode) { 37 | super(context); 38 | mFilterMode = filterMode; 39 | init(); 40 | } 41 | 42 | public RegionContainer(Context context, AttributeSet attrs) { 43 | super(context, attrs); 44 | init(); 45 | } 46 | 47 | private void init() { 48 | View inflate = LayoutInflater.from(getContext()).inflate(R.layout.layout_condition_region, this); 49 | mTabGroup = (LinearLayout) findViewById(R.id.tab_layout_condition_region); 50 | mPrimaryList = (RecyclerView) findViewById(R.id.list_primary_layout_condition_region); 51 | mSecondaryList = (RecyclerView) findViewById(R.id.list_secondary_layout_condition_region); 52 | mConfirmPannel = (LinearLayout) findViewById(R.id.bottom_layout_condition_region); 53 | 54 | if (checkConfimPannelVisibility()) { 55 | mConfirmPannel.setVisibility(View.VISIBLE); 56 | this.invalidate(); 57 | } 58 | 59 | mDecoration = new DividerItemDecoration(getContext(), R.drawable.shape_list_divider); 60 | mPrimaryList.addItemDecoration(mDecoration); 61 | 62 | mPrimaryList.setLayoutManager(new LinearLayoutManager(getContext())); 63 | mSecondaryList.setLayoutManager(new LinearLayoutManager(getContext())); 64 | mPrimaryAdapter = new ConditionItemAdapter(R.layout.item_condition_2, mLevelTwoClickListener); 65 | mSecondaryAdapter = new ConditionItem2Adapter(R.layout.item_condition_2, R.layout.item_condition_3, mLevelThreeClickListener); 66 | mSecondaryAdapter.setOnItemClickListener(mLevelThreeItemListener); 67 | mPrimaryList.setAdapter(mPrimaryAdapter); 68 | mSecondaryList.setAdapter(mSecondaryAdapter); 69 | 70 | mTvReset = (TextView) findViewById(R.id.tv_reset_condition); 71 | mTvConfirm = (TextView) findViewById(R.id.tv_confirm_condition); 72 | mTvReset.setOnClickListener(this); 73 | mTvConfirm.setOnClickListener(this); 74 | findViewById(R.id.blank_condition).setOnClickListener(this); 75 | } 76 | 77 | private LinearLayout mTabGroup; 78 | private LinearLayout mConfirmPannel; 79 | private RecyclerView mPrimaryList; 80 | private RecyclerView mSecondaryList; 81 | private TextView mTvReset; 82 | private TextView mTvConfirm; 83 | private ConditionItemAdapter mPrimaryAdapter; 84 | private ConditionItem2Adapter mSecondaryAdapter; 85 | private DividerItemDecoration mDecoration; 86 | private NewConditionItem mRoot; 87 | private boolean mSetting = false; 88 | 89 | private OnClickListener mLevelOneClickListener = new OnClickListener() { 90 | @Override 91 | public void onClick(View v) { 92 | CompoundButton cb = (CompoundButton) v; 93 | NewConditionItem item = (NewConditionItem) cb.getTag(); 94 | boolean refresh = item.parent.processSubItems(item, cb.isChecked()); 95 | cb.setChecked(item.selected); 96 | if (refresh) { 97 | setLv0Item(item.parent); 98 | } 99 | } 100 | }; 101 | private OnClickListener mLevelTwoClickListener = new OnClickListener() { 102 | @Override 103 | public void onClick(View v) { 104 | CompoundButton cb = (CompoundButton) v; 105 | NewConditionItem item = (NewConditionItem) cb.getTag(); 106 | boolean refresh = item.parent.processSubItems(item, cb.isChecked()); 107 | cb.setChecked(item.selected); 108 | if (refresh) { 109 | item.rootClear(); 110 | setLv1Item(item.parent); 111 | } 112 | 113 | } 114 | }; 115 | 116 | private OnClickListener mLevelThreeClickListener = new OnClickListener() { 117 | @Override 118 | public void onClick(View v) { 119 | CompoundButton cb = (CompoundButton) v; 120 | NewConditionItem item = (NewConditionItem) cb.getTag(); 121 | boolean refresh = item.parent.processSubItems(item, cb.isChecked()); 122 | cb.setChecked(item.selected); 123 | if (refresh) { 124 | item.rootClear(); 125 | setLv2Item(item.parent); 126 | } 127 | 128 | if (!checkConfimPannelVisibility()) { 129 | refreshDrawableState(); 130 | v.postDelayed(new Runnable() { 131 | @Override 132 | public void run() { 133 | mController.confirm(RegionContainer.this); 134 | } 135 | }, 20); 136 | } 137 | 138 | 139 | } 140 | }; 141 | 142 | private OnItemClickListener mLevelThreeItemListener = new OnItemClickListener() { 143 | 144 | @Override 145 | public void onEditClick(View v, int viewType) { 146 | mController.onItemClick(RegionContainer.this, viewType); 147 | } 148 | }; 149 | 150 | @Override 151 | public NewConditionItem getConditionItem() { 152 | return mRoot; 153 | } 154 | 155 | @Override 156 | public void setConditionItem(NewConditionItem item) { 157 | mRoot = item; 158 | if (item == null) { 159 | mTabGroup.removeAllViews(); 160 | return; 161 | } 162 | if (this.isLayoutRequested()) { 163 | this.addOnLayoutChangeListener(new OnLayoutChangeListener() { 164 | @Override 165 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 166 | v.removeOnLayoutChangeListener(this); 167 | mSetting = true; 168 | setLv0Item(mRoot); 169 | mSetting = false; 170 | } 171 | }); 172 | } else { 173 | mSetting = true; 174 | setLv0Item(mRoot); 175 | mSetting = false; 176 | } 177 | } 178 | 179 | @Override 180 | public NewConditionItem create(List itemList) { 181 | return null; 182 | } 183 | 184 | @Override 185 | public void addContentView(View view) { 186 | addView(view); 187 | } 188 | 189 | @Override 190 | public void onItemClick(ConditionContainer container) { 191 | super.onItemClick(container); 192 | mController.confirm(container); 193 | } 194 | 195 | private void setLv0Item(NewConditionItem item) { 196 | mTabGroup.removeAllViews(); 197 | for (NewConditionItem subItem : item.subItems) { 198 | CompoundButton btn = (CompoundButton) LayoutInflater.from(getContext()).inflate( 199 | R.layout.item_condition_1, mTabGroup, false); 200 | btn.setText(subItem.name); 201 | btn.setChecked(subItem.selected); 202 | btn.setTag(subItem); 203 | btn.setOnClickListener(mLevelOneClickListener); 204 | mTabGroup.addView(btn); 205 | } 206 | for (NewConditionItem subItem : item.subItems) { 207 | if (subItem.selected) { 208 | setLv1Item(subItem); 209 | return; 210 | } 211 | } 212 | setLv1Item(null); 213 | } 214 | 215 | private void setLv1Item(NewConditionItem item) { 216 | if (item == null) { 217 | mPrimaryList.setVisibility(View.GONE); 218 | setLv2Item(null); 219 | return; 220 | } 221 | if (mPrimaryList.getVisibility() != View.VISIBLE) { 222 | mPrimaryList.setVisibility(View.VISIBLE); 223 | } 224 | mPrimaryAdapter.setList(item.subItems); 225 | mPrimaryAdapter.notifyDataSetChanged(); 226 | 227 | for (int i = 0; i < item.subItems.size(); ++i) { 228 | NewConditionItem subItem = item.subItems.get(i); 229 | if (subItem.selected) { 230 | setLv2Item(subItem); 231 | if (mSetting) { 232 | checkSelectedItemVisibility(mPrimaryList, i); 233 | } 234 | return; 235 | } 236 | } 237 | setLv2Item(null); 238 | } 239 | 240 | private void setLv2Item(NewConditionItem item) { 241 | if (item == null || item.isLeaf()) { 242 | closeSecondaryList(); 243 | return; 244 | } 245 | mSecondaryAdapter.setList(item.subItems); 246 | mSecondaryAdapter.notifyDataSetChanged(); 247 | for (int i = 0; i < item.subItems.size(); ++i) { 248 | NewConditionItem subItem = item.subItems.get(i); 249 | if (subItem.selected) { 250 | if (mSetting) { 251 | checkSelectedItemVisibility(mSecondaryList, i); 252 | } 253 | } 254 | } 255 | openSecondaryList(); 256 | } 257 | 258 | private boolean mIsAnimating; 259 | private void closeSecondaryList() { 260 | final int w = getWidth(); 261 | if (mSetting) { 262 | ViewGroup.LayoutParams lp; 263 | lp = mPrimaryList.getLayoutParams(); 264 | lp.width = w; 265 | mPrimaryList.setLayoutParams(lp); 266 | lp = mSecondaryList.getLayoutParams(); 267 | lp.width = 0; 268 | mSecondaryList.setLayoutParams(lp); 269 | } else { 270 | if (!mIsAnimating && mSecondaryList.getWidth() != 0) { 271 | ValueAnimator anime = ValueAnimator.ofInt(w / 2, w); 272 | anime.setDuration(500); 273 | anime.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 274 | @Override 275 | public void onAnimationUpdate(ValueAnimator animation) { 276 | Integer value = (Integer) animation.getAnimatedValue(); 277 | mPrimaryList.layout( 278 | mPrimaryList.getLeft(), 279 | mPrimaryList.getTop(), 280 | value, 281 | mPrimaryList.getBottom() 282 | ); 283 | mSecondaryList.layout( 284 | value, 285 | mSecondaryList.getTop(), 286 | value + w / 2, 287 | mSecondaryList.getBottom() 288 | ); 289 | } 290 | }); 291 | anime.addListener(new Animator.AnimatorListener() { 292 | @Override 293 | public void onAnimationStart(Animator animation) { 294 | mIsAnimating = true; 295 | } 296 | 297 | @Override 298 | public void onAnimationEnd(Animator animation) { 299 | ViewGroup.LayoutParams lp; 300 | lp = mPrimaryList.getLayoutParams(); 301 | lp.width = w; 302 | mPrimaryList.setLayoutParams(lp); 303 | lp = mSecondaryList.getLayoutParams(); 304 | lp.width = 0; 305 | mSecondaryList.setLayoutParams(lp); 306 | mIsAnimating = false; 307 | } 308 | 309 | @Override 310 | public void onAnimationCancel(Animator animation) { 311 | 312 | } 313 | 314 | @Override 315 | public void onAnimationRepeat(Animator animation) { 316 | 317 | } 318 | }); 319 | anime.start(); 320 | } 321 | } 322 | } 323 | private void openSecondaryList() { 324 | final int w = getWidth(); 325 | if (mSetting) { 326 | ViewGroup.LayoutParams lp; 327 | lp = mPrimaryList.getLayoutParams(); 328 | lp.width = w / 2; 329 | mPrimaryList.setLayoutParams(lp); 330 | lp = mSecondaryList.getLayoutParams(); 331 | lp.width = w / 2; 332 | mSecondaryList.setLayoutParams(lp); 333 | } else { 334 | if (!mIsAnimating && mSecondaryList.getWidth() == 0) { 335 | ValueAnimator anime = ValueAnimator.ofInt(w, w / 2); 336 | anime.setDuration(500); 337 | anime.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 338 | @Override 339 | public void onAnimationUpdate(ValueAnimator animation) { 340 | Integer value = (Integer) animation.getAnimatedValue(); 341 | mPrimaryList.layout( 342 | mPrimaryList.getLeft(), 343 | mPrimaryList.getTop(), 344 | value, 345 | mPrimaryList.getBottom() 346 | ); 347 | mSecondaryList.layout( 348 | value, 349 | mSecondaryList.getTop(), 350 | value + w / 2, 351 | mSecondaryList.getBottom() 352 | ); 353 | } 354 | }); 355 | anime.addListener(new Animator.AnimatorListener() { 356 | @Override 357 | public void onAnimationStart(Animator animation) { 358 | mIsAnimating = true; 359 | } 360 | 361 | @Override 362 | public void onAnimationEnd(Animator animation) { 363 | ViewGroup.LayoutParams lp; 364 | lp = mPrimaryList.getLayoutParams(); 365 | lp.width = w / 2; 366 | mPrimaryList.setLayoutParams(lp); 367 | lp = mSecondaryList.getLayoutParams(); 368 | lp.width = w / 2; 369 | mSecondaryList.setLayoutParams(lp); 370 | mIsAnimating = false; 371 | } 372 | 373 | @Override 374 | public void onAnimationCancel(Animator animation) { 375 | 376 | } 377 | 378 | @Override 379 | public void onAnimationRepeat(Animator animation) { 380 | 381 | } 382 | }); 383 | anime.start(); 384 | } 385 | } 386 | } 387 | 388 | private void checkSelectedItemVisibility(RecyclerView listView, int position) { 389 | listView.scrollToPosition(position); 390 | } 391 | 392 | private boolean checkConfimPannelVisibility() { 393 | /* if (mCallback == null) { 394 | retur; 395 | }*/ 396 | // return FilterMode.ENTER.ordinal() == mCallback.geFiterMode().ordinal(); 397 | return FilterMode.ENTER.ordinal() == mFilterMode.ordinal(); 398 | } 399 | 400 | @Override 401 | public void onClick(View v) { 402 | int i = v.getId(); 403 | if (i == R.id.tv_reset_condition) { 404 | if (mRoot != null) { 405 | mRoot.clear(); 406 | mRoot.reset(); 407 | setConditionItem(mRoot); 408 | } 409 | } else if (i == R.id.tv_confirm_condition) { 410 | mController.confirm(RegionContainer.this); 411 | } else if (i == R.id.blank_condition) { 412 | mController.cancel(RegionContainer.this); 413 | } else { 414 | } 415 | } 416 | 417 | private static class ConditionItemAdapter extends RecyclerView.Adapter { 418 | 419 | static final List EMPTY_LIST = new ArrayList<>(); 420 | private List mList = EMPTY_LIST; 421 | private int mItemLayoutID; 422 | private OnClickListener mOnClickListener; 423 | 424 | public ConditionItemAdapter(int itemLayoutID, OnClickListener listener) { 425 | mItemLayoutID = itemLayoutID; 426 | mOnClickListener = listener; 427 | } 428 | 429 | public void setList(List list) { 430 | if (list == null) { 431 | list = EMPTY_LIST; 432 | } 433 | mList = list; 434 | } 435 | 436 | @Override 437 | public VH onCreateViewHolder(ViewGroup parent, int viewType) { 438 | return new VH(LayoutInflater.from(parent.getContext()).inflate( 439 | mItemLayoutID, parent, false)); 440 | } 441 | 442 | @Override 443 | public void onBindViewHolder(VH holder, int position) { 444 | NewConditionItem item = mList.get(position); 445 | holder.tb.setText(item.name); 446 | holder.tb.setChecked(item.selected); 447 | holder.tb.setTag(item); 448 | holder.tb.setOnClickListener(mOnClickListener); 449 | } 450 | 451 | @Override 452 | public int getItemCount() { 453 | return mList.size(); 454 | } 455 | 456 | } 457 | 458 | 459 | private static class ConditionItem2Adapter extends RecyclerView.Adapter { 460 | 461 | static final List EMPTY_LIST = new ArrayList<>(); 462 | private final int mItemSpecLayoutID; 463 | private List mList = EMPTY_LIST; 464 | private int mItemLayoutID; 465 | private OnClickListener mOnClickListener; 466 | private OnItemClickListener mOnItemClickListener; 467 | 468 | public ConditionItem2Adapter(int itemLayoutID,int itemSpecLayoutID ,OnClickListener listener) { 469 | mItemLayoutID = itemLayoutID; 470 | mItemSpecLayoutID = itemSpecLayoutID; 471 | mOnClickListener = listener; 472 | } 473 | 474 | public OnItemClickListener getOnItemClickListener() { 475 | return mOnItemClickListener; 476 | } 477 | 478 | public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) { 479 | this.mOnItemClickListener = mOnItemClickListener; 480 | } 481 | 482 | public void setList(List list) { 483 | if (list == null) { 484 | list = EMPTY_LIST; 485 | } 486 | mList = list; 487 | } 488 | 489 | @Override 490 | public RegionContainer.VH onCreateViewHolder(ViewGroup parent, int viewType) { 491 | 492 | if (viewType == TYPE_NORMAL) { 493 | return new RegionContainer.VH(LayoutInflater.from(parent.getContext()).inflate( 494 | mItemLayoutID, parent, false)); 495 | } else if (viewType == TYPE_EDIT) { 496 | return new RegionContainer.VH(LayoutInflater.from(parent.getContext()).inflate( 497 | mItemSpecLayoutID, parent, false)); 498 | } 499 | 500 | return new RegionContainer.VH(LayoutInflater.from(parent.getContext()).inflate( 501 | mItemLayoutID, parent, false)); 502 | } 503 | 504 | @Override 505 | public void onBindViewHolder(RegionContainer.VH holder, final int position) { 506 | final NewConditionItem item = mList.get(position); 507 | if (holder.getItemViewType() == TYPE_EDIT) { 508 | CompoundButton btnEdit = holder.tb; 509 | btnEdit.setText(item.name); 510 | btnEdit.setChecked(item.selected); 511 | btnEdit.setTag(item); 512 | btnEdit.setId(holder.getItemViewType()); 513 | btnEdit.setOnClickListener(mOnClickListener); 514 | final View finalView = btnEdit; 515 | final NewConditionItem finalitem =item; 516 | btnEdit.setOnClickListener(new OnClickListener() { 517 | @Override 518 | public void onClick(View v) { 519 | if (mOnItemClickListener!= null) { 520 | mOnItemClickListener.onEditClick(finalView, item.id); 521 | } 522 | } 523 | }); 524 | 525 | } else { 526 | CompoundButton btn = holder.tb; 527 | btn.setText(item.name); 528 | btn.setChecked(item.selected); 529 | btn.setTag(item); 530 | btn.setId(holder.getItemViewType()); 531 | btn.setOnClickListener(mOnClickListener); 532 | } 533 | 534 | } 535 | 536 | @Override 537 | public int getItemViewType(int position) { 538 | if (mList!=null && !mList.isEmpty()) { 539 | NewConditionItem item = mList.get(position); 540 | if (item.id == TYPE_EDIT) { 541 | return TYPE_EDIT; 542 | } 543 | } 544 | return super.getItemViewType(position); 545 | } 546 | 547 | 548 | @Override 549 | public int getItemCount() { 550 | return mList.size(); 551 | } 552 | 553 | @Override 554 | public void onViewRecycled(RegionContainer.VH holder) { 555 | super.onViewRecycled(holder); 556 | CompoundButton btn = (CompoundButton) holder.tb; 557 | 558 | } 559 | } 560 | 561 | public interface OnItemClickListener { 562 | void onEditClick(View v, int viewType); 563 | } 564 | 565 | private static class VH extends RecyclerView.ViewHolder { 566 | CompoundButton tb; 567 | public VH(View itemView) { 568 | super(itemView); 569 | tb = (CompoundButton) itemView.findViewById(R.id.tb); 570 | } 571 | } 572 | 573 | private static class DividerItemDecoration extends RecyclerView.ItemDecoration { 574 | 575 | private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; 576 | 577 | private Drawable mDivider; 578 | 579 | /** 580 | * Custom divider will be used 581 | */ 582 | public DividerItemDecoration(Context context, int resId) { 583 | mDivider = ContextCompat.getDrawable(context, resId); 584 | } 585 | 586 | @Override 587 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 588 | int left = parent.getPaddingLeft(); 589 | int right = parent.getWidth() - parent.getPaddingRight(); 590 | 591 | int childCount = parent.getChildCount(); 592 | for (int i = 0; i < childCount; i++) { 593 | View child = parent.getChildAt(i); 594 | 595 | RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); 596 | 597 | int top = child.getBottom() + params.bottomMargin; 598 | int bottom = top + mDivider.getIntrinsicHeight(); 599 | 600 | mDivider.setBounds(left, top, right, bottom); 601 | mDivider.draw(c); 602 | } 603 | } 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/SorterContainer.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.widget.CompoundButton; 8 | import android.widget.LinearLayout; 9 | 10 | import java.util.List; 11 | 12 | import widget.tamic.com.filterviewsipmle.R; 13 | 14 | /** 15 | * Created by tamic on 2017-08-03. 16 | * 17 | * @link {https://github.com/Tamicer/FilterBar} 18 | */ 19 | public class SorterContainer extends ConditionContainer implements View.OnClickListener { 20 | 21 | public SorterContainer(Context context) { 22 | super(context); 23 | init(); 24 | } 25 | 26 | public SorterContainer(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | init(); 29 | } 30 | 31 | private void init() { 32 | LayoutInflater.from(getContext()).inflate(R.layout.layout_condition_sorter, this); 33 | mList = (LinearLayout) findViewById(R.id.list_condition_sorter); 34 | findViewById(R.id.blank_condition).setOnClickListener(new OnClickListener() { 35 | 36 | @Override 37 | public void onClick(View v) { 38 | mController.cancel(SorterContainer.this); 39 | } 40 | }); 41 | } 42 | 43 | private NewConditionItem mRoot; 44 | private LinearLayout mList; 45 | 46 | @Override 47 | public NewConditionItem getConditionItem() { 48 | return mRoot; 49 | } 50 | 51 | @Override 52 | public NewConditionItem create(List sorterBean) { 53 | 54 | NewConditionItem sorterRoot = new NewConditionItem(null, 0, CategoryBar.SORTER_ROOT, true); 55 | sorterRoot.processor = NewConditionItem.RADIO_LIST; 56 | if (sorterBean != null) { 57 | for (BaseFilterItem item : sorterBean) { 58 | new NewConditionItem(sorterRoot, item.getId(), item.getValue()); 59 | } 60 | } 61 | sorterRoot.reset(); 62 | return sorterRoot; 63 | } 64 | 65 | @Override 66 | public void addContentView(View view) { 67 | addView(view); 68 | } 69 | 70 | @Override 71 | public void setConditionItem(NewConditionItem item) { 72 | mRoot = item; 73 | mList.removeAllViews(); 74 | LayoutInflater inflater = LayoutInflater.from(getContext()); 75 | for (NewConditionItem subItem : mRoot.subItems) { 76 | CompoundButton cb = (CompoundButton) inflater.inflate( 77 | R.layout.item_condition_1, mList, false); 78 | cb.setTag(subItem); 79 | cb.setText(subItem.name); 80 | cb.setChecked(subItem.selected); 81 | cb.setOnClickListener(this); 82 | mList.addView(cb); 83 | 84 | if (subItem != mRoot.subItems.get(mRoot.subItems.size() - 1)) { 85 | inflater.inflate(R.layout.layout_condition_divider, mList); 86 | } 87 | } 88 | } 89 | 90 | @Override 91 | public void onClick(View v) { 92 | CompoundButton cb = (CompoundButton) v; 93 | NewConditionItem item = (NewConditionItem) cb.getTag(); 94 | boolean refresh = item.parent.processSubItems(item, cb.isChecked()); 95 | cb.setChecked(item.selected); 96 | if (refresh) { 97 | setConditionItem(mRoot); 98 | this.postDelayed(new Runnable() { 99 | 100 | @Override 101 | public void run() { 102 | mController.confirm(SorterContainer.this); 103 | } 104 | }, 20); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filter/UIUtil.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filter; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ListAdapter; 9 | import android.widget.ListView; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * @link {https://github.com/Tamicer/FilterBar} 16 | */ 17 | public class UIUtil { 18 | 19 | public UIUtil() { 20 | } 21 | 22 | public static int px2dip(Context context, float pxValue) { 23 | float scale = context.getResources().getDisplayMetrics().density; 24 | return (int) (pxValue / scale + 0.5F); 25 | } 26 | 27 | public static int dip2px(Context context, float dipValue) { 28 | float scale = context.getResources().getDisplayMetrics().density; 29 | return (int) (dipValue * scale + 0.5F); 30 | } 31 | 32 | public static void measureView(View child) { 33 | ViewGroup.LayoutParams p = child.getLayoutParams(); 34 | if (p == null) { 35 | p = new ViewGroup.LayoutParams(-1, -2); 36 | } 37 | 38 | int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width); 39 | int lpHeight = p.height; 40 | int childHeightSpec; 41 | if (lpHeight > 0) { 42 | childHeightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, 1073741824); 43 | } else { 44 | childHeightSpec = View.MeasureSpec.makeMeasureSpec(0, 0); 45 | } 46 | 47 | child.measure(childWidthSpec, childHeightSpec); 48 | } 49 | 50 | public static List getViewGroupAllLeafs(ViewGroup root) { 51 | ArrayList ret = new ArrayList(); 52 | if (root.getChildCount() != 0) { 53 | for (int i = 0; i < root.getChildCount(); ++i) { 54 | try { 55 | ViewGroup e = (ViewGroup) root.getChildAt(i); 56 | ret.addAll(getViewGroupAllLeafs(e)); 57 | } catch (Exception var4) { 58 | ret.add(root.getChildAt(i)); 59 | } 60 | } 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | public static List getViewGroupAll(ViewGroup root) { 67 | ArrayList ret = new ArrayList(); 68 | if (root.getChildCount() != 0) { 69 | for (int i = 0; i < root.getChildCount(); ++i) { 70 | try { 71 | ret.add(root.getChildAt(i)); 72 | ViewGroup e = (ViewGroup) root.getChildAt(i); 73 | ret.addAll(getViewGroupAll(e)); 74 | } catch (Exception var4) { 75 | ret.add(root.getChildAt(i)); 76 | } 77 | } 78 | } 79 | 80 | return ret; 81 | } 82 | 83 | public static Bitmap convertViewToBitmap(View view) { 84 | if (view.getLayoutParams() == null) { 85 | view.setLayoutParams(new ViewGroup.LayoutParams(-2, -2)); 86 | } 87 | 88 | measureView(view); 89 | view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); 90 | view.destroyDrawingCache(); 91 | view.buildDrawingCache(); 92 | return view.getDrawingCache(); 93 | } 94 | 95 | public static int getWindowHeight(Context context) { 96 | return context.getResources().getDisplayMetrics().heightPixels; 97 | } 98 | 99 | public static int getWindowWidth(Context context) { 100 | return context.getResources().getDisplayMetrics().widthPixels; 101 | } 102 | 103 | public static float getWindowDensity(Context context) { 104 | return context.getResources().getDisplayMetrics().density; 105 | } 106 | 107 | public static int getListViewMesureHeight(ListView lv) { 108 | int height = 0; 109 | 110 | try { 111 | ListAdapter la = lv.getAdapter(); 112 | int count = la.getCount(); 113 | 114 | for (int i = 0; i < count; ++i) { 115 | View listItem = la.getView(i, (View) null, lv); 116 | listItem.measure(View.MeasureSpec.makeMeasureSpec(0, 0), View.MeasureSpec.makeMeasureSpec(0, 0)); 117 | height += listItem.getMeasuredHeight(); 118 | } 119 | 120 | height += lv.getDividerHeight() * (count - 1); 121 | } catch (Exception var6) { 122 | ; 123 | } 124 | 125 | return height; 126 | } 127 | 128 | public static float px2sp(Context context, float px) { 129 | float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; 130 | return px / scaledDensity; 131 | } 132 | 133 | public static float sp2px(Context context, float sp) { 134 | float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; 135 | return sp * scaledDensity; 136 | } 137 | 138 | public static View getFirstChildView(Activity activity) { 139 | return ((ViewGroup) ((ViewGroup) activity.getWindow().getDecorView().findViewById(16908290)).getChildAt(0)).getChildAt(0); 140 | } 141 | } -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filterviewsipmle/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filterviewsipmle; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.support.design.widget.FloatingActionButton; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.Toolbar; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.Toast; 13 | 14 | import com.alibaba.fastjson.JSON; 15 | import com.alibaba.fastjson.TypeReference; 16 | import com.google.gson.Gson; 17 | import com.google.gson.reflect.TypeToken; 18 | import com.tamic.widget.filter.BaseFilterItem; 19 | import com.tamic.widget.filter.CategoryBar; 20 | import com.tamic.widget.filter.ConditionContainer; 21 | import com.tamic.widget.filter.DateSelectConditionItem; 22 | import com.tamic.widget.filter.FilterContainer; 23 | import com.tamic.widget.filter.FliterResultItem; 24 | import com.tamic.widget.filter.GroupFilterItem; 25 | import com.tamic.widget.filter.ListFilterBean; 26 | import com.tamic.widget.filter.NewConditionItem; 27 | import com.tamic.widget.filter.RegionContainer; 28 | import com.tamic.widget.filter.SorterContainer; 29 | 30 | import java.io.BufferedReader; 31 | import java.io.IOException; 32 | import java.io.InputStream; 33 | import java.io.InputStreamReader; 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | import java.util.Map; 37 | 38 | import widget.tamic.com.filterviewsipmle.R; 39 | 40 | 41 | /** 42 | * Created by TAMIC on 2017-07-27 43 | */ 44 | public class MainActivity extends AppCompatActivity { 45 | 46 | private CategoryBar mfilterView; 47 | protected NewConditionItem mRegionRoot; 48 | protected NewConditionItem mFilterRoot; 49 | protected NewConditionItem mSortRoot; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | setContentView(R.layout.activity_main); 55 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 56 | setSupportActionBar(toolbar); 57 | mfilterView = (CategoryBar) findViewById(R.id.ablwf_filter_cfv); 58 | initFilterView(); 59 | setFilterData(); 60 | } 61 | 62 | 63 | public void initFilterView() { 64 | mfilterView.setFristTabText("地区"); 65 | // mfilterView.addFristContainer(new SorterContainer(MainActivity.this)); 66 | mfilterView.setSorterShow(true); 67 | mfilterView.setThreeTabText("默认排序"); 68 | mfilterView.setOnConfirmListener(new CategoryBar.ControllerListener() { 69 | 70 | @Override 71 | public void onConfirm(ConditionContainer container) { 72 | 73 | if (container == mfilterView.getRegionContainer()) { 74 | mRegionRoot = container.getConditionItem(); 75 | } else if (container == mfilterView.getFilterContainer()) { 76 | mFilterRoot = container.getConditionItem(); 77 | } 78 | 79 | onFilterHandleResult(container); 80 | } 81 | 82 | @Override 83 | public void onOpen(ConditionContainer container) { 84 | 85 | if (container == mfilterView.getRegionContainer()) { 86 | if (mRegionRoot == null) { 87 | return; 88 | } 89 | container.setConditionItem(mRegionRoot.clone()); 90 | } else if (container == mfilterView.getFilterContainer()) { 91 | if (mFilterRoot == null) { 92 | return; 93 | } 94 | container.setConditionItem(mFilterRoot.clone()); 95 | } 96 | } 97 | 98 | @Override 99 | public void onCancel(ConditionContainer container) { 100 | 101 | } 102 | 103 | @Override 104 | public void onSpecial(ConditionContainer container, int id) { 105 | 106 | 107 | } 108 | }); 109 | } 110 | 111 | private void onFilterHandleResult(ConditionContainer container) { 112 | 113 | FliterResultItem item = new FliterResultItem(); 114 | 115 | if (container instanceof SorterContainer) { 116 | mSortRoot = container.getConditionItem(); 117 | NewConditionItem lv0 = mSortRoot.firstSelectedSubItem(); 118 | if (lv0.isEmpty()) { 119 | return; 120 | } 121 | onResult(item.createResult(lv0)); 122 | } 123 | 124 | if (container instanceof FilterContainer) { 125 | mFilterRoot = container.getConditionItem(); 126 | 127 | List lv0 = mFilterRoot.selectedSubItems(); 128 | if (lv0.isEmpty()) { 129 | return; 130 | } 131 | onResult(item.createResult(mFilterRoot)); 132 | } 133 | 134 | if (container instanceof RegionContainer) { 135 | mRegionRoot = container.getConditionItem(); 136 | if (mRegionRoot.selected) { 137 | onResult(item.createResult(mRegionRoot)); 138 | } 139 | } 140 | } 141 | 142 | 143 | 144 | protected void onFilterResult(ConditionContainer container) { 145 | 146 | NewConditionItem mRegionRoot; 147 | NewConditionItem mFilterRoot; 148 | NewConditionItem mSortRoot; 149 | if (container == mfilterView.getRegionContainer()) { 150 | mRegionRoot = container.getConditionItem(); 151 | NewConditionItem lv0 = mRegionRoot.firstSelectedSubItem(); 152 | if (lv0.isEmpty()) { 153 | return; 154 | } 155 | 156 | return; 157 | 158 | } else if (container == mfilterView.getSorterContainer()) { 159 | mSortRoot = container.getConditionItem(); 160 | NewConditionItem lv0 = mSortRoot.firstSelectedSubItem(); 161 | if (lv0.isEmpty()) { 162 | return; 163 | } 164 | 165 | return; 166 | } else if (container == mfilterView.getFilterContainer()) { 167 | mFilterRoot = container.getConditionItem(); 168 | NewConditionItem lv0 = mFilterRoot.subItemById(0); 169 | NewConditionItem selectprice = lv0.firstSelectedSubItem(); 170 | if (!lv0.isEmpty()) { 171 | if (selectprice != null) { 172 | 173 | } else { 174 | 175 | } 176 | } 177 | 178 | NewConditionItem lv1 = mFilterRoot.subItemById(1); 179 | List select1 = lv1.selectedSubItems(); 180 | if (!lv1.isEmpty()) { 181 | if (select1 != null) { 182 | for (NewConditionItem child : select1) { 183 | 184 | } 185 | 186 | } else { 187 | 188 | } 189 | } 190 | 191 | NewConditionItem lv2 = mFilterRoot.subItemById(2); 192 | NewConditionItem select2 = lv2.firstSelectedSubItem(); 193 | /* if (!lv2.isEmpty()) { 194 | 195 | DateSelectConditionItem dataitem = (DateSelectConditionItem) lv2.subItemById(555); 196 | dataitem.rangeInfo.getInfo(dataitem); 197 | 198 | Toast.makeText(MainActivity.this, dataitem.getInfo(), Toast.LENGTH_SHORT).show(); 199 | }*/ 200 | if (select2 != null) { 201 | for (NewConditionItem child : select1) { 202 | Toast.makeText(MainActivity.this, String.valueOf(child.id), Toast.LENGTH_SHORT).show(); 203 | } 204 | } else { 205 | Toast.makeText(MainActivity.this, "被重置了!", Toast.LENGTH_SHORT).show(); 206 | 207 | } 208 | return; 209 | } 210 | 211 | } 212 | 213 | 214 | 215 | /** 216 | * 设置筛选数据 217 | */ 218 | public void setFilterData() { 219 | //模擬數據 220 | ListFilterBean listFilterBean = new ListFilterBean(); 221 | 222 | listFilterBean.region = new ListFilterBean.ItemList(); 223 | listFilterBean.region.name = "地铁"; 224 | listFilterBean.region.list = new ArrayList<>(); 225 | listFilterBean.sort = new ListFilterBean.ItemList(); 226 | 227 | listFilterBean.sort.name = "其他"; 228 | listFilterBean.sort.list = new ArrayList<>(); 229 | listFilterBean.layout = new ListFilterBean.ItemList(); 230 | listFilterBean.layout.name = "tab2"; 231 | listFilterBean.layout.list = new ArrayList<>(); 232 | for (int i = 1; i < 6; i++) { 233 | ListFilterBean.Item item = new ListFilterBean.Item(); 234 | item.id = i; 235 | item.name = "item" + i; 236 | listFilterBean.sort.list.add(item); 237 | } 238 | 239 | for (int i = 0; i < 8; i++) { 240 | ListFilterBean.Item item = new ListFilterBean.Item(); 241 | item.children = new ArrayList<>(); 242 | item.id = i; 243 | item.name = "我的" + i; 244 | for (int j = 0; j < 4; j++) { 245 | ListFilterBean.Item childitem = new ListFilterBean.Item(); 246 | if (j == 0) { 247 | childitem.name = "不限"; 248 | childitem.id = i * j; 249 | } else { 250 | childitem.name = "tag" + i + "-" + j; 251 | childitem.id = i * j; 252 | } 253 | 254 | item.children.add(childitem); 255 | } 256 | listFilterBean.layout.list.add(item); 257 | } 258 | 259 | 260 | for (int i = 0; i < 10; i++) { 261 | ListFilterBean.Item item = new ListFilterBean.Item(); 262 | item.children = new ArrayList<>(); 263 | item.id = i; 264 | item.name = "地铁" + i + "号线"; 265 | for (int j = 0; j < 20; j++) { 266 | ListFilterBean.Item childitem = new ListFilterBean.Item(); 267 | childitem.name = "tamic" + i + "-" + j; 268 | childitem.id = i * j; 269 | item.children.add(childitem); 270 | } 271 | listFilterBean.region.list.add(item); 272 | } 273 | ListFilterBean.Item item = new ListFilterBean.Item(); 274 | item.children = new ArrayList<>(); 275 | item.id = 5000; 276 | item.name = "不限"; 277 | listFilterBean.region.list.add(0, item); 278 | 279 | 280 | //設置 281 | mSortRoot = getSorterRoot(listFilterBean); 282 | mfilterView.getSorterContainer().setConditionItem(mSortRoot); 283 | 284 | //区域 285 | mRegionRoot = getRegionRoot(listFilterBean); 286 | mfilterView.getRegionContainer().setConditionItem(mSortRoot); 287 | 288 | //筛选 289 | mFilterRoot = getFilterRoot(listFilterBean); 290 | mfilterView.getFilterContainer().setConditionItem(mFilterRoot); 291 | 292 | } 293 | 294 | 295 | public NewConditionItem getSorterRoot(ListFilterBean filterBean) { 296 | NewConditionItem sorterRoot = new NewConditionItem(null, 0, CategoryBar.SORTER_ROOT, true); 297 | sorterRoot.processor = NewConditionItem.RADIO_LIST; 298 | if (filterBean.sort != null) { 299 | mfilterView.setThreeTabText(filterBean.sort.name); 300 | for (ListFilterBean.Item item : filterBean.sort.list) { 301 | new NewConditionItem(sorterRoot, item.id, item.name); 302 | } 303 | } 304 | sorterRoot.reset(); 305 | return sorterRoot; 306 | } 307 | 308 | public NewConditionItem getFilterRoot(ListFilterBean filterBean) { 309 | NewConditionItem filterRoot = new NewConditionItem(null, 0, CategoryBar.FILTER_ROOT, true); 310 | 311 | 312 | if (filterBean != null && filterBean.layout != null) { 313 | 314 | /* new NewConditionItem(filterRoot, 1111, "选择区间", true, 315 | NewConditionItem.TYPE_RANGE);*/ 316 | 317 | for (ListFilterBean.Item item : filterBean.layout.list) { 318 | NewConditionItem createTimeItem = new NewConditionItem(filterRoot, item.id, item.name, true, 319 | NewConditionItem.TYPE_GRID); 320 | createTimeItem.canReset = false; 321 | createTimeItem.processor = NewConditionItem.DEFAULT; 322 | for (ListFilterBean.Item item1 : item.children) { 323 | new NewConditionItem(createTimeItem, item1.id, item1.name); 324 | } 325 | } 326 | 327 | 328 | } 329 | return filterRoot; 330 | } 331 | 332 | public NewConditionItem getRegionRoot(ListFilterBean filterBean) { 333 | 334 | NewConditionItem regionRoot = new NewConditionItem(null, 0, "regionRoot", true); 335 | regionRoot.processor = NewConditionItem.RADIO_LIST_NO_CLEAR; 336 | if (filterBean.region != null) { 337 | NewConditionItem lv0 = new NewConditionItem(regionRoot, 0, filterBean.region.name); 338 | lv0.processor = NewConditionItem.RADIO_LIST; 339 | for (ListFilterBean.Item item : filterBean.region.list) { 340 | NewConditionItem lv1 = new NewConditionItem(lv0, item.id, item.name); 341 | if (item.children == null || item.children.size() == 0) { 342 | continue; 343 | } 344 | lv1.processor = NewConditionItem.DEFAULT; 345 | for (ListFilterBean.Item subItem : item.children) { 346 | new NewConditionItem(lv1, subItem.id, subItem.name); 347 | } 348 | } 349 | } 350 | 351 | if (filterBean != null && filterBean.layout != null) { 352 | NewConditionItem lv0 = new NewConditionItem(regionRoot, 1, filterBean.layout.name); 353 | lv0.processor = NewConditionItem.RADIO_LIST; 354 | for (ListFilterBean.Item item : filterBean.layout.list) { 355 | NewConditionItem lv1 = new NewConditionItem(lv0, item.id, item.name); 356 | if (item.children == null || item.children.size() == 0) { 357 | continue; 358 | } 359 | lv1.processor = NewConditionItem.DEFAULT; 360 | for (ListFilterBean.Item subItem : item.children) { 361 | new NewConditionItem(lv1, subItem.id, subItem.name); 362 | } 363 | } 364 | } 365 | regionRoot.reset(); 366 | return regionRoot; 367 | } 368 | 369 | public static String getFromAssets(Context context, String fileName) { 370 | BufferedReader reader = null; 371 | 372 | try { 373 | InputStream e = context.getResources().getAssets().open(fileName); 374 | reader = new BufferedReader(new InputStreamReader(e)); 375 | char[] buf = new char[1024]; 376 | boolean count = false; 377 | StringBuffer sb = new StringBuffer(e.available()); 378 | 379 | String readData; 380 | int count1; 381 | while ((count1 = reader.read(buf)) != -1) { 382 | readData = String.valueOf(buf, 0, count1); 383 | sb.append(readData); 384 | } 385 | 386 | readData = sb.toString(); 387 | return readData; 388 | } catch (Exception var11) { 389 | var11.printStackTrace(); 390 | } finally { 391 | try { 392 | reader.close(); 393 | } catch (IOException e) { 394 | e.printStackTrace(); 395 | } 396 | } 397 | 398 | return ""; 399 | } 400 | 401 | protected void onResult(FliterResultItem resultItem) { 402 | 403 | if (resultItem == null) { 404 | } 405 | 406 | if (resultItem.isEmpty()) { 407 | Log.d("Tag", "lv0: "+ resultItem.name); 408 | Toast.makeText(MainActivity.this, String.valueOf(resultItem.id), Toast.LENGTH_SHORT).show(); 409 | 410 | return; 411 | } 412 | 413 | for (FliterResultItem item : resultItem.subItems()) { 414 | 415 | if (item.isEmpty()) { 416 | continue; 417 | } 418 | for (FliterResultItem item1 : item.subItems()) { 419 | Log.d("Tag", "lv1: "+ item.name); 420 | Log.d("Tag", "lv1: "+ item1.name); 421 | Toast.makeText(MainActivity.this, String.valueOf(item.id), Toast.LENGTH_SHORT).show(); 422 | for (FliterResultItem item2 : item1.subItems()) { 423 | Log.d("Tag", "lv2: "+ item2.name); 424 | Toast.makeText(MainActivity.this, String.valueOf(item.id), Toast.LENGTH_SHORT).show(); 425 | } 426 | } 427 | } 428 | } 429 | 430 | } 431 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/java/com/tamic/widget/filterviewsipmle/Test.java: -------------------------------------------------------------------------------- 1 | package com.tamic.widget.filterviewsipmle; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by LIUYONGKUI726 on 2017-08-10. 8 | */ 9 | 10 | public class Test { 11 | 12 | 13 | /** 14 | * keyName : 支付条件 15 | * value : hjjjj 16 | */ 17 | 18 | private String keyName; 19 | private String value; 20 | 21 | public String getKeyName() { 22 | return keyName; 23 | } 24 | 25 | public void setKeyName(String keyName) { 26 | this.keyName = keyName; 27 | } 28 | 29 | public String getValue() { 30 | return value; 31 | } 32 | 33 | public void setValue(String value) { 34 | this.value = value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/drawable/less.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/more_unfold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/drawable/more_unfold.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/range_seek_bar_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/drawable/range_seek_bar_thumb.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/selector_item_condition_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/selector_item_condition_grid_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/selector_item_condition_grid_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/selector_item_condition_text2_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/selector_item_condition_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/shape_btn_standard_3b4263_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/drawable/shape_list_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/item_condition_1.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/item_condition_2.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/item_condition_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/item_condition_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_category_bar_mode_brand_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 24 | 25 | 30 | 31 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_category_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_category_button_new.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_data_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 18 | 28 | 29 | 36 | 37 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 17 | 18 | 24 | 25 | 35 | 36 | 46 | 47 | 48 | 53 | 54 | 59 | 60 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 24 | 25 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_range.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 24 | 25 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_region.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 17 | 18 | 24 | 25 | 30 | 31 | 35 | 36 | 37 | 43 | 44 | 53 | 54 | 63 | 64 | 65 | 71 | 72 | 77 | 78 | 84 | 85 | 86 | 87 | 88 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/layout/layout_condition_sorter.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tamicer/FilterBar/88d7dae23d4e029dcfc3cf64d2879e2a33e331df/FilterViewSipmle/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values/attrs-rangeseekbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #f98e39 7 | #FFFFFF 8 | #BBBBBB 9 | #666666 10 | 11 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | FilterViewSipmle 3 | Settings 4 | 5 | -------------------------------------------------------------------------------- /FilterViewSipmle/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 14 |