├── .gitignore ├── README.md ├── bmwidget.iml ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── benmu │ └── widget │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── java │ ├── android │ │ └── support │ │ │ └── v4 │ │ │ └── view │ │ │ └── BetterViewPager.java │ └── com │ │ └── benmu │ │ └── widget │ │ ├── utils │ │ ├── BaseCommonUtil.java │ │ ├── ColorUtils.java │ │ ├── FunctionParser.java │ │ └── SingleFunctionParser.java │ │ └── view │ │ ├── BMAlert.java │ │ ├── BMFloatingLayer.java │ │ ├── BMGridDialog.java │ │ ├── BMLoding.java │ │ ├── BMPartLayout.java │ │ ├── BMReceiverTextView.java │ │ ├── BMSimpleSeekBar.java │ │ ├── BaseToolBar.java │ │ ├── DebugErrorDialog.java │ │ ├── MaterialCircleView.java │ │ ├── MaxScrollView.java │ │ ├── calendar │ │ ├── AnimatorListener.java │ │ ├── CalendarDay.java │ │ ├── CalendarMode.java │ │ ├── CalendarPager.java │ │ ├── CalendarPagerAdapter.java │ │ ├── CalendarPagerView.java │ │ ├── CalendarUtils.java │ │ ├── CustomerStyle.java │ │ ├── DateRangeIndex.java │ │ ├── DayView.java │ │ ├── DayViewDecorator.java │ │ ├── DayViewFacade.java │ │ ├── DecoratorResult.java │ │ ├── DirectionButton.java │ │ ├── Experimental.java │ │ ├── MaterialCalendarView.java │ │ ├── MonthPagerAdapter.java │ │ ├── MonthView.java │ │ ├── OnDateSelectedListener.java │ │ ├── OnMonthChangedListener.java │ │ ├── OnRangeSelectedListener.java │ │ ├── TitleChanger.java │ │ ├── WeekDayView.java │ │ ├── WeekPagerAdapter.java │ │ ├── WeekView.java │ │ ├── format │ │ │ ├── ArrayWeekDayFormatter.java │ │ │ ├── CalendarWeekDayFormatter.java │ │ │ ├── DateFormatDayFormatter.java │ │ │ ├── DateFormatTitleFormatter.java │ │ │ ├── DayFormatter.java │ │ │ ├── MonthArrayTitleFormatter.java │ │ │ ├── TitleFormatter.java │ │ │ └── WeekDayFormatter.java │ │ └── spans │ │ │ └── DotSpan.java │ │ └── loading │ │ └── LoadingDialog.java └── res │ ├── anim │ ├── bottom_in.xml │ ├── bottom_out.xml │ ├── dialog_enter.xml │ ├── dialog_exit.xml │ ├── svfade_in_center.xml │ ├── svfade_out_center.xml │ ├── svslide_in_bottom.xml │ ├── svslide_in_top.xml │ ├── svslide_out_bottom.xml │ └── svslide_out_top.xml │ ├── color │ ├── mcv_text_date_dark.xml │ └── mcv_text_date_light.xml │ ├── drawable-xhdpi │ ├── ic_launcher.png │ ├── ic_svstatus_error.png │ ├── ic_svstatus_info.png │ ├── ic_svstatus_loading.png │ ├── ic_svstatus_success.png │ ├── icon_backnav.png │ ├── loading_icon.png │ ├── mcv_action_next.png │ └── mcv_action_previous.png │ ├── drawable │ ├── bg_black.xml │ ├── bg_overlay_gradient.xml │ ├── bg_svprogresshuddefault.xml │ ├── dialog_loading.xml │ ├── floatlayer_bg.xml │ └── shape_round_rectange.xml │ ├── layout │ ├── dialog_debug_error_layout.xml │ ├── dialog_progress_layout.xml │ ├── layout_animloading.xml │ ├── layout_custom_toolbar.xml │ ├── layout_dialog.xml │ ├── layout_floatlayer.xml │ ├── layout_grid_dialog.xml │ ├── layout_grid_item.xml │ ├── layout_svprogresshud.xml │ ├── layout_top.xml │ └── view_svprogressdefault.xml │ └── values │ ├── Integer.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ └── style.xml └── test └── java └── com └── benmu └── widget └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/README.md -------------------------------------------------------------------------------- /bmwidget.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '26.0.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(include: ['*.jar'], dir: 'libs') 26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 27 | exclude group: 'com.android.support', module: 'support-annotations' 28 | }) 29 | testCompile 'junit:junit:4.12' 30 | compile "com.android.support:appcompat-v7:${PROJECT_SUPPORTLIBVERSION}" 31 | compile "com.android.support:support-v4:${PROJECT_SUPPORTLIBVERSION}" 32 | compile "com.android.support:recyclerview-v7:${PROJECT_SUPPORTLIBVERSION}" 33 | } 34 | -------------------------------------------------------------------------------- /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 /Users/Carry/Documents/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 | -------------------------------------------------------------------------------- /src/androidTest/java/com/benmu/widget/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget; 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("com.benmu.widget.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/android/support/v4/view/BetterViewPager.java: -------------------------------------------------------------------------------- 1 | package android.support.v4.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | /** 7 | * {@linkplain #setChildrenDrawingOrderEnabledCompat(boolean)} does some reflection that isn't needed. 8 | * And was making view creation time rather large. So lets override it and make it better! 9 | */ 10 | public class BetterViewPager extends ViewPager { 11 | 12 | public BetterViewPager(Context context) { 13 | super(context); 14 | } 15 | 16 | public BetterViewPager(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | @Override 21 | public void setChildrenDrawingOrderEnabledCompat(boolean enable) { 22 | setChildrenDrawingOrderEnabled(enable); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/utils/BaseCommonUtil.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.utils; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.content.ComponentName; 6 | import android.content.Context; 7 | import android.content.pm.PackageInfo; 8 | import android.content.pm.PackageManager; 9 | import android.content.res.TypedArray; 10 | import android.graphics.Bitmap; 11 | import android.graphics.Canvas; 12 | import android.graphics.Color; 13 | import android.graphics.Paint; 14 | import android.graphics.Point; 15 | import android.graphics.PorterDuff; 16 | import android.graphics.PorterDuffColorFilter; 17 | import android.os.Build; 18 | import android.support.v7.app.ActionBar; 19 | import android.support.v7.app.AppCompatActivity; 20 | import android.util.DisplayMetrics; 21 | import android.util.Log; 22 | import android.webkit.CookieManager; 23 | import android.webkit.CookieSyncManager; 24 | 25 | import java.lang.reflect.Field; 26 | import java.lang.reflect.Method; 27 | import java.util.List; 28 | 29 | /** 30 | * Created by Carry on 17/1/19. 31 | */ 32 | 33 | public class BaseCommonUtil { 34 | public static String getVersionName(Context context) { 35 | try { 36 | PackageManager packageManager = context.getPackageManager(); 37 | PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 38 | return packageInfo.versionName; 39 | } catch (Exception e) { 40 | return ""; 41 | } 42 | } 43 | 44 | public static String getVersionCode(Context context) { 45 | try { 46 | PackageInfo pi = context.getPackageManager().getPackageInfo(context.getPackageName(), 47 | 0); 48 | return pi.versionCode + ""; 49 | } catch (PackageManager.NameNotFoundException e) { 50 | // TODO Auto-generated catch block 51 | e.printStackTrace(); 52 | return 0 + ""; 53 | } 54 | } 55 | 56 | public static float getScreenScale(Context context) { 57 | return context.getResources().getDisplayMetrics().density; 58 | } 59 | 60 | public static int dp2px(Context context, float dpValue) { 61 | final float scale = context.getResources().getDisplayMetrics().density; 62 | return (int) (dpValue * scale + 0.5f); 63 | } 64 | 65 | /** 66 | * px转dp 67 | */ 68 | public static int px2dp(Context context, float pxValue) { 69 | final float scale = context.getResources().getDisplayMetrics().density; 70 | return (int) (pxValue / scale + 0.5f); 71 | } 72 | 73 | /** 74 | * sp转px 75 | */ 76 | public static int sp2px(Context context, float spValue) { 77 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 78 | return (int) (spValue * fontScale + 0.5f); 79 | } 80 | 81 | /** 82 | * px转sp 83 | */ 84 | public static int px2sp(Context context, float pxValue) { 85 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 86 | return (int) (pxValue / fontScale + 0.5f); 87 | } 88 | 89 | /** 90 | * @return 将色值转换成16进制 91 | */ 92 | public static int getHexColor(String colorStirng) { 93 | return Color.parseColor(colorStirng); 94 | } 95 | 96 | 97 | public static void clearAllCookies(Context context) { 98 | CookieSyncManager.createInstance(context); 99 | CookieSyncManager.getInstance().startSync(); 100 | CookieManager cookieManager = CookieManager.getInstance(); 101 | cookieManager.removeAllCookie(); 102 | CookieSyncManager.getInstance().sync(); 103 | } 104 | 105 | public static String getTopActivity(Context context) { 106 | String topActivityClassName = null; 107 | ActivityManager activityManager = 108 | (ActivityManager) (context.getSystemService(Context 109 | .ACTIVITY_SERVICE)); 110 | List runningTaskInfos = activityManager.getRunningTasks(1); 111 | if (runningTaskInfos != null && runningTaskInfos.size() > 0) { 112 | ComponentName f = runningTaskInfos.get(0).topActivity; 113 | topActivityClassName = f.getClassName(); 114 | } 115 | return topActivityClassName; 116 | } 117 | 118 | public static boolean isAPPRunningForeground(Context context) { 119 | if (context != null) { 120 | String packageName = context.getPackageName(); 121 | String topName = getTopActivity(context); 122 | return packageName != null && topName != null && topName.startsWith(packageName); 123 | } else { 124 | return false; 125 | } 126 | } 127 | 128 | 129 | public static int[] getWH(Activity context) { 130 | DisplayMetrics metrics = new DisplayMetrics(); 131 | context.getWindow().getWindowManager().getDefaultDisplay() 132 | .getMetrics(metrics); 133 | int[] wh = new int[2]; 134 | wh[0] = metrics.widthPixels; 135 | wh[1] = metrics.heightPixels; 136 | return wh; 137 | } 138 | 139 | public static float getScaleByFontsize(String fontsize) { 140 | switch (fontsize) { 141 | case "NORM": 142 | return 1f; 143 | case "BIG": 144 | return 1.15f; 145 | case "EXTRALARGE": 146 | return 1.3f; 147 | default: 148 | return 0; 149 | } 150 | } 151 | 152 | 153 | private static final String TAG = "WXTBUtil"; 154 | 155 | private static boolean isSupportSmartBar = false; 156 | 157 | static { 158 | isSupportSmartBar = isSupportSmartBar(); 159 | } 160 | 161 | public static int getDisplayWidth(AppCompatActivity activity) { 162 | int width = 0; 163 | if (activity != null && activity.getWindowManager() != null && activity.getWindowManager 164 | ().getDefaultDisplay() != null) { 165 | Point point = new Point(); 166 | activity.getWindowManager().getDefaultDisplay().getSize(point); 167 | width = point.x; 168 | } 169 | return width; 170 | } 171 | 172 | public static int getDisplayHeight(AppCompatActivity activity) { 173 | int height = 0; 174 | if (activity != null && activity.getWindowManager() != null && activity.getWindowManager 175 | ().getDefaultDisplay() != null) { 176 | Point point = new Point(); 177 | activity.getWindowManager().getDefaultDisplay().getSize(point); 178 | height = point.y; 179 | } 180 | 181 | Log.e(TAG, "isSupportSmartBar:" + isSupportSmartBar); 182 | 183 | if (isSupportSmartBar) { 184 | int smartBarHeight = getSmartBarHeight(activity); 185 | Log.e(TAG, "smartBarHeight:" + smartBarHeight); 186 | height -= smartBarHeight; 187 | } 188 | 189 | if (activity != null && activity.getSupportActionBar() != null) { 190 | int actionbar = activity.getSupportActionBar().getHeight(); 191 | if (actionbar == 0) { 192 | TypedArray actionbarSizeTypedArray = activity.obtainStyledAttributes(new 193 | int[]{android.R.attr.actionBarSize}); 194 | actionbar = (int) actionbarSizeTypedArray.getDimension(0, 0); 195 | } 196 | Log.d(TAG, "actionbar:" + actionbar); 197 | height -= actionbar; 198 | } 199 | 200 | int status = getStatusBarHeight(activity); 201 | Log.d(TAG, "status:" + status); 202 | 203 | height -= status; 204 | 205 | Log.d(TAG, "height:" + height); 206 | return height; 207 | } 208 | 209 | private static int getStatusBarHeight(AppCompatActivity activity) { 210 | Class c; 211 | Object obj; 212 | Field field; 213 | int x; 214 | int statusBarHeight = 0; 215 | try { 216 | c = Class.forName("com.android.internal.R$dimen"); 217 | obj = c.newInstance(); 218 | field = c.getField("status_bar_height"); 219 | x = Integer.parseInt(field.get(obj).toString()); 220 | statusBarHeight = activity.getResources().getDimensionPixelSize(x); 221 | } catch (Exception e1) { 222 | e1.printStackTrace(); 223 | } 224 | return statusBarHeight; 225 | } 226 | 227 | private static int getSmartBarHeight(AppCompatActivity activity) { 228 | ActionBar actionbar = activity.getSupportActionBar(); 229 | if (actionbar != null) 230 | try { 231 | Class c = Class.forName("com.android.internal.R$dimen"); 232 | Object obj = c.newInstance(); 233 | Field field = c.getField("mz_action_button_min_height"); 234 | int height = Integer.parseInt(field.get(obj).toString()); 235 | return activity.getResources().getDimensionPixelSize(height); 236 | } catch (Exception e) { 237 | e.printStackTrace(); 238 | actionbar.getHeight(); 239 | } 240 | return 0; 241 | } 242 | 243 | private static boolean isSupportSmartBar() { 244 | boolean hasSmartBar = false; 245 | try { 246 | final Method method = Build.class.getMethod("hasSmartBar"); 247 | if (method != null) { 248 | hasSmartBar = true; 249 | } 250 | } catch (final Exception e) { 251 | // return false; 252 | } 253 | return hasSmartBar; 254 | } 255 | 256 | public static void throwIfNull(Object object, T e) throws T { 257 | if (object == null) { 258 | throw e; 259 | } 260 | } 261 | 262 | 263 | /** 264 | * bitmap着色 265 | * @param inBitmap 266 | * @param tintColor 267 | * @return 268 | */ 269 | public static Bitmap tintBitmap(Bitmap inBitmap , int tintColor) { 270 | if (inBitmap == null) { 271 | return null; 272 | } 273 | Bitmap outBitmap = Bitmap.createBitmap (inBitmap.getWidth(), inBitmap.getHeight() , inBitmap.getConfig()); 274 | Canvas canvas = new Canvas(outBitmap); 275 | Paint paint = new Paint(); 276 | paint.setColorFilter( new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN)) ; 277 | canvas.drawBitmap(inBitmap , 0, 0, paint) ; 278 | return outBitmap ; 279 | } 280 | 281 | } 282 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/utils/FunctionParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.benmu.widget.utils; 20 | 21 | import android.support.annotation.NonNull; 22 | 23 | import java.util.LinkedHashMap; 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * Parser for function like "rotate(30 ) transform(50 , 20)". 30 | * This class will translate the raw string presentation of a group of function(s) to give type 31 | * according to the {@link com.taobao.weex.utils.FunctionParser.Mapper} 32 | */ 33 | public class FunctionParser { 34 | 35 | public static final char SPACE = ' '; 36 | 37 | private Mapper mapper; 38 | private Lexer lexer; 39 | 40 | /** 41 | * Construct a function parser 42 | * @param source the raw string representation of a group of function(s) 43 | * @param mapper the mapping rule between string and corresponding type of object. 44 | */ 45 | public FunctionParser(@NonNull String source, @NonNull Mapper mapper) { 46 | this.lexer = new Lexer(source); 47 | this.mapper = mapper; 48 | } 49 | 50 | /** 51 | * Start to parse the raw string. The result will be stored in a sorted map where the order 52 | * is defined by the function order in the raw string. 53 | * @return 54 | */ 55 | public LinkedHashMap parse() { 56 | lexer.moveOn(); 57 | return definition(); 58 | } 59 | 60 | private LinkedHashMap definition() { 61 | LinkedHashMap result = new LinkedHashMap<>(); 62 | do { 63 | result.putAll(function()); 64 | } while (lexer.getCurrentToken() == Token.FUNC_NAME); 65 | return result; 66 | } 67 | 68 | private Map function() { 69 | List list = new LinkedList<>(); 70 | String functionName = match(Token.FUNC_NAME); 71 | match(Token.LEFT_PARENT); 72 | list.add(match(Token.PARAM_VALUE)); 73 | while (lexer.getCurrentToken() == Token.COMMA) { 74 | match(Token.COMMA); 75 | list.add(match(Token.PARAM_VALUE)); 76 | } 77 | match(Token.RIGHT_PARENT); 78 | return mapper.map(functionName, list); 79 | } 80 | 81 | private String match(Token token) { 82 | if (token == lexer.getCurrentToken()) { 83 | String value = lexer.getCurrentTokenValue(); 84 | lexer.moveOn(); 85 | return value; 86 | } 87 | throw new WXInterpretationException(token + "Token doesn't match" + lexer.source); 88 | } 89 | 90 | private enum Token { 91 | FUNC_NAME, PARAM_VALUE, LEFT_PARENT, RIGHT_PARENT, COMMA 92 | } 93 | 94 | public interface Mapper { 95 | 96 | /** 97 | * Map one function to a specified type of object 98 | * @param functionName the name of the raw function. 99 | * @param raw the list of parameter of the raw function 100 | * @return the expected mapping relationship, where the key in the map is the same as the 101 | * key in the return value of {{@link #parse()}}, 102 | * and the value in the map is the type of object that expected by user. 103 | */ 104 | Map map(String functionName, List raw); 105 | } 106 | 107 | private static class WXInterpretationException extends RuntimeException { 108 | 109 | private WXInterpretationException(String msg) { 110 | super(msg); 111 | } 112 | } 113 | 114 | /** 115 | * Lexer for the parser. For now, digit, alphabet, '(', ')', '.', ',', '+', '-' and '%' is 116 | * valid character, and '(', ')', ',', parameter value and function name is valid token. 117 | * Parameter value is defined as "(?i)[\+-]?[0-9]+(\.[0-9]+)?(%||deg||px)?" while function 118 | * name is defined as "[a-zA-Z]+". 119 | * 120 | * The Lexer can also be expressed using the following EBNF format. 121 | *
    122 | *
  • definition = {function};
  • 123 | *
  • function = name, "(", value, { ",", value } , ")";
  • 124 | *
  • name = character, {character};
  • 125 | *
  • value = identifier, {identifier};
  • 126 | *
  • identifier = character | "." | "%" | "+" | "-";
  • 127 | *
  • character = digit | letter;
  • 128 | *
  • digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
  • 129 | *
  • letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | 130 | * "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | 131 | * "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | 132 | * "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" ;
  • 133 | *
134 | */ 135 | private static class Lexer { 136 | 137 | private static final String LEFT_PARENT = "("; 138 | private static final String RIGHT_PARENT = ")"; 139 | private static final String COMMA = ","; 140 | private static final char A_LOWER = 'a'; 141 | private static final char Z_LOWER = 'z'; 142 | private static final char A_UPPER = 'A'; 143 | private static final char Z_UPPER = 'Z'; 144 | private static final char ZERO = '0'; 145 | private static final char NINE = '9'; 146 | private static final char DOT = '.'; 147 | private static final char MINUS = '-'; 148 | private static final char PLUS = '+'; 149 | public static final char PERCENT = '%'; 150 | private String source; 151 | private Token current; 152 | private String value; 153 | private int pointer = 0; 154 | 155 | private Lexer(String source) { 156 | this.source = source; 157 | } 158 | 159 | private Token getCurrentToken() { 160 | return current; 161 | } 162 | 163 | private String getCurrentTokenValue() { 164 | return value; 165 | } 166 | 167 | private boolean moveOn() { 168 | int start = pointer; 169 | char curChar; 170 | while (pointer < source.length()) { 171 | curChar = source.charAt(pointer); 172 | if (curChar == SPACE) { 173 | if (start == pointer++) { 174 | start++; 175 | } else { 176 | break; 177 | } 178 | } else if (isCharacterOrDigit(curChar) || curChar == DOT 179 | || curChar == PERCENT || curChar == MINUS || curChar == PLUS) { 180 | pointer++; 181 | } else { 182 | if (start == pointer) { 183 | pointer++; 184 | } 185 | break; 186 | } 187 | } 188 | if (start != pointer) { 189 | String symbol = source.substring(start, pointer); 190 | moveOn(symbol); 191 | return true; 192 | } else { 193 | current = null; 194 | value = null; 195 | return false; 196 | } 197 | } 198 | 199 | private void moveOn(String token) { 200 | if (LEFT_PARENT.equals(token)) { 201 | current = Token.LEFT_PARENT; 202 | value = LEFT_PARENT; 203 | } else if (RIGHT_PARENT.equals(token)) { 204 | current = Token.RIGHT_PARENT; 205 | value = RIGHT_PARENT; 206 | } else if (COMMA.equals(token)) { 207 | current = Token.COMMA; 208 | value = COMMA; 209 | } else if (isFuncName(token)) { 210 | current = Token.FUNC_NAME; 211 | value = token; 212 | } else { 213 | current = Token.PARAM_VALUE; 214 | value = token; 215 | } 216 | } 217 | 218 | private boolean isFuncName(CharSequence funcName) { 219 | char letter; 220 | for (int i = 0; i < funcName.length(); i++) { 221 | letter = funcName.charAt(i); 222 | if (!((A_LOWER <= letter && letter <= Z_LOWER) || 223 | (A_UPPER <= letter && letter <= Z_UPPER) || 224 | letter == MINUS)) { 225 | return false; 226 | } 227 | } 228 | return true; 229 | } 230 | 231 | private boolean isCharacterOrDigit(char letter) { 232 | return (ZERO <= letter && letter <= NINE) || (A_LOWER <= letter && letter <= Z_LOWER) || 233 | (A_UPPER <= letter && letter <= Z_UPPER); 234 | } 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/utils/SingleFunctionParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package com.benmu.widget.utils; 21 | 22 | import android.support.annotation.NonNull; 23 | 24 | import java.util.HashMap; 25 | import java.util.LinkedList; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | public class SingleFunctionParser extends FunctionParser> { 30 | 31 | public interface FlatMapper { 32 | 33 | V map(String raw); 34 | } 35 | 36 | public interface NonUniformMapper{ 37 | List map(List raw); 38 | } 39 | 40 | /** 41 | * Construct a function parser for uniform parameters. 42 | * @param source the raw string representation of a group of function(s) 43 | * @param mapper the mapping rule between string and corresponding type of object. 44 | */ 45 | public SingleFunctionParser(@NonNull String source, @NonNull final FlatMapper mapper) { 46 | super(source, new Mapper>() { 47 | @Override 48 | public Map> map(String functionName, List raw) { 49 | Map> map = new HashMap>(); 50 | List list = new LinkedList(); 51 | for (String item : raw) { 52 | list.add(mapper.map(item)); 53 | } 54 | map.put(functionName, list); 55 | return map; 56 | } 57 | }); 58 | } 59 | 60 | /** 61 | * Construct a function parser for non-uniform parameters. 62 | * @param source the raw string representation of a group of function(s) 63 | * @param mapper the mapping rule between string and corresponding type of object. 64 | */ 65 | public SingleFunctionParser(@NonNull String source, @NonNull final NonUniformMapper mapper) { 66 | super(source, new Mapper>() { 67 | @Override 68 | public Map> map(String functionName, List raw) { 69 | Map> map = new HashMap>(); 70 | map.put(functionName, mapper.map(raw)); 71 | return map; 72 | } 73 | }); 74 | } 75 | 76 | public List parse(String functionName) { 77 | Map> map = parse(); 78 | if(map.containsKey(functionName)){ 79 | return map.get(functionName); 80 | } 81 | return null; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BMFloatingLayer.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.PixelFormat; 6 | import android.os.Handler; 7 | import android.os.IBinder; 8 | import android.util.Log; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.WindowManager; 15 | 16 | 17 | import com.benmu.widget.R; 18 | 19 | import java.math.BigDecimal; 20 | import java.util.Timer; 21 | import java.util.TimerTask; 22 | 23 | /** 24 | * Created by Carry on 17/3/17. 25 | */ 26 | 27 | public class BMFloatingLayer { 28 | 29 | private static final String TAG = "FLOATINGLAYER"; 30 | 31 | public boolean ISSHOW = false; 32 | 33 | 34 | private WindowManager mWindowManager; 35 | private WindowManager.LayoutParams mLayoutParams; 36 | 37 | private Context mContext; 38 | 39 | private View mPopView; 40 | 41 | private AnimationTimerTask mAnimationTask; 42 | private Timer mAnimationTimer; 43 | private GetTokenRunnable mGetTokenRunnable; 44 | 45 | private FloatingLayerListener mListener; 46 | 47 | private Handler mHander = new Handler(); 48 | 49 | private int mWidth; 50 | private int mHeight; 51 | private float mPrevX; 52 | private float mPrevY; 53 | private int mGetTokenPeriodTime = 500; 54 | private int mAnimatonPeriodTime = 16; 55 | private boolean isMove = false; 56 | private BigDecimal mStartClickTime; 57 | private float mDownX; 58 | private float mDownY; 59 | 60 | public BMFloatingLayer(Context context) { 61 | this.mContext = context; 62 | 63 | initView(); 64 | initWindowManager(); 65 | initLayoutParams(); 66 | initDrag(); 67 | } 68 | 69 | private void initView() { 70 | LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context 71 | .LAYOUT_INFLATER_SERVICE); 72 | mPopView = layoutInflater.inflate(R.layout.layout_floatlayer, null); 73 | } 74 | 75 | private void initWindowManager() { 76 | mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 77 | } 78 | 79 | private void initLayoutParams() { 80 | mWidth = mContext.getResources().getDisplayMetrics().widthPixels; 81 | mHeight = mContext.getResources().getDisplayMetrics().heightPixels; 82 | 83 | mLayoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 84 | ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams 85 | .TYPE_APPLICATION_ATTACHED_DIALOG, 86 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 87 | | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, 88 | PixelFormat.TRANSLUCENT); 89 | mLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; 90 | mLayoutParams.x = 0; 91 | mLayoutParams.y = mContext.getResources().getDisplayMetrics().heightPixels / 6 * 4; 92 | } 93 | 94 | private void initDrag() { 95 | mPopView.setOnTouchListener(new View.OnTouchListener() { 96 | @Override 97 | public boolean onTouch(View view, MotionEvent motionEvent) { 98 | switch (motionEvent.getActionMasked()) { 99 | case MotionEvent.ACTION_DOWN: 100 | mDownX = mPrevX = motionEvent.getRawX(); 101 | mDownY = mPrevY = motionEvent.getRawY(); 102 | mStartClickTime = BigDecimal.valueOf(System.currentTimeMillis()); 103 | break; 104 | case MotionEvent.ACTION_MOVE: 105 | float deltaX = motionEvent.getRawX() - mPrevX; 106 | float deltaY = motionEvent.getRawY() - mPrevY; 107 | mLayoutParams.x += deltaX; 108 | mLayoutParams.y += deltaY; 109 | mPrevX = motionEvent.getRawX(); 110 | mPrevY = motionEvent.getRawY(); 111 | 112 | if (mLayoutParams.x < 0) mLayoutParams.x = 0; 113 | if (mLayoutParams.x > mWidth - mPopView.getWidth()) 114 | mLayoutParams.x = mWidth - mPopView.getWidth(); 115 | if (mLayoutParams.y < 0) mLayoutParams.y = 0; 116 | if (mLayoutParams.y > mHeight - mPopView.getHeight() * 2) 117 | mLayoutParams.y = mHeight - mPopView.getHeight() * 2; 118 | 119 | try { 120 | mWindowManager.updateViewLayout(mPopView, mLayoutParams); 121 | } catch (Exception e) { 122 | Log.d(TAG, e.toString()); 123 | } 124 | 125 | if (deltaX > 10 | deltaY > 10) isMove = true; 126 | break; 127 | case MotionEvent.ACTION_CANCEL: 128 | case MotionEvent.ACTION_UP: 129 | isMove = false; 130 | float upX = motionEvent.getRawX(); 131 | float upY = motionEvent.getRawY(); 132 | BigDecimal now = BigDecimal.valueOf(System.currentTimeMillis()); 133 | if (!isMove && (Math.abs(now.subtract(mStartClickTime).floatValue()) < 134 | 500) && (Math.abs(upX - mDownX) < 20 && Math.abs(upY - mDownY) < 20)) { 135 | if (null != mListener) { 136 | mListener.onClick(); 137 | return false; 138 | } 139 | } 140 | 141 | mAnimationTimer = new Timer(); 142 | mAnimationTask = new AnimationTimerTask(); 143 | mAnimationTimer.schedule(mAnimationTask, 0, mAnimatonPeriodTime); 144 | 145 | break; 146 | } 147 | 148 | return false; 149 | } 150 | }); 151 | } 152 | 153 | public void show(Activity activity) { 154 | 155 | if (!ISSHOW) { 156 | mGetTokenRunnable = new GetTokenRunnable(activity); 157 | mHander.postDelayed(mGetTokenRunnable, 500); 158 | } 159 | } 160 | 161 | public void close() { 162 | try { 163 | if (ISSHOW) { 164 | mWindowManager.removeViewImmediate(mPopView); 165 | ISSHOW = false; 166 | if (null != mListener) { 167 | mListener.onClose(); 168 | } 169 | } 170 | } catch (Exception e) { 171 | Log.d(TAG, e.toString()); 172 | } 173 | 174 | } 175 | 176 | public BMFloatingLayer setListener(FloatingLayerListener listener) { 177 | this.mListener = listener; 178 | return this; 179 | } 180 | 181 | public interface FloatingLayerListener { 182 | void onClick(); 183 | 184 | void onShow(); 185 | 186 | void onClose(); 187 | } 188 | 189 | class AnimationTimerTask extends TimerTask { 190 | 191 | int mStepX; 192 | int mDestX; 193 | 194 | public AnimationTimerTask() { 195 | if (mLayoutParams.x > mWidth / 2) { 196 | mDestX = mWidth - mPopView.getWidth(); 197 | mStepX = (mWidth - mLayoutParams.x) / 10; 198 | } else { 199 | mDestX = 0; 200 | mStepX = -((mLayoutParams.x) / 10); 201 | } 202 | 203 | } 204 | 205 | @Override 206 | public void run() { 207 | 208 | if (Math.abs(mDestX - mLayoutParams.x) <= Math.abs(mStepX)) { 209 | mLayoutParams.x = mDestX; 210 | } else { 211 | mLayoutParams.x += mStepX; 212 | } 213 | 214 | try { 215 | mHander.post(new Runnable() { 216 | @Override 217 | public void run() { 218 | mWindowManager.updateViewLayout(mPopView, mLayoutParams); 219 | } 220 | }); 221 | } catch (Exception e) { 222 | Log.d(TAG, e.toString()); 223 | } 224 | 225 | 226 | if (mLayoutParams.x == mDestX) { 227 | mAnimationTask.cancel(); 228 | mAnimationTimer.cancel(); 229 | } 230 | 231 | 232 | } 233 | } 234 | 235 | class GetTokenRunnable implements Runnable { 236 | 237 | int count = 0; 238 | private Activity mActivity; 239 | 240 | public GetTokenRunnable(Activity activity) { 241 | this.mActivity = activity; 242 | } 243 | 244 | @Override 245 | public void run() { 246 | 247 | if (null == mActivity) 248 | return; 249 | IBinder token = null; 250 | try { 251 | token = mActivity.getWindow().getDecorView().getWindowToken(); 252 | } catch (Exception e) { 253 | } 254 | 255 | if (null != token) { 256 | 257 | try { 258 | mLayoutParams.token = token; 259 | mWindowManager.addView(mPopView, 260 | mLayoutParams); 261 | mActivity = null; 262 | ISSHOW = true; 263 | return; 264 | } catch (Exception e) { 265 | } 266 | } 267 | count++; 268 | mLayoutParams.token = null; 269 | if (count < 10 && null != mLayoutParams) { 270 | mHander.postDelayed(mGetTokenRunnable, 500); 271 | } 272 | 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BMGridDialog.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.text.TextUtils; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.Window; 12 | import android.view.WindowManager; 13 | import android.widget.ImageView; 14 | import android.widget.RelativeLayout; 15 | import android.widget.TextView; 16 | 17 | import com.benmu.widget.R; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * Created by Carry on 2017/6/29. 23 | */ 24 | 25 | public class BMGridDialog { 26 | private Context mContext; 27 | private Dialog mDialog; 28 | private RecyclerView mRv; 29 | private TextView mTv_cancel; 30 | 31 | private BMGridDialog(Context context, int mTheme, int mGravity, String 32 | mCancelText, View.OnClickListener mOnNegativeClick, Adapter adapter, 33 | OnItemClickListener mListenner) { 34 | this.mContext = context; 35 | build(mTheme, mGravity, mCancelText, adapter, mOnNegativeClick, mListenner); 36 | } 37 | 38 | private BMGridDialog(Context mContext, int mTheme, int mGravity, View mContentView) { 39 | this.mContext = mContext; 40 | build(mTheme, mGravity, mContentView); 41 | } 42 | 43 | private void build(int mTheme, int mGravity, View mContentView) { 44 | //create dialog 45 | mDialog = new Dialog(mContext, mTheme); 46 | mDialog.setContentView(mContentView); 47 | Window window = mDialog.getWindow(); 48 | if (window != null) { 49 | window.setGravity(mGravity); 50 | WindowManager.LayoutParams lp = window.getAttributes(); 51 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; 52 | lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 53 | window.setAttributes(lp); 54 | } 55 | } 56 | 57 | private void build(int mTheme, int mGravity, String mCancelText, Adapter adapter, final View 58 | .OnClickListener mOnNegativeClick, OnItemClickListener mListenner) { 59 | 60 | //create dialog 61 | mDialog = new Dialog(mContext, mTheme); 62 | 63 | View inflate = LayoutInflater.from(mContext).inflate(R.layout.layout_grid_dialog, null); 64 | mRv = (RecyclerView) inflate.findViewById(R.id.rv_items); 65 | mTv_cancel = (TextView) inflate.findViewById(R.id.btn_cancel); 66 | if (!TextUtils.isEmpty(mCancelText)) { 67 | mTv_cancel.setText(mCancelText); 68 | } 69 | if (mOnNegativeClick != null) { 70 | mTv_cancel.setOnClickListener(new View.OnClickListener() { 71 | @Override 72 | public void onClick(View v) { 73 | mOnNegativeClick.onClick(v); 74 | } 75 | }); 76 | } else { 77 | mTv_cancel.setOnClickListener(new View.OnClickListener() { 78 | @Override 79 | public void onClick(View v) { 80 | hide(); 81 | } 82 | }); 83 | } 84 | // linearLayoutManager 85 | LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext); 86 | linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); 87 | mRv.setLayoutManager(linearLayoutManager); 88 | //setAdapter 89 | if (adapter != null) { 90 | //set listener 91 | if (mListenner != null) { 92 | adapter.setOnItemClickListenner(mListenner, mDialog); 93 | } 94 | mRv.setAdapter(adapter); 95 | } 96 | 97 | mDialog.setContentView(inflate); 98 | Window window = mDialog.getWindow(); 99 | if (window != null) { 100 | window.setGravity(mGravity); 101 | WindowManager.LayoutParams lp = window.getAttributes(); 102 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; 103 | lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 104 | window.setAttributes(lp); 105 | } 106 | } 107 | 108 | 109 | /** 110 | * dialog builder 111 | */ 112 | public static class Builder { 113 | private int mGravity; 114 | private Context mContext; 115 | private int mTheme; 116 | private String mCancelText; 117 | private View.OnClickListener mOnNegativeClick; 118 | private Adapter mAdapter; 119 | private OnItemClickListener mListenner; 120 | private View mContentView; 121 | 122 | public Builder(Context context, int theme) { 123 | mContext = context; 124 | mTheme = theme; 125 | } 126 | 127 | 128 | public Builder setGravity(int gravity) { 129 | mGravity = gravity; 130 | return this; 131 | } 132 | 133 | public Builder setNegativeButton(String btnText, View.OnClickListener onNegativeClick) { 134 | mCancelText = btnText; 135 | mOnNegativeClick = onNegativeClick; 136 | return this; 137 | } 138 | 139 | public Builder setAdapter(Adapter adapter) { 140 | mAdapter = adapter; 141 | return this; 142 | } 143 | 144 | public Builder setOnItemClickListenner(OnItemClickListener listenner) { 145 | mListenner = listenner; 146 | return this; 147 | } 148 | 149 | /** 150 | * the method with highest priority to set view 151 | * 152 | * @param view view custom by user 153 | */ 154 | public Builder setContentView(View view) { 155 | mContentView = view; 156 | return this; 157 | } 158 | 159 | public BMGridDialog build() { 160 | if (mContext == null) return null; 161 | if (mContentView != null) { 162 | return new BMGridDialog(mContext, mTheme, mGravity, mContentView); 163 | } 164 | return new BMGridDialog(mContext, mTheme, mGravity, mCancelText, mOnNegativeClick, 165 | mAdapter, mListenner); 166 | } 167 | } 168 | 169 | 170 | public interface OnItemClickListener { 171 | void onItemClick(int position, View view, GridItem item, Dialog dialog); 172 | } 173 | 174 | 175 | public void show() { 176 | if (mDialog != null && !mDialog.isShowing()) { 177 | mDialog.show(); 178 | } 179 | } 180 | 181 | 182 | public void hide() { 183 | if (mDialog != null && mDialog.isShowing()) { 184 | mDialog.dismiss(); 185 | } 186 | } 187 | 188 | 189 | public static class GridItem { 190 | private String imgUrl; 191 | private int imgResId; 192 | private String title; 193 | private Object tag; 194 | 195 | public GridItem(String imgUrl, int imgResId, String title, Object tag) { 196 | this.imgUrl = imgUrl; 197 | this.imgResId = imgResId; 198 | this.title = title; 199 | this.tag = tag; 200 | } 201 | 202 | public GridItem() { 203 | } 204 | 205 | public String getImgUrl() { 206 | return imgUrl; 207 | } 208 | 209 | public void setImgUrl(String imgUrl) { 210 | this.imgUrl = imgUrl; 211 | } 212 | 213 | public int getImgResId() { 214 | return imgResId; 215 | } 216 | 217 | public void setImgResId(int imgResId) { 218 | this.imgResId = imgResId; 219 | } 220 | 221 | public String getTitle() { 222 | return title; 223 | } 224 | 225 | public void setTitle(String title) { 226 | this.title = title; 227 | } 228 | 229 | public Object getTag() { 230 | return tag; 231 | } 232 | 233 | public void setTag(Object tag) { 234 | this.tag = tag; 235 | } 236 | } 237 | 238 | 239 | public static class Adapter extends RecyclerView.Adapter { 240 | private List items; 241 | private Context mContext; 242 | private OnItemClickListener mItemClickListener; 243 | private Dialog mDialog; 244 | private int mPart; 245 | 246 | public Adapter(Context context, List list, int part) { 247 | items = list; 248 | mContext = context; 249 | mPart = part; 250 | } 251 | 252 | public Adapter(Context context, List list) { 253 | items = list; 254 | mContext = context; 255 | } 256 | 257 | 258 | public void setOnItemClickListenner(OnItemClickListener listener, Dialog Dialog) { 259 | mItemClickListener = listener; 260 | mDialog = Dialog; 261 | } 262 | 263 | @Override 264 | public PartItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 265 | 266 | return new PartItemViewHolder(LayoutInflater.from(mContext).inflate(R.layout 267 | .layout_grid_item, parent, false)); 268 | } 269 | 270 | @Override 271 | public void onBindViewHolder(final PartItemViewHolder holder, final int position) { 272 | final GridItem gridItem = items.get(position); 273 | if (mPart > 0) { 274 | holder.mLayout.setPart(mPart); 275 | } 276 | holder.tv.setText(gridItem.getTitle()); 277 | holder.iv.setImageResource(gridItem.getImgResId()); 278 | holder.rl.setOnClickListener(new View.OnClickListener() { 279 | @Override 280 | public void onClick(View v) { 281 | if (mItemClickListener != null) { 282 | mItemClickListener.onItemClick(position, holder.rl, gridItem, mDialog); 283 | } 284 | } 285 | }); 286 | } 287 | 288 | @Override 289 | public int getItemCount() { 290 | return items.size(); 291 | } 292 | 293 | 294 | class PartItemViewHolder extends RecyclerView.ViewHolder { 295 | private TextView tv; 296 | private ImageView iv; 297 | private RelativeLayout rl; 298 | private BMPartLayout mLayout; 299 | 300 | PartItemViewHolder(View itemView) { 301 | super(itemView); 302 | tv = (TextView) itemView.findViewById(R.id.tv); 303 | iv = (ImageView) itemView.findViewById(R.id.iv); 304 | rl = (RelativeLayout) itemView.findViewById(R.id.rl_container); 305 | mLayout = (BMPartLayout) itemView.findViewById(R.id.rl_root); 306 | } 307 | } 308 | } 309 | 310 | } 311 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BMLoding.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.view.WindowManager; 7 | import android.widget.TextView; 8 | 9 | import com.benmu.widget.R; 10 | 11 | 12 | /** 13 | * Created by Carry on 17/2/8. 14 | */ 15 | 16 | public class BMLoding extends Dialog { 17 | 18 | boolean isCenter = false; 19 | boolean canWatchOutsideTouch = true; 20 | boolean dimBehindEnabled = false; 21 | private TextView tv_message; 22 | 23 | /** 24 | * 设置dialog是否可以响应下面activity的事件 25 | * 26 | * @method: setWatchOutsideTouch 27 | * @description: TODO 28 | * @author: DongFuhai 29 | * @return: void 30 | * @date: 2013-9-18 下午3:46:09 31 | */ 32 | public BMLoding setWatchOutsideTouch(boolean canWatchOutsideTouch) { 33 | this.canWatchOutsideTouch = canWatchOutsideTouch; 34 | return this; 35 | } 36 | 37 | /** 38 | * 设置dialog背景是不是变灰。默认是不变灰的 39 | * 40 | * @method: setDimBehindEnabled 41 | * @description: TODO 42 | * @author: DongFuhai 43 | * @return: void 44 | * @date: 2013-9-18 下午4:09:11 45 | */ 46 | public BMLoding setDimBehindEnabled(boolean dimBehindEnabled) { 47 | this.dimBehindEnabled = dimBehindEnabled; 48 | return this; 49 | } 50 | 51 | public BMLoding setCentered(boolean isCenter) { 52 | this.isCenter = isCenter; 53 | return this; 54 | } 55 | 56 | public BMLoding(Context context) { 57 | super(context, R.style.AnimDialogLoading); 58 | } 59 | 60 | private BMLoding(Context context, boolean cancelable, 61 | OnCancelListener cancelListener) { 62 | super(context, cancelable, cancelListener); 63 | } 64 | 65 | public BMLoding(Context context, int theme) { 66 | super(context, R.style.AnimDialogLoading); 67 | } 68 | 69 | @Override 70 | protected void onCreate(Bundle savedInstanceState) { 71 | super.onCreate(savedInstanceState); 72 | if (canWatchOutsideTouch) { 73 | getWindow().setFlags( 74 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, 75 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); 76 | getWindow().setFlags( 77 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, 78 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); 79 | } 80 | 81 | setContentView(R.layout.layout_animloading); 82 | tv_message = (TextView) findViewById(R.id.tv_message); 83 | setCanceledOnTouchOutside(false); 84 | } 85 | 86 | @Override 87 | public void dismiss() { 88 | super.dismiss(); 89 | } 90 | 91 | // @Override 92 | // public void show() { 93 | // initXY(); 94 | // try { 95 | // super.show(); 96 | // } catch (Exception ex) { 97 | // } 98 | // } 99 | 100 | private void initXY() { 101 | // 100 - (getWindow().getWindowManager().getDefaultDisplay().getHeight() 102 | // / 2) 103 | // Log.i("tag", 104 | // "height==>"+getWindow().getWindowManager().getDefaultDisplay().getHeight()); 105 | // LayoutParams params = new LayoutParams(); 106 | // if (dimBehindEnabled) { 107 | // params.flags = LayoutParams.FLAG_DIM_BEHIND; 108 | // params.dimAmount = 0.5f; 109 | // } // 默认全部透明 110 | // if(!isCenter) 111 | // params.y = -200; 112 | // getWindow().setAttributes(params); 113 | } 114 | 115 | public void setMessage(String message) { 116 | if (this.tv_message != null) { 117 | tv_message.setText(message); 118 | } 119 | } 120 | 121 | } 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BMPartLayout.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.Display; 6 | import android.view.WindowManager; 7 | import android.widget.RelativeLayout; 8 | 9 | /** 10 | * Created by Carry on 2017/6/30. 11 | */ 12 | 13 | public class BMPartLayout extends RelativeLayout { 14 | private int mPart; 15 | private Context mContext; 16 | private int mSrceenWidth; 17 | private int mRealWidhth; 18 | 19 | public BMPartLayout(Context context) { 20 | super(context); 21 | init(context); 22 | } 23 | 24 | public BMPartLayout(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | init(context); 27 | } 28 | 29 | public BMPartLayout(Context context, AttributeSet attrs, int defStyleAttr) { 30 | super(context, attrs, defStyleAttr); 31 | init(context); 32 | } 33 | 34 | private void init(Context context) { 35 | this.mContext = context; 36 | WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 37 | Display display = manager.getDefaultDisplay(); 38 | mSrceenWidth = display.getWidth(); 39 | } 40 | 41 | public void setPart(int part) { 42 | this.mPart = part; 43 | } 44 | 45 | 46 | @Override 47 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 48 | if (mPart == 0) { 49 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 50 | } else { 51 | mRealWidhth = mSrceenWidth / mPart; 52 | super.onMeasure(MeasureSpec.makeMeasureSpec(mRealWidhth, MeasureSpec.EXACTLY), 53 | heightMeasureSpec); 54 | } 55 | // if (!mFlag) { 56 | // mRealWidth = mWidth / 6; 57 | // } else if (mFlag && mPart != 0) { 58 | // mRealWidth = (int) ((mWidth - MMUtil.getPXbyDP(mContext, 40)) / mPart); 59 | // } 60 | // int fixedWidth = MeasureSpec.makeMeasureSpec(mRealWidth, MeasureSpec.EXACTLY); 61 | // super.onMeasure(fixedWidth, heightMeasureSpec); 62 | } 63 | 64 | public int getRealWidth() { 65 | return mRealWidhth; 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BMReceiverTextView.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.graphics.Canvas; 8 | import android.graphics.Paint; 9 | import android.graphics.Rect; 10 | import android.support.v4.content.LocalBroadcastManager; 11 | import android.util.AttributeSet; 12 | import android.util.TypedValue; 13 | import android.widget.TextView; 14 | 15 | import com.benmu.widget.utils.BaseCommonUtil; 16 | 17 | /** 18 | * Created by Carry on 17/3/16. 19 | */ 20 | 21 | public class BMReceiverTextView extends TextView { 22 | private BroadcastReceiver mReceiver; 23 | private String[] mActionFilter; 24 | private Context mContext; 25 | private String mCurrentFontSize; 26 | private String mChangeFontSize; 27 | private float mCurrentEnlarge; 28 | private boolean mAutoZoom; 29 | private int mAvailableWidth; 30 | private float mCurrentTextSize; 31 | 32 | public BMReceiverTextView(Context context) { 33 | this(context, null); 34 | } 35 | 36 | public BMReceiverTextView(Context context, AttributeSet attrs) { 37 | this(context, attrs, 0); 38 | } 39 | 40 | public BMReceiverTextView(Context context, AttributeSet attrs, int defStyleAttr) { 41 | super(context, attrs, defStyleAttr); 42 | this.mContext = context; 43 | init(); 44 | } 45 | 46 | private void init() { 47 | } 48 | 49 | public void initFontSize(String fontSize) { 50 | this.mCurrentFontSize = fontSize; 51 | if (mCurrentFontSize != null) { 52 | mCurrentEnlarge = BaseCommonUtil.getScaleByFontsize(mCurrentFontSize); 53 | setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize() * mCurrentEnlarge); 54 | } 55 | } 56 | 57 | public void setIntentFilter(String[] actions) { 58 | if (actions == null) return; 59 | mActionFilter = actions; 60 | mReceiver = new MyBroadcastReceiver(); 61 | IntentFilter intentFilter = new IntentFilter(); 62 | for (String action : mActionFilter) { 63 | intentFilter.addAction(action); 64 | } 65 | LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver, intentFilter); 66 | } 67 | 68 | 69 | private class MyBroadcastReceiver extends BroadcastReceiver { 70 | 71 | @Override 72 | public void onReceive(Context context, Intent intent) { 73 | mChangeFontSize = intent.getStringExtra("currentFontSize"); 74 | if (mCurrentFontSize.equals(mChangeFontSize)) { 75 | return; 76 | } 77 | float changEnlarge = BaseCommonUtil.getScaleByFontsize(mChangeFontSize); 78 | setTextSize(TypedValue.COMPLEX_UNIT_PX, getTextSize() * (changEnlarge / 79 | mCurrentEnlarge)); 80 | mCurrentEnlarge = changEnlarge; 81 | mCurrentFontSize = mChangeFontSize; 82 | mChangeFontSize = null; 83 | } 84 | } 85 | 86 | 87 | public void setAutoZoomEnable(boolean autoZoomEnable) { 88 | this.mAutoZoom = autoZoomEnable; 89 | } 90 | 91 | @Override 92 | protected void onDraw(Canvas canvas) { 93 | super.onDraw(canvas); 94 | if (mAutoZoom) { 95 | autoZoom(); 96 | } 97 | } 98 | 99 | private void autoZoom() { 100 | int width = getWidth(); 101 | mAvailableWidth = width - getPaddingLeft() - getPaddingRight(); 102 | // calculate text width 103 | CharSequence text = getText(); 104 | if (text == null) return; 105 | Paint measruePaint = new Paint(); 106 | measruePaint.set(getPaint()); 107 | 108 | Rect textRect = new Rect(); 109 | measruePaint.getTextBounds((String) text, 0, text.length(), textRect); 110 | 111 | int textWidth = textRect.width(); 112 | if (textWidth <= 0 || textWidth <= mAvailableWidth) return; 113 | 114 | //reduce textSize 115 | mCurrentTextSize = getTextSize(); 116 | while (textWidth > mAvailableWidth) { 117 | mCurrentTextSize--; 118 | measruePaint.setTextSize(mCurrentTextSize); 119 | measruePaint.getTextBounds((String) text, 0, text.length(), textRect); 120 | textWidth = textRect.width(); 121 | } 122 | setTextSize(TypedValue.COMPLEX_UNIT_PX, mCurrentTextSize); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BMSimpleSeekBar.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.graphics.Canvas; 9 | import android.graphics.Matrix; 10 | import android.graphics.Paint; 11 | import android.text.TextUtils; 12 | import android.util.AttributeSet; 13 | import android.view.MotionEvent; 14 | import android.view.View; 15 | 16 | import com.benmu.widget.R; 17 | import com.benmu.widget.utils.BaseCommonUtil; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * seekBar like wechat fontsizeSelector Created by Carry on 2017/6/30. 24 | */ 25 | 26 | public class BMSimpleSeekBar extends View { 27 | private int mHeight; 28 | private int mWidth; 29 | private Paint mLinePaint; 30 | private Paint mTextPaint; 31 | private int mPaintWidth = 2; 32 | //距两边的距离 33 | private int mMargenLine = 30; 34 | //小球半径 35 | private int mSeekBarRadius = 60; 36 | //刻度数 37 | private int mMarkCount = 5; 38 | 39 | private float[] markers; 40 | 41 | private int distanceX; 42 | 43 | private int mMarkHeight = 20; 44 | 45 | private int mTextMargin = 100; 46 | 47 | private List markerList; 48 | 49 | private Context mContext; 50 | 51 | private int mSeekBarId; 52 | 53 | private float mDesigenRaduis; 54 | 55 | private Bitmap mSeekBar; 56 | 57 | private int mCurrentSeekBarPosition; 58 | 59 | private boolean mFollow; 60 | 61 | private float mSeekBarOffset; 62 | 63 | private int mPreviousPosition; 64 | 65 | private OnPositionChangedListener mListener; 66 | 67 | 68 | public BMSimpleSeekBar(Context context) { 69 | this(context, null); 70 | } 71 | 72 | public BMSimpleSeekBar(Context context, AttributeSet attrs) { 73 | this(context, attrs, 0); 74 | } 75 | 76 | public BMSimpleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { 77 | super(context, attrs, defStyleAttr); 78 | mContext = context; 79 | TypedArray t = null; 80 | mLinePaint = new Paint(); 81 | mTextPaint = new Paint(); 82 | mTextPaint.setAntiAlias(true); 83 | mLinePaint.setAntiAlias(true); 84 | markerList = new ArrayList<>(); 85 | 86 | try { 87 | t = context.obtainStyledAttributes(attrs, R.styleable.BMSimpleSeekBar, 88 | 0, defStyleAttr); 89 | int lineColor = t.getColor(R.styleable.BMSimpleSeekBar_lineColor, 90 | getResources().getColor(android.R.color.holo_blue_light)); 91 | mLinePaint.setColor(lineColor); 92 | int textColor = t.getColor(R.styleable.BMSimpleSeekBar_textColor, 93 | getResources().getColor(android.R.color.holo_blue_light)); 94 | mTextPaint.setColor(textColor); 95 | mSeekBarId = t.getResourceId(R.styleable.BMSimpleSeekBar_seekBar, R.drawable.bg_black); 96 | mDesigenRaduis = t.getDimension(R.styleable.BMSimpleSeekBar_raduis, 0); 97 | mMargenLine = Math.round(t.getDimension(R.styleable.BMSimpleSeekBar_margin_line, 0)); 98 | } finally { 99 | if (t != null) { 100 | t.recycle(); 101 | } 102 | } 103 | 104 | } 105 | 106 | @Override 107 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 108 | super.onSizeChanged(w, h, oldw, oldh); 109 | mHeight = h; 110 | mWidth = w; 111 | } 112 | 113 | 114 | @Override 115 | protected void onDraw(Canvas canvas) { 116 | super.onDraw(canvas); 117 | //draw line 118 | drawLine(canvas); 119 | //draw mark 120 | drawMark(canvas); 121 | //draw text 122 | drawText(canvas); 123 | //draw seekbar 124 | drawSeekBar(canvas); 125 | } 126 | 127 | 128 | @Override 129 | public boolean onTouchEvent(MotionEvent event) { 130 | switch (event.getActionMasked()) { 131 | case MotionEvent.ACTION_DOWN: 132 | float startX = event.getX(); 133 | //if down action x smaller than raduis 134 | mFollow = Math.abs(startX - mSeekBarOffset) <= mDesigenRaduis; 135 | if (!mFollow) { 136 | int position = (int) ((event.getX() - mMargenLine) / (distanceX / 2)); 137 | mCurrentSeekBarPosition = (position + 1) / 2; 138 | changePosition(); 139 | } 140 | 141 | break; 142 | case MotionEvent.ACTION_MOVE: 143 | //if follow mode change seekBar position 144 | if (mFollow) { 145 | // in case out of edge 146 | if (event.getX() >= mMargenLine && event.getX() <= (mWidth - mMargenLine)) { 147 | int position = (int) ((event.getX() - mMargenLine) / (distanceX / 2)); 148 | mCurrentSeekBarPosition = (position + 1) / 2; 149 | changePosition(); 150 | } 151 | } 152 | break; 153 | case MotionEvent.ACTION_UP: 154 | break; 155 | } 156 | return true; 157 | } 158 | 159 | private void changePosition() { 160 | if (mCurrentSeekBarPosition < markers.length) { 161 | mSeekBarOffset = markers[mCurrentSeekBarPosition]; 162 | invalidate(); 163 | } 164 | if (mCurrentSeekBarPosition != mPreviousPosition) { 165 | if (mListener != null) { 166 | mListener.onPositionChanged(mCurrentSeekBarPosition, markerList.get 167 | (mCurrentSeekBarPosition)); 168 | } 169 | mPreviousPosition = mCurrentSeekBarPosition; 170 | } 171 | 172 | } 173 | 174 | private void drawSeekBar(Canvas canvas) { 175 | if (mSeekBar == null) { 176 | initBitmap(); 177 | } 178 | if (mSeekBar == null) return; 179 | if (mCurrentSeekBarPosition < markers.length) { 180 | canvas.drawBitmap(mSeekBar, mSeekBarOffset - mDesigenRaduis, 181 | mHeight / 2 - mDesigenRaduis, 182 | mTextPaint); 183 | } 184 | } 185 | 186 | private void initBitmap() { 187 | Resources resources = mContext.getResources(); 188 | if (mDesigenRaduis != 0) { 189 | //zoom 190 | Bitmap bitmap = BitmapFactory.decodeResource(resources, mSeekBarId); 191 | mSeekBar = zoomImage(bitmap, mDesigenRaduis * 2, mDesigenRaduis * 2); 192 | } 193 | if (mCurrentSeekBarPosition < markers.length) { 194 | mSeekBarOffset = markers[mCurrentSeekBarPosition]; 195 | } 196 | if (mPreviousPosition != mCurrentSeekBarPosition) { 197 | mPreviousPosition = mCurrentSeekBarPosition; 198 | } 199 | } 200 | 201 | 202 | private Bitmap zoomImage(Bitmap bitmap, float targetWidth, float targetHeight) { 203 | if (bitmap == null) { 204 | return null; 205 | } 206 | int width = bitmap.getWidth(); 207 | int height = bitmap.getHeight(); 208 | 209 | if (width > 0 && height > 0) { 210 | float widthScale = targetWidth / width; 211 | float heightScale = targetHeight / height; 212 | Matrix matrix = new Matrix(); 213 | matrix.postScale(widthScale, heightScale); 214 | return Bitmap.createBitmap(bitmap, 0, 0, width, height, 215 | matrix, 216 | true); 217 | 218 | } 219 | return null; 220 | } 221 | 222 | 223 | private void drawText(Canvas canvas) { 224 | for (int i = 0; i < markerList.size(); i++) { 225 | float x = markers[i]; 226 | Marker marker = markerList.get(i); 227 | if (!TextUtils.isEmpty(marker.getMarkerText())) { 228 | if (marker.getTextColor() != 0) { 229 | mTextPaint.setColor(marker.getTextColor()); 230 | } 231 | int textPixelSize = marker.getTextPixelSize(); 232 | if (textPixelSize != 0) { 233 | mTextPaint.setTextSize(BaseCommonUtil.dp2px(mContext, textPixelSize / 2)); 234 | } 235 | mTextPaint.setFakeBoldText(true); 236 | float textWidth = mTextPaint.measureText(marker.getMarkerText()); 237 | canvas.drawText(marker.getMarkerText(), x - textWidth / 2, mHeight / 2 - 238 | mTextMargin, 239 | mTextPaint); 240 | } 241 | 242 | } 243 | } 244 | 245 | private void drawMark(Canvas canvas) { 246 | mMarkCount = markerList.size(); 247 | markers = new float[mMarkCount]; 248 | distanceX = (mWidth - mMargenLine * 2) / (mMarkCount - 1); 249 | for (int i = 0; i < mMarkCount; i++) { 250 | markers[i] = i * distanceX + mMargenLine; 251 | } 252 | for (float x : markers) { 253 | canvas.drawLine(x, mHeight / 2 - mMarkHeight / 2, x, mHeight / 2 + mMarkHeight / 2, 254 | mLinePaint); 255 | } 256 | } 257 | 258 | private void drawLine(Canvas canvas) { 259 | mLinePaint.setStyle(Paint.Style.FILL); 260 | mLinePaint.setStrokeWidth(BaseCommonUtil.dp2px(getContext(), mPaintWidth/2)); 261 | canvas.drawLine(getPaddingLeft() + mMargenLine, mHeight / 2, mWidth - mMargenLine - 262 | getPaddingRight(), mHeight / 2, mLinePaint); 263 | 264 | } 265 | 266 | public void setOnPositionChangedListner(OnPositionChangedListener listner) { 267 | this.mListener = listner; 268 | } 269 | 270 | /** 271 | * ser markers 272 | */ 273 | public void setMarkers(List list) { 274 | markerList.clear(); 275 | markerList.addAll(list); 276 | } 277 | 278 | /** 279 | * set origin position 280 | */ 281 | public void setOriginPosition(int originPosition) { 282 | mCurrentSeekBarPosition = originPosition; 283 | mPreviousPosition = originPosition; 284 | } 285 | 286 | /** 287 | * set left and right marigin 288 | */ 289 | public void setMariginLine(int mariginLine) { 290 | this.mMargenLine = mariginLine; 291 | } 292 | 293 | 294 | public static class Marker { 295 | private String markerText; 296 | private int textColor; 297 | private int textPixelSize; 298 | 299 | public Marker(String markerText, int textColor) { 300 | this.markerText = markerText; 301 | this.textColor = textColor; 302 | } 303 | 304 | public Marker(String markerText, int textColor, int textPixelSize) { 305 | this.markerText = markerText; 306 | this.textColor = textColor; 307 | this.textPixelSize = textPixelSize; 308 | } 309 | 310 | public int getTextPixelSize() { 311 | return textPixelSize; 312 | } 313 | 314 | public void setTextPixelSize(int textPixelSize) { 315 | this.textPixelSize = textPixelSize; 316 | } 317 | 318 | public String getMarkerText() { 319 | return markerText; 320 | } 321 | 322 | public void setMarkerText(String markerText) { 323 | this.markerText = markerText; 324 | } 325 | 326 | public int getTextColor() { 327 | return textColor; 328 | } 329 | 330 | public void setTextColor(int textColor) { 331 | this.textColor = textColor; 332 | } 333 | } 334 | 335 | public interface OnPositionChangedListener { 336 | void onPositionChanged(int position, Marker maker); 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/BaseToolBar.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Color; 6 | import android.graphics.drawable.BitmapDrawable; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.annotation.Nullable; 9 | import android.text.TextUtils; 10 | import android.util.AttributeSet; 11 | import android.util.Log; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.ImageView; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | 18 | import com.benmu.widget.R; 19 | import com.benmu.widget.utils.BaseCommonUtil; 20 | import com.benmu.widget.utils.ColorUtils; 21 | 22 | import org.w3c.dom.Text; 23 | 24 | 25 | /** 26 | * Created by Carry on 17/2/23. 自定义toolbar 27 | */ 28 | 29 | 30 | public class BaseToolBar extends LinearLayout { 31 | private View mChildView; 32 | public ImageView mLeftIcon; 33 | public TextView mTitle; 34 | private TextView mRightText; 35 | public ImageView mRightIcon; 36 | private LinearLayout mContainer; 37 | private OnTitleClick mOnTitleClick; 38 | private OnLeftIconClick mOnLeftListenner; 39 | private OnRightIconClick mOnRightListenner; 40 | private OnWebViewClosed mOnWebViewClosedListenner; 41 | public TextView mWebViewClosed; 42 | public Context mContext; 43 | private ViewGroup mRightViewGroper; 44 | 45 | public BaseToolBar(Context context) { 46 | this(context, null, 0); 47 | } 48 | 49 | public BaseToolBar(Context context, @Nullable AttributeSet attrs) { 50 | this(context, attrs, 0); 51 | } 52 | 53 | public BaseToolBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 54 | super(context, attrs, defStyleAttr); 55 | this.mContext = context; 56 | initView(); 57 | initListenner(); 58 | } 59 | 60 | /** 61 | * set nav test color 62 | */ 63 | public void setNavigationItemColor(String color, View view) { 64 | if (view == null || TextUtils.isEmpty(color)) return; 65 | if (view instanceof ViewGroup) { 66 | ViewGroup viewGroup = (ViewGroup) view; 67 | int childCount = viewGroup.getChildCount(); 68 | for (int i = 0; i < childCount; i++) { 69 | View childView = viewGroup.getChildAt(i); 70 | if (childView instanceof ViewGroup) { 71 | setNavigationItemColor(color, childView); 72 | } else if (childView instanceof TextView) { 73 | ((TextView) childView).setTextColor(ColorUtils.getColor(color)); 74 | }else if(childView instanceof ImageView){ 75 | ImageView imageItem=(ImageView)childView; 76 | Drawable drawable = imageItem.getDrawable(); 77 | if(drawable instanceof BitmapDrawable){ 78 | BitmapDrawable drawable1 = (BitmapDrawable) drawable; 79 | Bitmap bitmap = drawable1.getBitmap(); 80 | Bitmap bitmap1 = BaseCommonUtil.tintBitmap(bitmap, ColorUtils.getColor 81 | (color)); 82 | imageItem.setImageBitmap(bitmap1); 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | private void initListenner() { 90 | if (mTitle != null) { 91 | mTitle.setOnClickListener(new OnClickListener() { 92 | @Override 93 | public void onClick(View v) { 94 | if (mOnTitleClick != null) { 95 | mOnTitleClick.onClick(v); 96 | } 97 | } 98 | }); 99 | } 100 | if (mLeftIcon != null) { 101 | mLeftIcon.setOnClickListener(new OnClickListener() { 102 | @Override 103 | public void onClick(View v) { 104 | if (mOnLeftListenner != null) { 105 | mOnLeftListenner.onClick(v); 106 | } 107 | } 108 | }); 109 | } 110 | 111 | if (mRightIcon != null) { 112 | mRightIcon.setOnClickListener(new OnClickListener() { 113 | @Override 114 | public void onClick(View v) { 115 | if (mOnRightListenner != null) { 116 | mOnRightListenner.onClick(v); 117 | } 118 | } 119 | }); 120 | } 121 | 122 | if (mRightText != null) { 123 | mRightText.setOnClickListener(new OnClickListener() { 124 | @Override 125 | public void onClick(View v) { 126 | if (mOnRightListenner != null) { 127 | mOnRightListenner.onClick(v); 128 | } 129 | } 130 | }); 131 | } 132 | 133 | if (mWebViewClosed != null) { 134 | mWebViewClosed.setOnClickListener(new OnClickListener() { 135 | @Override 136 | public void onClick(View v) { 137 | if (mOnWebViewClosedListenner != null) { 138 | mOnWebViewClosedListenner.onClick(v); 139 | } 140 | } 141 | }); 142 | } 143 | } 144 | 145 | 146 | public void setBackground(int resId) { 147 | if (mContainer != null) { 148 | mContainer.setBackgroundResource(resId); 149 | } 150 | } 151 | 152 | public void setBackgroundColor(int color) { 153 | mContainer.setBackgroundColor(color); 154 | } 155 | 156 | public void setNavigationVisible(boolean b) { 157 | setVisibility(b ? VISIBLE : GONE); 158 | } 159 | 160 | public void setTitle(CharSequence title) { 161 | if (mTitle != null) { 162 | mTitle.setText(title); 163 | } 164 | } 165 | 166 | public void setLeftIconVisible(boolean b) { 167 | if (mLeftIcon != null) { 168 | mLeftIcon.setVisibility(b ? VISIBLE : 169 | GONE); 170 | } 171 | } 172 | 173 | public TextView getLeftTextView() { 174 | mWebViewClosed.setVisibility(View.VISIBLE); 175 | return mWebViewClosed; 176 | } 177 | 178 | public ImageView getLeftIcon() { 179 | return mLeftIcon; 180 | } 181 | 182 | public TextView getRightText() { 183 | return mRightText; 184 | } 185 | 186 | public ImageView getRightIcon() { 187 | return mRightIcon; 188 | } 189 | 190 | public TextView getTitleTextView() { 191 | return mTitle; 192 | } 193 | 194 | public void setLeftTextColor(String color) { 195 | if (mWebViewClosed != null) { 196 | mWebViewClosed.setTextColor(ColorUtils.getColor(color)); 197 | } 198 | } 199 | 200 | public void setLeftIcon(int resId) { 201 | if (mLeftIcon != null) { 202 | mLeftIcon.setImageResource(resId); 203 | } 204 | } 205 | 206 | 207 | public void setRightText(CharSequence title) { 208 | if (mRightText != null) { 209 | mRightText.setVisibility(View.VISIBLE); 210 | mRightText.setText(title); 211 | } 212 | } 213 | 214 | public void setRightTextColor(String color) { 215 | if (mRightText != null) { 216 | mRightText.setTextColor(ColorUtils.getColor(color)); 217 | } 218 | } 219 | 220 | public void setRightIcon(int resId) { 221 | if (mRightIcon != null) { 222 | mRightIcon.setImageResource(resId); 223 | } 224 | } 225 | 226 | 227 | public void setRightIconVisible(boolean b) { 228 | if (mRightIcon != null) { 229 | mRightIcon.setVisibility(b ? VISIBLE : 230 | GONE); 231 | } 232 | } 233 | 234 | public void setOnTitleListenner(OnTitleClick listenner) { 235 | this.mOnTitleClick = listenner; 236 | } 237 | 238 | public void setTitleColor(String color) { 239 | if (mTitle != null) { 240 | mTitle.setTextColor(ColorUtils.getColor(color)); 241 | } 242 | } 243 | 244 | 245 | private void initView() { 246 | if (mChildView == null) { 247 | mChildView = View.inflate(getContext(), R.layout.layout_custom_toolbar, null); 248 | } 249 | LayoutParams params = new LayoutParams(ViewGroup.LayoutParams 250 | .MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 251 | addView(mChildView, params); 252 | mContainer = (LinearLayout) mChildView.findViewById(R.id.toolbar_container); 253 | mLeftIcon = (ImageView) mChildView.findViewById(R.id.toolbar_left_icon); 254 | mTitle = (TextView) mChildView.findViewById(R.id.toolbar_title); 255 | mRightViewGroper = (ViewGroup) mChildView.findViewById(R.id.toolbar_right); 256 | mRightText = (TextView) mChildView.findViewById(R.id.toolbar_right_text); 257 | mRightIcon = (ImageView) mChildView.findViewById(R.id.toolbar_right_icon); 258 | mWebViewClosed = (TextView) mChildView.findViewById(R.id.action_bar_tvitem_webclose); 259 | } 260 | 261 | public void setOnWebViewClosedVisiblty(boolean visiblty) { 262 | mWebViewClosed.setVisibility(visiblty ? VISIBLE : GONE); 263 | } 264 | 265 | public interface OnLeftIconClick { 266 | void onClick(View v); 267 | } 268 | 269 | public interface OnRightIconClick { 270 | void onClick(View v); 271 | } 272 | 273 | public interface OnWebViewClosed { 274 | void onClick(View v); 275 | } 276 | 277 | public interface OnTitleClick { 278 | void onClick(View v); 279 | } 280 | 281 | public void setOnWebClosedListenner(OnWebViewClosed listenner) { 282 | this.mOnWebViewClosedListenner = listenner; 283 | } 284 | 285 | public void setOnLeftListenner(OnLeftIconClick listenner) { 286 | this.mOnLeftListenner = listenner; 287 | } 288 | 289 | public void setOnRightListenner(OnRightIconClick listenner) { 290 | this.mOnRightListenner = listenner; 291 | } 292 | 293 | 294 | } 295 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/DebugErrorDialog.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | import android.text.method.ScrollingMovementMethod; 7 | import android.view.Gravity; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.Window; 11 | import android.view.WindowManager; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | 15 | import com.benmu.widget.R; 16 | 17 | /** 18 | * Created by liuyuanxiao on 17/12/26. 19 | */ 20 | 21 | public class DebugErrorDialog { 22 | TextView messageTv; 23 | Dialog debugErrorDialog; 24 | 25 | public Dialog createErrorDialog(Context context) { 26 | LayoutInflater inflater = LayoutInflater.from(context); 27 | View v = inflater.inflate(R.layout.dialog_debug_error_layout, null);// 得到加载view 28 | messageTv = (TextView) v.findViewById(R.id.tvMsg); 29 | messageTv.setMovementMethod(ScrollingMovementMethod.getInstance()); 30 | debugErrorDialog = new Dialog(context, R.style.MyDialogStyle);// 创建自定义样式dialog 31 | debugErrorDialog.setCancelable(true); // 是否可以按“返回键”消失 32 | debugErrorDialog.setCanceledOnTouchOutside(false); // 点击加载框以外的区域 33 | debugErrorDialog.setContentView(v, new LinearLayout.LayoutParams( 34 | LinearLayout.LayoutParams.MATCH_PARENT, 35 | LinearLayout.LayoutParams.MATCH_PARENT));// 设置布局 36 | /** 37 | *将显示Dialog的方法封装在这里面 38 | */ 39 | Window window = debugErrorDialog.getWindow(); 40 | WindowManager.LayoutParams lp = window.getAttributes(); 41 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; 42 | lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 43 | window.setGravity(Gravity.CENTER); 44 | window.setAttributes(lp); 45 | window.setWindowAnimations(R.style.PopWindowAnimStyle); 46 | 47 | return debugErrorDialog; 48 | } 49 | 50 | public void setTextMsg(String msg) { 51 | String cMsg = messageTv.getText().toString(); 52 | if (!TextUtils.isEmpty(cMsg)) { 53 | cMsg += "\n"; 54 | } 55 | messageTv.setText(cMsg + msg); 56 | 57 | } 58 | 59 | public void show() { 60 | if (debugErrorDialog.isShowing()) return; 61 | ; 62 | debugErrorDialog.show(); 63 | } 64 | 65 | public void dismiss() { 66 | if (debugErrorDialog.isShowing()) { 67 | debugErrorDialog.dismiss(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/MaterialCircleView.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Bitmap; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.Paint; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.PorterDuffXfermode; 12 | import android.graphics.RectF; 13 | import android.util.AttributeSet; 14 | import android.util.TypedValue; 15 | import android.view.View; 16 | 17 | import com.benmu.widget.R; 18 | 19 | 20 | /** 21 | * Created by aliouswang on 15/5/6. 22 | */ 23 | public class MaterialCircleView extends View { 24 | 25 | private boolean bGradient; 26 | 27 | private int circleColor; 28 | private int circleWidth; 29 | private int radius; 30 | 31 | private Paint mPaint; 32 | private int rotateDelta = 4; 33 | private int curAngle = 0; 34 | 35 | private int minAngle = 0; 36 | private int startAngle = 0; 37 | private int endAngle = 120; 38 | 39 | private int sWidth; 40 | private int sHeight; 41 | 42 | private int halfWidth; 43 | private int halfHeight; 44 | 45 | private int red = 0; 46 | private int green = 0; 47 | private int blue = 255; 48 | 49 | int phase = 0; 50 | 51 | 52 | public MaterialCircleView(Context context) { 53 | this(context, null); 54 | } 55 | 56 | public MaterialCircleView(Context context, AttributeSet attrs) { 57 | this(context, attrs, 0); 58 | } 59 | 60 | public MaterialCircleView(Context context, AttributeSet attrs, int defStyleAttr) { 61 | super(context, attrs, defStyleAttr); 62 | 63 | TypedArray t = null; 64 | try { 65 | t = context.obtainStyledAttributes(attrs, R.styleable.MaterialCircleView, 66 | 0, defStyleAttr); 67 | setbGradient(t.getBoolean(R.styleable.MaterialCircleView_bGradient, true)); 68 | circleColor = t.getColor(R.styleable.MaterialCircleView_circleColor, 69 | getResources().getColor(android.R.color.holo_blue_light)); 70 | circleWidth = t.getDimensionPixelSize(R.styleable.MaterialCircleView_circleWidth, 71 | 10); 72 | radius = t.getDimensionPixelSize(R.styleable.MaterialCircleView_radius, 73 | 50); 74 | } finally { 75 | if (t != null) { 76 | t.recycle(); 77 | } 78 | } 79 | 80 | mPaint = new Paint(); 81 | if (isbGradient()) { 82 | mPaint.setColor(Color.rgb(red, green, blue)); 83 | }else { 84 | mPaint.setColor(circleColor); 85 | } 86 | mPaint.setAntiAlias(true); 87 | setBackgroundColor(getResources().getColor(android.R.color.transparent)); 88 | } 89 | 90 | 91 | 92 | @Override 93 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 94 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 95 | 96 | sWidth = this.getMeasuredWidth(); 97 | sHeight = this.getMeasuredHeight(); 98 | halfWidth = sWidth / 2; 99 | halfHeight = sHeight / 2; 100 | } 101 | 102 | private int colorDelta = 2; 103 | private void checkPaint() { 104 | if (isbGradient()) { 105 | switch (phase % 5) { 106 | case 0: 107 | green += colorDelta; 108 | if (green > 255) { 109 | green = 255; 110 | phase ++; 111 | } 112 | break; 113 | case 1: 114 | red += colorDelta; 115 | green -= colorDelta; 116 | if (red > 255) { 117 | red = 255; 118 | green = 0; 119 | phase ++; 120 | } 121 | break; 122 | case 2: 123 | blue -= colorDelta; 124 | if (blue < 0) { 125 | blue = 0; 126 | phase ++; 127 | } 128 | break; 129 | case 3: 130 | red -= colorDelta; 131 | green += colorDelta; 132 | if (red < 0) { 133 | red = 0; 134 | green = 255; 135 | phase ++; 136 | } 137 | break; 138 | case 4: 139 | green -= colorDelta; 140 | blue += colorDelta; 141 | if (green < 0) { 142 | green = 0; 143 | blue = 255; 144 | phase ++; 145 | } 146 | break; 147 | } 148 | 149 | 150 | mPaint.setColor(Color.rgb(red, green, blue)); 151 | } 152 | } 153 | 154 | @Override 155 | protected void onDraw(Canvas canvas) { 156 | super.onDraw(canvas); 157 | if (startAngle == minAngle) { 158 | endAngle += 6; 159 | } 160 | if (endAngle >= 280 || startAngle > minAngle) { 161 | startAngle += 6; 162 | if(endAngle > 20) { 163 | endAngle -= 6; 164 | } 165 | 166 | } 167 | if (startAngle > minAngle + 280) { 168 | minAngle = startAngle; 169 | startAngle = minAngle; 170 | endAngle = 20; 171 | } 172 | 173 | checkPaint(); 174 | canvas.rotate(curAngle += rotateDelta, halfWidth, halfHeight); 175 | 176 | Bitmap bitmap = Bitmap.createBitmap(sWidth, sHeight, Bitmap.Config.ARGB_8888); 177 | Canvas bmpCanvas = new Canvas(bitmap); 178 | bmpCanvas.drawArc(new RectF(0, 0, sWidth, sHeight), startAngle, endAngle, true, mPaint); 179 | Paint transparentPaint = new Paint(); 180 | transparentPaint.setAntiAlias(true); 181 | transparentPaint.setColor(getResources().getColor(android.R.color.transparent)); 182 | transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 183 | bmpCanvas.drawCircle(halfWidth, halfHeight, 184 | halfWidth - circleWidth, transparentPaint); 185 | canvas.drawBitmap(bitmap, 0, 0, new Paint()); 186 | 187 | invalidate(); 188 | } 189 | 190 | /** 191 | * Convert Dp to Pixel 192 | */ 193 | public static int dpToPx(float dp, Resources resources){ 194 | float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics()); 195 | return (int) px; 196 | } 197 | 198 | /** 199 | * need gradient or not 200 | */ 201 | public boolean isbGradient() { 202 | return bGradient; 203 | } 204 | 205 | public void setbGradient(boolean bGradient) { 206 | this.bGradient = bGradient; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/MaxScrollView.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.widget.ScrollView; 7 | 8 | import com.benmu.widget.R; 9 | 10 | 11 | /** 12 | * @ClassName: MaxSrollView 13 | * @Description: TODO(in one word) 14 | * @author yuemeilin 15 | * @date 2014-12-4 上午10:46:31 16 | */ 17 | public class MaxScrollView extends ScrollView { 18 | 19 | private int maxHeight; 20 | 21 | public MaxScrollView(Context context) { 22 | super(context); 23 | init(context, null); 24 | } 25 | 26 | public MaxScrollView(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | init(context, attrs); 29 | } 30 | 31 | public MaxScrollView(Context context, AttributeSet attrs, int defStyle) { 32 | super(context, attrs, defStyle); 33 | init(context, attrs); 34 | } 35 | 36 | /** 动态设置最大高度 */ 37 | public void setMaxHeight(int resId) { 38 | maxHeight = resId; 39 | } 40 | 41 | private void init(Context context, AttributeSet attrs) { 42 | if (attrs != null) { 43 | TypedArray typedArray = context.obtainStyledAttributes(attrs, 44 | R.styleable.MaxScrollViewStyle); 45 | /** 获取自定义属性和默认值 */ 46 | maxHeight = (int) typedArray.getDimension( 47 | R.styleable.MaxScrollViewStyle_maxHeight, -1); 48 | typedArray.recycle(); 49 | } 50 | } 51 | 52 | @Override 53 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 54 | int height = MeasureSpec.getSize(heightMeasureSpec); 55 | if (maxHeight >= 0 && height > maxHeight) { 56 | height = MeasureSpec.makeMeasureSpec(maxHeight, 57 | MeasureSpec.getMode(heightMeasureSpec)); 58 | } 59 | super.onMeasure(widthMeasureSpec, height); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/AnimatorListener.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.animation.Animator; 4 | 5 | class AnimatorListener implements Animator.AnimatorListener { 6 | @Override 7 | public void onAnimationStart(Animator animator) { 8 | } 9 | 10 | @Override 11 | public void onAnimationEnd(Animator animator) { 12 | } 13 | 14 | @Override 15 | public void onAnimationCancel(Animator animator) { 16 | } 17 | 18 | @Override 19 | public void onAnimationRepeat(Animator animator) { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/CalendarDay.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | 8 | import java.util.Calendar; 9 | import java.util.Date; 10 | 11 | /** 12 | * An imputable representation of a day on a calendar 13 | */ 14 | public final class CalendarDay implements Parcelable { 15 | 16 | private boolean isExacyltSameDay; 17 | 18 | public boolean isExacyltSameDay() { 19 | return isExacyltSameDay; 20 | } 21 | 22 | public void setExacyltSameDay(boolean exacyltSameDay) { 23 | isExacyltSameDay = exacyltSameDay; 24 | } 25 | 26 | /** 27 | * Get a new instance set to today 28 | * 29 | * @return CalendarDay set to today's date 30 | */ 31 | @NonNull 32 | public static CalendarDay today() { 33 | return from(CalendarUtils.getInstance()); 34 | } 35 | 36 | /** 37 | * Get a new instance set to the specified day 38 | * 39 | * @param year new instance's year 40 | * @param month new instance's month as defined by {@linkplain Calendar} 41 | * @param day new instance's day of month 42 | * @return CalendarDay set to the specified date 43 | */ 44 | @NonNull 45 | public static CalendarDay from(int year, int month, int day) { 46 | return new CalendarDay(year, month, day); 47 | } 48 | 49 | /** 50 | * Get a new instance set to the specified day 51 | * 52 | * @param calendar {@linkplain Calendar} to pull date information from. Passing null will return 53 | * null 54 | * @return CalendarDay set to the specified date 55 | */ 56 | public static CalendarDay from(@Nullable Calendar calendar) { 57 | if (calendar == null) { 58 | return null; 59 | } 60 | return from( 61 | CalendarUtils.getYear(calendar), 62 | CalendarUtils.getMonth(calendar), 63 | CalendarUtils.getDay(calendar) 64 | ); 65 | } 66 | 67 | /** 68 | * Get a new instance set to the specified day 69 | * 70 | * @param date {@linkplain Date} to pull date information from. Passing null will return null. 71 | * @return CalendarDay set to the specified date 72 | */ 73 | public static CalendarDay from(@Nullable Date date) { 74 | if (date == null) { 75 | return null; 76 | } 77 | return from(CalendarUtils.getInstance(date)); 78 | } 79 | 80 | private final int year; 81 | private final int month; 82 | private final int day; 83 | 84 | /** 85 | * Cache for calls to {@linkplain #getCalendar()} 86 | */ 87 | private transient Calendar _calendar; 88 | 89 | /** 90 | * Cache for calls to {@linkplain #getDate()} 91 | */ 92 | private transient Date _date; 93 | 94 | /** 95 | * Initialized to the current day 96 | * 97 | * @see CalendarDay#today() 98 | */ 99 | @Deprecated 100 | public CalendarDay() { 101 | this(CalendarUtils.getInstance()); 102 | } 103 | 104 | /** 105 | * @param calendar source to pull date information from for this instance 106 | * @see CalendarDay#from(Calendar) 107 | */ 108 | @Deprecated 109 | public CalendarDay(Calendar calendar) { 110 | this( 111 | CalendarUtils.getYear(calendar), 112 | CalendarUtils.getMonth(calendar), 113 | CalendarUtils.getDay(calendar) 114 | ); 115 | } 116 | 117 | /** 118 | * @param year new instance's year 119 | * @param month new instance's month as defined by {@linkplain Calendar} 120 | * @param day new instance's day of month 121 | * @see CalendarDay#from(Calendar) 122 | */ 123 | @Deprecated 124 | public CalendarDay(int year, int month, int day) { 125 | this.year = year; 126 | this.month = month; 127 | this.day = day; 128 | } 129 | 130 | /** 131 | * @param date source to pull date information from for this instance 132 | * @see CalendarDay#from(Date) 133 | */ 134 | @Deprecated 135 | public CalendarDay(Date date) { 136 | this(CalendarUtils.getInstance(date)); 137 | } 138 | 139 | /** 140 | * Get the year 141 | * 142 | * @return the year for this day 143 | */ 144 | public int getYear() { 145 | return year; 146 | } 147 | 148 | /** 149 | * Get the month, represented by values from {@linkplain Calendar} 150 | * 151 | * @return the month of the year as defined by {@linkplain Calendar} 152 | */ 153 | public int getMonth() { 154 | return month; 155 | } 156 | 157 | /** 158 | * Get the day 159 | * 160 | * @return the day of the month for this day 161 | */ 162 | public int getDay() { 163 | return day; 164 | } 165 | 166 | /** 167 | * Get this day as a {@linkplain Date} 168 | * 169 | * @return a date with this days information 170 | */ 171 | @NonNull 172 | public Date getDate() { 173 | if (_date == null) { 174 | _date = getCalendar().getTime(); 175 | } 176 | return _date; 177 | } 178 | 179 | /** 180 | * Get this day as a {@linkplain Calendar} 181 | * 182 | * @return a new calendar instance with this day information 183 | */ 184 | @NonNull 185 | public Calendar getCalendar() { 186 | if (_calendar == null) { 187 | _calendar = CalendarUtils.getInstance(); 188 | copyTo(_calendar); 189 | } 190 | return _calendar; 191 | } 192 | 193 | void copyToMonthOnly(@NonNull Calendar calendar) { 194 | calendar.clear(); 195 | calendar.set(year, month, 1); 196 | } 197 | 198 | /** 199 | * Copy this day's information to the given calendar instance 200 | * 201 | * @param calendar calendar to set date information to 202 | */ 203 | public void copyTo(@NonNull Calendar calendar) { 204 | calendar.clear(); 205 | calendar.set(year, month, day); 206 | } 207 | 208 | /** 209 | * Determine if this day is within a specified range 210 | * 211 | * @param minDate the earliest day, may be null 212 | * @param maxDate the latest day, may be null 213 | * @return true if the between (inclusive) the min and max dates. 214 | */ 215 | public boolean isInRange(@Nullable CalendarDay minDate, @Nullable CalendarDay maxDate) { 216 | return !(minDate != null && minDate.isAfter(this)) && 217 | !(maxDate != null && maxDate.isBefore(this)); 218 | } 219 | 220 | /** 221 | * Determine if this day is before the given instance 222 | * 223 | * @param other the other day to test 224 | * @return true if this is before other, false if equal or after 225 | */ 226 | public boolean isBefore(@NonNull CalendarDay other) { 227 | if (other == null) { 228 | throw new IllegalArgumentException("other cannot be null"); 229 | } 230 | if (year == other.year) { 231 | return ((month == other.month) ? (day < other.day) : (month < other.month)); 232 | } else { 233 | return year < other.year; 234 | } 235 | } 236 | 237 | /** 238 | * Determine if this day is after the given instance 239 | * 240 | * @param other the other day to test 241 | * @return true if this is after other, false if equal or before 242 | */ 243 | public boolean isAfter(@NonNull CalendarDay other) { 244 | if (other == null) { 245 | throw new IllegalArgumentException("other cannot be null"); 246 | } 247 | 248 | if (year == other.year) { 249 | return (month == other.month) ? (day > other.day) : (month > other.month); 250 | } else { 251 | return year > other.year; 252 | } 253 | } 254 | 255 | @Override 256 | public boolean equals(Object o) { 257 | if (this == o) { 258 | return true; 259 | } 260 | if (o == null || getClass() != o.getClass()) { 261 | return false; 262 | } 263 | 264 | CalendarDay that = (CalendarDay) o; 265 | 266 | return day == that.day && month == that.month && year == that.year; 267 | } 268 | 269 | @Override 270 | public int hashCode() { 271 | return hashCode(year, month, day); 272 | } 273 | 274 | private static int hashCode(int year, int month, int day) { 275 | //Should produce hashes like "20150401" 276 | return (year * 10000) + (month * 100) + day; 277 | } 278 | 279 | @Override 280 | public String toString() { 281 | return year + "-" + (month+1) + "-" + day; 282 | } 283 | 284 | /* 285 | * Parcelable Stuff 286 | */ 287 | 288 | public CalendarDay(Parcel in) { 289 | this(in.readInt(), in.readInt(), in.readInt()); 290 | } 291 | 292 | @Override 293 | public int describeContents() { 294 | return 0; 295 | } 296 | 297 | @Override 298 | public void writeToParcel(Parcel dest, int flags) { 299 | dest.writeInt(year); 300 | dest.writeInt(month); 301 | dest.writeInt(day); 302 | } 303 | 304 | public static final Creator CREATOR = new Creator() { 305 | public CalendarDay createFromParcel(Parcel in) { 306 | return new CalendarDay(in); 307 | } 308 | 309 | public CalendarDay[] newArray(int size) { 310 | return new CalendarDay[size]; 311 | } 312 | }; 313 | 314 | 315 | } 316 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/CalendarMode.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | @Experimental 4 | public enum CalendarMode { 5 | 6 | MONTHS(6), 7 | WEEKS(1); 8 | 9 | final int visibleWeeksCount; 10 | 11 | CalendarMode(int visibleWeeksCount) { 12 | this.visibleWeeksCount = visibleWeeksCount; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/CalendarPager.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.BetterViewPager; 5 | import android.view.MotionEvent; 6 | 7 | /** 8 | * Custom ViewPager that allows swiping to be disabled. 9 | */ 10 | class CalendarPager extends BetterViewPager { 11 | 12 | private boolean pagingEnabled = true; 13 | 14 | public CalendarPager(Context context) { 15 | super(context); 16 | } 17 | 18 | /** 19 | * enable disable viewpager scroll 20 | * 21 | * @param pagingEnabled false to disable paging, true for paging (default) 22 | */ 23 | public void setPagingEnabled(boolean pagingEnabled) { 24 | this.pagingEnabled = pagingEnabled; 25 | } 26 | 27 | /** 28 | * @return is this viewpager allowed to page 29 | */ 30 | public boolean isPagingEnabled() { 31 | return pagingEnabled; 32 | } 33 | 34 | @Override 35 | public boolean onInterceptTouchEvent(MotionEvent ev) { 36 | return pagingEnabled && super.onInterceptTouchEvent(ev); 37 | } 38 | 39 | @Override 40 | public boolean onTouchEvent(MotionEvent ev) { 41 | return pagingEnabled && super.onTouchEvent(ev); 42 | } 43 | 44 | @Override 45 | public boolean canScrollVertically(int direction) { 46 | /** 47 | * disables scrolling vertically when paging disabled, fixes scrolling 48 | * for nested {@link android.support.v4.view.ViewPager} 49 | */ 50 | return pagingEnabled && super.canScrollVertically(direction); 51 | } 52 | 53 | @Override 54 | public boolean canScrollHorizontally(int direction) { 55 | /** 56 | * disables scrolling horizontally when paging disabled, fixes scrolling 57 | * for nested {@link android.support.v4.view.ViewPager} 58 | */ 59 | return pagingEnabled && super.canScrollHorizontally(direction); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/CalendarPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.view.PagerAdapter; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.benmu.widget.view.calendar.format.DayFormatter; 9 | import com.benmu.widget.view.calendar.format.TitleFormatter; 10 | import com.benmu.widget.view.calendar.format.WeekDayFormatter; 11 | 12 | import java.util.ArrayDeque; 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * Pager adapter backing the calendar view 19 | */ 20 | abstract class CalendarPagerAdapter extends PagerAdapter { 21 | 22 | private final ArrayDeque currentViews; 23 | 24 | protected final MaterialCalendarView mcv; 25 | private final CalendarDay today; 26 | 27 | private TitleFormatter titleFormatter = null; 28 | private Integer color = null; 29 | private Integer dateTextAppearance = null; 30 | private Integer weekDayTextAppearance = null; 31 | @MaterialCalendarView.ShowOtherDates 32 | private int showOtherDates = MaterialCalendarView.SHOW_DEFAULTS; 33 | private CalendarDay minDate = null; 34 | private CalendarDay maxDate = null; 35 | private DateRangeIndex rangeIndex; 36 | private List selectedDates = new ArrayList<>(); 37 | private WeekDayFormatter weekDayFormatter = WeekDayFormatter.DEFAULT; 38 | private DayFormatter dayFormatter = DayFormatter.DEFAULT; 39 | private List decorators = new ArrayList<>(); 40 | private List decoratorResults = null; 41 | private boolean selectionEnabled = true; 42 | 43 | CalendarPagerAdapter(MaterialCalendarView mcv) { 44 | this.mcv = mcv; 45 | this.today = CalendarDay.today(); 46 | currentViews = new ArrayDeque<>(); 47 | currentViews.iterator(); 48 | setRangeDates(null, null); 49 | } 50 | 51 | public void setDecorators(List decorators) { 52 | this.decorators = decorators; 53 | invalidateDecorators(); 54 | } 55 | 56 | public void invalidateDecorators() { 57 | decoratorResults = new ArrayList<>(); 58 | for (DayViewDecorator decorator : decorators) { 59 | DayViewFacade facade = new DayViewFacade(); 60 | decorator.decorate(facade); 61 | if (facade.isDecorated()) { 62 | decoratorResults.add(new DecoratorResult(decorator, facade)); 63 | } 64 | } 65 | for (V pagerView : currentViews) { 66 | pagerView.setDayViewDecorators(decoratorResults); 67 | } 68 | } 69 | 70 | @Override 71 | public int getCount() { 72 | return rangeIndex.getCount(); 73 | } 74 | 75 | @Override 76 | public CharSequence getPageTitle(int position) { 77 | return titleFormatter == null ? "" : titleFormatter.format(getItem(position)); 78 | } 79 | 80 | public CalendarPagerAdapter migrateStateAndReturn(CalendarPagerAdapter newAdapter) { 81 | newAdapter.titleFormatter = titleFormatter; 82 | newAdapter.color = color; 83 | newAdapter.dateTextAppearance = dateTextAppearance; 84 | newAdapter.weekDayTextAppearance = weekDayTextAppearance; 85 | newAdapter.showOtherDates = showOtherDates; 86 | newAdapter.minDate = minDate; 87 | newAdapter.maxDate = maxDate; 88 | newAdapter.selectedDates = selectedDates; 89 | newAdapter.weekDayFormatter = weekDayFormatter; 90 | newAdapter.dayFormatter = dayFormatter; 91 | newAdapter.decorators = decorators; 92 | newAdapter.decoratorResults = decoratorResults; 93 | newAdapter.selectionEnabled = selectionEnabled; 94 | return newAdapter; 95 | } 96 | 97 | public int getIndexForDay(CalendarDay day) { 98 | if (day == null) { 99 | return getCount() / 2; 100 | } 101 | if (minDate != null && day.isBefore(minDate)) { 102 | return 0; 103 | } 104 | if (maxDate != null && day.isAfter(maxDate)) { 105 | return getCount() - 1; 106 | } 107 | return rangeIndex.indexOf(day); 108 | } 109 | 110 | protected abstract V createView(int position); 111 | 112 | protected abstract int indexOf(V view); 113 | 114 | protected abstract boolean isInstanceOfView(Object object); 115 | 116 | protected abstract DateRangeIndex createRangeIndex(CalendarDay min, CalendarDay max); 117 | 118 | @Override 119 | public int getItemPosition(Object object) { 120 | if (!(isInstanceOfView(object))) { 121 | return POSITION_NONE; 122 | } 123 | CalendarPagerView pagerView = (CalendarPagerView) object; 124 | CalendarDay firstViewDay = pagerView.getFirstViewDay(); 125 | if (firstViewDay == null) { 126 | return POSITION_NONE; 127 | } 128 | int index = indexOf((V) object); 129 | if (index < 0) { 130 | return POSITION_NONE; 131 | } 132 | return index; 133 | } 134 | 135 | @Override 136 | public Object instantiateItem(ViewGroup container, int position) { 137 | V pagerView = createView(position); 138 | pagerView.setContentDescription(mcv.getCalendarContentDescription()); 139 | pagerView.setAlpha(0); 140 | pagerView.setSelectionEnabled(selectionEnabled); 141 | 142 | pagerView.setWeekDayFormatter(weekDayFormatter); 143 | pagerView.setDayFormatter(dayFormatter); 144 | if (color != null) { 145 | pagerView.setSelectionColor(color); 146 | } 147 | if (dateTextAppearance != null) { 148 | pagerView.setDateTextAppearance(dateTextAppearance); 149 | } 150 | if (weekDayTextAppearance != null) { 151 | pagerView.setWeekDayTextAppearance(weekDayTextAppearance); 152 | } 153 | pagerView.setShowOtherDates(showOtherDates); 154 | pagerView.setMinimumDate(minDate); 155 | pagerView.setMaximumDate(maxDate); 156 | pagerView.setSelectedDates(selectedDates); 157 | 158 | container.addView(pagerView); 159 | currentViews.add(pagerView); 160 | 161 | pagerView.setDayViewDecorators(decoratorResults); 162 | 163 | return pagerView; 164 | } 165 | 166 | public void setSelectionEnabled(boolean enabled) { 167 | selectionEnabled = enabled; 168 | for (V pagerView : currentViews) { 169 | pagerView.setSelectionEnabled(selectionEnabled); 170 | } 171 | } 172 | 173 | @Override 174 | public void destroyItem(ViewGroup container, int position, Object object) { 175 | CalendarPagerView pagerView = (CalendarPagerView) object; 176 | currentViews.remove(pagerView); 177 | container.removeView(pagerView); 178 | } 179 | 180 | @Override 181 | public boolean isViewFromObject(View view, Object object) { 182 | return view == object; 183 | } 184 | 185 | public void setTitleFormatter(@NonNull TitleFormatter titleFormatter) { 186 | this.titleFormatter = titleFormatter; 187 | } 188 | 189 | public void setSelectionColor(int color) { 190 | this.color = color; 191 | for (V pagerView : currentViews) { 192 | pagerView.setSelectionColor(color); 193 | } 194 | } 195 | 196 | public void setDateTextAppearance(int taId) { 197 | if (taId == 0) { 198 | return; 199 | } 200 | this.dateTextAppearance = taId; 201 | for (V pagerView : currentViews) { 202 | pagerView.setDateTextAppearance(taId); 203 | } 204 | } 205 | 206 | public void setShowOtherDates(@MaterialCalendarView.ShowOtherDates int showFlags) { 207 | this.showOtherDates = showFlags; 208 | for (V pagerView : currentViews) { 209 | pagerView.setShowOtherDates(showFlags); 210 | } 211 | } 212 | 213 | public void setWeekDayFormatter(WeekDayFormatter formatter) { 214 | this.weekDayFormatter = formatter; 215 | for (V pagerView : currentViews) { 216 | pagerView.setWeekDayFormatter(formatter); 217 | } 218 | } 219 | 220 | public void setDayFormatter(DayFormatter formatter) { 221 | this.dayFormatter = formatter; 222 | for (V pagerView : currentViews) { 223 | pagerView.setDayFormatter(formatter); 224 | } 225 | } 226 | 227 | @MaterialCalendarView.ShowOtherDates 228 | public int getShowOtherDates() { 229 | return showOtherDates; 230 | } 231 | 232 | public void setWeekDayTextAppearance(int taId) { 233 | if (taId == 0) { 234 | return; 235 | } 236 | this.weekDayTextAppearance = taId; 237 | for (V pagerView : currentViews) { 238 | pagerView.setWeekDayTextAppearance(taId); 239 | } 240 | } 241 | 242 | public void setRangeDates(CalendarDay min, CalendarDay max) { 243 | 244 | this.minDate = min; 245 | this.maxDate = max; 246 | for (V pagerView : currentViews) { 247 | pagerView.setMinimumDate(min); 248 | pagerView.setMaximumDate(max); 249 | } 250 | 251 | if (min == null) { 252 | min = CalendarDay.from(today.getYear() - 200, today.getMonth(), today.getDay()); 253 | } 254 | 255 | if (max == null) { 256 | max = CalendarDay.from(today.getYear() + 200, today.getMonth(), today.getDay()); 257 | } 258 | 259 | rangeIndex = createRangeIndex(min, max); 260 | 261 | notifyDataSetChanged(); 262 | invalidateSelectedDates(); 263 | } 264 | 265 | public DateRangeIndex getRangeIndex() { 266 | return rangeIndex; 267 | } 268 | 269 | public void clearSelections() { 270 | selectedDates.clear(); 271 | invalidateSelectedDates(); 272 | } 273 | 274 | public void setDateSelected(CalendarDay day, boolean selected) { 275 | if (selected) { 276 | // if (!selectedDates.contains(day)) { 277 | selectedDates.add(day); 278 | invalidateSelectedDates(); 279 | // } 280 | } else { 281 | if (selectedDates.contains(day)) { 282 | selectedDates.remove(day); 283 | invalidateSelectedDates(); 284 | } 285 | } 286 | } 287 | 288 | 289 | private void invalidateSelectedDates() { 290 | validateSelectedDates(); 291 | for (V pagerView : currentViews) { 292 | pagerView.setSelectedDates(selectedDates); 293 | } 294 | } 295 | 296 | private void validateSelectedDates() { 297 | for (int i = 0; i < selectedDates.size(); i++) { 298 | CalendarDay date = selectedDates.get(i); 299 | 300 | if ((minDate != null && minDate.isAfter(date)) || (maxDate != null && maxDate 301 | .isBefore(date))) { 302 | selectedDates.remove(i); 303 | mcv.onDateUnselected(date); 304 | i -= 1; 305 | } 306 | } 307 | } 308 | 309 | public CalendarDay getItem(int position) { 310 | return rangeIndex.getItem(position); 311 | } 312 | 313 | @NonNull 314 | public List getSelectedDates() { 315 | return Collections.unmodifiableList(selectedDates); 316 | } 317 | 318 | protected int getDateTextAppearance() { 319 | return dateTextAppearance == null ? 0 : dateTextAppearance; 320 | } 321 | 322 | protected int getWeekDayTextAppearance() { 323 | return weekDayTextAppearance == null ? 0 : weekDayTextAppearance; 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/CalendarUtils.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.annotation.Nullable; 5 | 6 | import java.util.Calendar; 7 | import java.util.Date; 8 | 9 | import static java.util.Calendar.DATE; 10 | import static java.util.Calendar.DAY_OF_WEEK; 11 | import static java.util.Calendar.MONTH; 12 | import static java.util.Calendar.YEAR; 13 | 14 | /** 15 | * Utilities for Calendar 16 | */ 17 | public class CalendarUtils { 18 | 19 | /** 20 | * @param date {@linkplain Date} to pull date information from 21 | * @return a new Calendar instance with the date set to the provided date. Time set to zero. 22 | */ 23 | public static Calendar getInstance(@Nullable Date date) { 24 | Calendar calendar = Calendar.getInstance(); 25 | if (date != null) { 26 | calendar.setTime(date); 27 | } 28 | copyDateTo(calendar, calendar); 29 | return calendar; 30 | } 31 | 32 | /** 33 | * @return a new Calendar instance with the date set to today. Time set to zero. 34 | */ 35 | @NonNull 36 | public static Calendar getInstance() { 37 | Calendar calendar = Calendar.getInstance(); 38 | copyDateTo(calendar, calendar); 39 | return calendar; 40 | } 41 | 42 | /** 43 | * Set the provided calendar to the first day of the month. Also clears all time information. 44 | * 45 | * @param calendar {@linkplain Calendar} to modify to be at the first fay of the month 46 | */ 47 | public static void setToFirstDay(Calendar calendar) { 48 | int year = getYear(calendar); 49 | int month = getMonth(calendar); 50 | calendar.clear(); 51 | calendar.set(year, month, 1); 52 | } 53 | 54 | /** 55 | * Copy only date information to a new calendar. 56 | * 57 | * @param from calendar to copy from 58 | * @param to calendar to copy to 59 | */ 60 | public static void copyDateTo(Calendar from, Calendar to) { 61 | int year = getYear(from); 62 | int month = getMonth(from); 63 | int day = getDay(from); 64 | to.clear(); 65 | to.set(year, month, day); 66 | } 67 | 68 | public static int getYear(Calendar calendar) { 69 | return calendar.get(YEAR); 70 | } 71 | 72 | public static int getMonth(Calendar calendar) { 73 | return calendar.get(MONTH); 74 | } 75 | 76 | public static int getDay(Calendar calendar) { 77 | return calendar.get(DATE); 78 | } 79 | 80 | public static int getDayOfWeek(Calendar calendar) { 81 | return calendar.get(DAY_OF_WEEK); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/CustomerStyle.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | /** 4 | * Created by Carry on 2018/1/22. 5 | */ 6 | 7 | public class CustomerStyle { 8 | public static String WEEKEND_COLOR; 9 | public static String WEEKDAY_COLOR; 10 | public static String CHECKED_COLOR; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/DateRangeIndex.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | /** 4 | * Use math to calculate first days of months by postion from a minium date 5 | */ 6 | interface DateRangeIndex { 7 | 8 | int getCount(); 9 | 10 | int indexOf(CalendarDay day); 11 | 12 | CalendarDay getItem(int position); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/DayViewDecorator.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | /** 4 | * Decorate Day views with drawables and text manipulation 5 | */ 6 | public interface DayViewDecorator { 7 | 8 | /** 9 | * Determine if a specific day should be decorated 10 | * 11 | * @param day {@linkplain CalendarDay} to possibly decorate 12 | * @return true if this decorator should be applied to the provided day 13 | */ 14 | boolean shouldDecorate(CalendarDay day); 15 | 16 | /** 17 | * Set decoration options onto a facade to be applied to all relevant days 18 | * 19 | * @param view View to decorate 20 | */ 21 | void decorate(DayViewFacade view); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/DayViewFacade.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.support.annotation.NonNull; 5 | 6 | import java.util.Collections; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | /** 11 | * Abstraction layer to help in decorating Day views 12 | */ 13 | public class DayViewFacade { 14 | 15 | private boolean isDecorated; 16 | 17 | private Drawable backgroundDrawable = null; 18 | private Drawable selectionDrawable = null; 19 | private final LinkedList spans = new LinkedList<>(); 20 | private boolean daysDisabled = false; 21 | 22 | DayViewFacade() { 23 | isDecorated = false; 24 | } 25 | 26 | /** 27 | * Set a drawable to draw behind everything else 28 | * 29 | * @param drawable Drawable to draw behind everything 30 | */ 31 | public void setBackgroundDrawable(@NonNull Drawable drawable) { 32 | if (drawable == null) { 33 | throw new IllegalArgumentException("Cannot be null"); 34 | } 35 | this.backgroundDrawable = drawable; 36 | isDecorated = true; 37 | } 38 | 39 | /** 40 | * Set a custom selection drawable 41 | * TODO: define states that can/should be used in StateListDrawables 42 | * 43 | * @param drawable the drawable for selection 44 | */ 45 | public void setSelectionDrawable(@NonNull Drawable drawable) { 46 | if (drawable == null) { 47 | throw new IllegalArgumentException("Cannot be null"); 48 | } 49 | selectionDrawable = drawable; 50 | isDecorated = true; 51 | } 52 | 53 | /** 54 | * Add a span to the entire text of a day 55 | * 56 | * @param span text span instance 57 | */ 58 | public void addSpan(@NonNull Object span) { 59 | if (spans != null) { 60 | this.spans.add(new Span(span)); 61 | isDecorated = true; 62 | } 63 | } 64 | 65 | /** 66 | *

Set days to be in a disabled state, or re-enabled.

67 | *

Note, passing true here will not override minimum and maximum dates, if set. 68 | * This will only re-enable disabled dates.

69 | * 70 | * @param daysDisabled true to disable days, false to re-enable days 71 | */ 72 | public void setDaysDisabled(boolean daysDisabled) { 73 | this.daysDisabled = daysDisabled; 74 | this.isDecorated = true; 75 | } 76 | 77 | void reset() { 78 | backgroundDrawable = null; 79 | selectionDrawable = null; 80 | spans.clear(); 81 | isDecorated = false; 82 | daysDisabled = false; 83 | } 84 | 85 | /** 86 | * Apply things set this to other 87 | * 88 | * @param other facade to apply our data to 89 | */ 90 | void applyTo(DayViewFacade other) { 91 | if (selectionDrawable != null) { 92 | other.setSelectionDrawable(selectionDrawable); 93 | } 94 | if (backgroundDrawable != null) { 95 | other.setBackgroundDrawable(backgroundDrawable); 96 | } 97 | other.spans.addAll(spans); 98 | other.isDecorated |= this.isDecorated; 99 | other.daysDisabled = daysDisabled; 100 | } 101 | 102 | boolean isDecorated() { 103 | return isDecorated; 104 | } 105 | 106 | Drawable getSelectionDrawable() { 107 | return selectionDrawable; 108 | } 109 | 110 | Drawable getBackgroundDrawable() { 111 | return backgroundDrawable; 112 | } 113 | 114 | List getSpans() { 115 | return Collections.unmodifiableList(spans); 116 | } 117 | 118 | /** 119 | * Are days from this facade disabled 120 | * 121 | * @return true if disabled, false if not re-enabled 122 | */ 123 | public boolean areDaysDisabled() { 124 | return daysDisabled; 125 | } 126 | 127 | static class Span { 128 | 129 | final Object span; 130 | 131 | public Span(Object span) { 132 | this.span = span; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/DecoratorResult.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | class DecoratorResult { 4 | public final DayViewDecorator decorator; 5 | public final DayViewFacade result; 6 | 7 | DecoratorResult(DayViewDecorator decorator, DayViewFacade result) { 8 | this.decorator = decorator; 9 | this.result = result; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/DirectionButton.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.content.Context; 4 | import android.graphics.PorterDuff; 5 | import android.os.Build; 6 | import android.util.TypedValue; 7 | import android.widget.ImageView; 8 | 9 | /** 10 | * An {@linkplain ImageView} to pragmatically set the color of arrows 11 | * using a {@linkplain android.graphics.ColorFilter} 12 | */ 13 | class DirectionButton extends ImageView { 14 | 15 | public DirectionButton(Context context) { 16 | super(context); 17 | 18 | setBackgroundResource(getThemeSelectableBackgroundId(context)); 19 | } 20 | 21 | public void setColor(int color) { 22 | setColorFilter(color, PorterDuff.Mode.SRC_ATOP); 23 | } 24 | 25 | @Override 26 | public void setEnabled(boolean enabled) { 27 | super.setEnabled(enabled); 28 | setAlpha(enabled ? 1f : 0.1f); 29 | } 30 | 31 | private static int getThemeSelectableBackgroundId(Context context) { 32 | //Get selectableItemBackgroundBorderless defined for AppCompat 33 | int colorAttr = context.getResources().getIdentifier( 34 | "selectableItemBackgroundBorderless", "attr", context.getPackageName()); 35 | 36 | if (colorAttr == 0) { 37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 38 | colorAttr = android.R.attr.selectableItemBackgroundBorderless; 39 | } else { 40 | colorAttr = android.R.attr.selectableItemBackground; 41 | } 42 | } 43 | 44 | TypedValue outValue = new TypedValue(); 45 | context.getTheme().resolveAttribute(colorAttr, outValue, true); 46 | return outValue.resourceId; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/Experimental.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 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 | * Inspired from https://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/annotations/Beta.java 17 | */ 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * Signifies that a public API (public class, method or field) is will almost certainly 27 | * be changed or removed in a future release. An API bearing this annotation should not 28 | * be used or relied upon in production code. APIs exposed with this annotation exist 29 | * to allow broad testing and feedback on experimental features. 30 | **/ 31 | @Retention(RetentionPolicy.CLASS) 32 | @Target({ 33 | ElementType.ANNOTATION_TYPE, 34 | ElementType.CONSTRUCTOR, 35 | ElementType.FIELD, 36 | ElementType.METHOD, 37 | ElementType.TYPE}) 38 | @Documented 39 | @Experimental 40 | public @interface Experimental { 41 | } -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/MonthPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.util.SparseArrayCompat; 5 | 6 | /** 7 | * Pager adapter backing the calendar view 8 | */ 9 | class MonthPagerAdapter extends CalendarPagerAdapter { 10 | 11 | MonthPagerAdapter(MaterialCalendarView mcv) { 12 | super(mcv); 13 | } 14 | 15 | @Override 16 | protected MonthView createView(int position) { 17 | return new MonthView(mcv, getItem(position), mcv.getFirstDayOfWeek()); 18 | } 19 | 20 | @Override 21 | protected int indexOf(MonthView view) { 22 | CalendarDay month = view.getMonth(); 23 | return getRangeIndex().indexOf(month); 24 | } 25 | 26 | @Override 27 | protected boolean isInstanceOfView(Object object) { 28 | return object instanceof MonthView; 29 | } 30 | 31 | @Override 32 | protected DateRangeIndex createRangeIndex(CalendarDay min, CalendarDay max) { 33 | return new Monthly(min, max); 34 | } 35 | 36 | public static class Monthly implements DateRangeIndex { 37 | 38 | private final CalendarDay min; 39 | private final int count; 40 | 41 | private SparseArrayCompat dayCache = new SparseArrayCompat<>(); 42 | 43 | public Monthly(@NonNull CalendarDay min, @NonNull CalendarDay max) { 44 | this.min = CalendarDay.from(min.getYear(), min.getMonth(), 1); 45 | max = CalendarDay.from(max.getYear(), max.getMonth(), 1); 46 | this.count = indexOf(max) + 1; 47 | } 48 | 49 | public int getCount() { 50 | return count; 51 | } 52 | 53 | public int indexOf(CalendarDay day) { 54 | int yDiff = day.getYear() - min.getYear(); 55 | int mDiff = day.getMonth() - min.getMonth(); 56 | 57 | return (yDiff * 12) + mDiff; 58 | } 59 | 60 | public CalendarDay getItem(int position) { 61 | 62 | CalendarDay re = dayCache.get(position); 63 | if (re != null) { 64 | return re; 65 | } 66 | 67 | int numY = position / 12; 68 | int numM = position % 12; 69 | 70 | int year = min.getYear() + numY; 71 | int month = min.getMonth() + numM; 72 | if (month >= 12) { 73 | year += 1; 74 | month -= 12; 75 | } 76 | 77 | re = CalendarDay.from(year, month, 1); 78 | dayCache.put(position, re); 79 | return re; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/MonthView.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.support.annotation.NonNull; 5 | 6 | import java.util.Calendar; 7 | import java.util.Collection; 8 | 9 | /** 10 | * Display a month of {@linkplain DayView}s and seven {@linkplain WeekDayView}s. 11 | */ 12 | @SuppressLint("ViewConstructor") 13 | class MonthView extends CalendarPagerView { 14 | 15 | public MonthView(@NonNull MaterialCalendarView view, CalendarDay month, int firstDayOfWeek) { 16 | super(view, month, firstDayOfWeek); 17 | } 18 | 19 | @Override 20 | protected void buildDayViews(Collection dayViews, Calendar calendar) { 21 | for (int r = 0; r < DEFAULT_MAX_WEEKS; r++) { 22 | for (int i = 0; i < DEFAULT_DAYS_IN_WEEK; i++) { 23 | addDayView(dayViews, calendar); 24 | } 25 | } 26 | } 27 | 28 | public CalendarDay getMonth() { 29 | return getFirstViewDay(); 30 | } 31 | 32 | @Override 33 | protected boolean isDayEnabled(CalendarDay day) { 34 | return day.getMonth() == getFirstViewDay().getMonth(); 35 | } 36 | 37 | @Override 38 | protected int getRows() { 39 | return DEFAULT_MAX_WEEKS + DAY_NAMES_ROW; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/OnDateSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | /** 6 | * The callback used to indicate a date has been selected or deselected 7 | */ 8 | public interface OnDateSelectedListener { 9 | 10 | /** 11 | * Called when a user clicks on a day. 12 | * There is no logic to prevent multiple calls for the same date and state. 13 | * 14 | * @param widget the view associated with this listener 15 | * @param date the date that was selected or unselected 16 | * @param selected true if the day is now selected, false otherwise 17 | */ 18 | void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean 19 | selected); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/OnMonthChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | /** 4 | * The callback used to indicate the user changes the displayed month 5 | */ 6 | public interface OnMonthChangedListener { 7 | 8 | /** 9 | * Called upon change of the selected day 10 | * 11 | * @param widget the view associated with this listener 12 | * @param date the month picked, as the first day of the month 13 | */ 14 | void onMonthChanged(MaterialCalendarView widget, CalendarDay date); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/OnRangeSelectedListener.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * The callback used to indicate a range has been selected 9 | */ 10 | public interface OnRangeSelectedListener { 11 | 12 | /** 13 | * Called when a user selects a range of days. 14 | * There is no logic to prevent multiple calls for the same date and state. 15 | * 16 | * @param widget the view associated with this listener 17 | * @param dates the dates in the range, in ascending order 18 | */ 19 | void onRangeSelected(@NonNull MaterialCalendarView widget, @NonNull List dates); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/TitleChanger.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.animation.Animator; 4 | import android.content.res.Resources; 5 | import android.text.TextUtils; 6 | import android.util.Log; 7 | import android.util.TypedValue; 8 | import android.view.ViewPropertyAnimator; 9 | import android.view.animation.DecelerateInterpolator; 10 | import android.view.animation.Interpolator; 11 | import android.widget.TextView; 12 | 13 | import com.benmu.widget.view.calendar.format.TitleFormatter; 14 | 15 | class TitleChanger { 16 | 17 | public static final int DEFAULT_ANIMATION_DELAY = 400; 18 | public static final int DEFAULT_Y_TRANSLATION_DP = 20; 19 | 20 | private final TextView title; 21 | private TitleFormatter titleFormatter; 22 | 23 | private final int animDelay; 24 | private final int animDuration; 25 | private final int translate; 26 | private final Interpolator interpolator = new DecelerateInterpolator(2f); 27 | 28 | private int orientation = MaterialCalendarView.VERTICAL; 29 | 30 | private long lastAnimTime = 0; 31 | private CalendarDay previousMonth = null; 32 | 33 | public TitleChanger(TextView title) { 34 | this.title = title; 35 | 36 | Resources res = title.getResources(); 37 | 38 | animDelay = DEFAULT_ANIMATION_DELAY; 39 | 40 | animDuration = res.getInteger(android.R.integer.config_shortAnimTime) / 2; 41 | 42 | translate = (int) TypedValue.applyDimension( 43 | TypedValue.COMPLEX_UNIT_DIP, DEFAULT_Y_TRANSLATION_DP, res.getDisplayMetrics() 44 | ); 45 | } 46 | 47 | public void change(final CalendarDay currentMonth) { 48 | long currentTime = System.currentTimeMillis(); 49 | 50 | if (currentMonth == null) { 51 | return; 52 | } 53 | 54 | if (TextUtils.isEmpty(title.getText()) || (currentTime - lastAnimTime) < animDelay) { 55 | doChange(currentTime, currentMonth, false); 56 | } 57 | 58 | if (currentMonth.equals(previousMonth) || 59 | (currentMonth.getMonth() == previousMonth.getMonth() 60 | && currentMonth.getYear() == previousMonth.getYear())) { 61 | return; 62 | } 63 | 64 | doChange(currentTime, currentMonth, true); 65 | } 66 | 67 | private void doChange(final long now, final CalendarDay currentMonth, boolean animate) { 68 | 69 | title.animate().cancel(); 70 | doTranslation(title, 0); 71 | 72 | title.setAlpha(1); 73 | lastAnimTime = now; 74 | 75 | final CharSequence newTitle = titleFormatter.format(currentMonth); 76 | Log.e("title","tilte>>>>>>"+newTitle); 77 | if (!animate) { 78 | title.setText(newTitle); 79 | } else { 80 | final int translation = translate * (previousMonth.isBefore(currentMonth) ? 1 : -1); 81 | final ViewPropertyAnimator viewPropertyAnimator = title.animate(); 82 | 83 | if (orientation == MaterialCalendarView.HORIZONTAL) { 84 | viewPropertyAnimator.translationX(translation * -1); 85 | } else { 86 | viewPropertyAnimator.translationY(translation * -1); 87 | } 88 | 89 | viewPropertyAnimator 90 | .alpha(0) 91 | .setDuration(animDuration) 92 | .setInterpolator(interpolator) 93 | .setListener(new AnimatorListener() { 94 | 95 | @Override 96 | public void onAnimationCancel(Animator animator) { 97 | doTranslation(title, 0); 98 | title.setAlpha(1); 99 | } 100 | 101 | @Override 102 | public void onAnimationEnd(Animator animator) { 103 | title.setText(newTitle); 104 | doTranslation(title, translation); 105 | 106 | final ViewPropertyAnimator viewPropertyAnimator = title.animate(); 107 | if (orientation == MaterialCalendarView.HORIZONTAL) { 108 | viewPropertyAnimator.translationX(0); 109 | } else { 110 | viewPropertyAnimator.translationY(0); 111 | } 112 | 113 | viewPropertyAnimator 114 | .alpha(1) 115 | .setDuration(animDuration) 116 | .setInterpolator(interpolator) 117 | .setListener(new AnimatorListener()) 118 | .start(); 119 | } 120 | }).start(); 121 | } 122 | 123 | previousMonth = currentMonth; 124 | } 125 | 126 | private void doTranslation(final TextView title, final int translate) { 127 | if (orientation == MaterialCalendarView.HORIZONTAL) { 128 | title.setTranslationX(translate); 129 | } else { 130 | title.setTranslationY(translate); 131 | } 132 | } 133 | 134 | public TitleFormatter getTitleFormatter() { 135 | return titleFormatter; 136 | } 137 | 138 | public void setTitleFormatter(TitleFormatter titleFormatter) { 139 | this.titleFormatter = titleFormatter; 140 | } 141 | 142 | public void setOrientation(int orientation) { 143 | this.orientation = orientation; 144 | } 145 | 146 | public int getOrientation() { 147 | return orientation; 148 | } 149 | 150 | public void setPreviousMonth(CalendarDay previousMonth) { 151 | this.previousMonth = previousMonth; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/WeekDayView.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.text.TextUtils; 7 | import android.view.Gravity; 8 | import android.widget.TextView; 9 | 10 | import com.benmu.widget.view.calendar.format.WeekDayFormatter; 11 | 12 | import java.util.Calendar; 13 | 14 | /** 15 | * Display a day of the week 16 | */ 17 | @Experimental 18 | @SuppressLint("ViewConstructor") 19 | class WeekDayView extends TextView { 20 | 21 | private WeekDayFormatter formatter = WeekDayFormatter.DEFAULT; 22 | private int dayOfWeek; 23 | 24 | public WeekDayView(Context context, int dayOfWeek) { 25 | super(context); 26 | 27 | setGravity(Gravity.CENTER); 28 | 29 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 30 | setTextAlignment(TEXT_ALIGNMENT_CENTER); 31 | } 32 | 33 | setDayOfWeek(dayOfWeek); 34 | } 35 | 36 | public void setWeekDayFormatter(WeekDayFormatter formatter) { 37 | this.formatter = formatter == null ? WeekDayFormatter.DEFAULT : formatter; 38 | setDayOfWeek(dayOfWeek); 39 | } 40 | 41 | public void setDayOfWeek(int dayOfWeek) { 42 | this.dayOfWeek = dayOfWeek; 43 | CharSequence week = formatter.format(dayOfWeek); 44 | if (!TextUtils.isEmpty(week) && week.length() > 1) 45 | setText(week.subSequence(0, 1)); 46 | } 47 | 48 | public void setDayOfWeek(Calendar calendar) { 49 | setDayOfWeek(CalendarUtils.getDayOfWeek(calendar)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/WeekPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | @Experimental 10 | public class WeekPagerAdapter extends CalendarPagerAdapter { 11 | 12 | public WeekPagerAdapter(MaterialCalendarView mcv) { 13 | super(mcv); 14 | } 15 | 16 | @Override 17 | protected WeekView createView(int position) { 18 | return new WeekView(mcv, getItem(position), mcv.getFirstDayOfWeek()); 19 | } 20 | 21 | @Override 22 | protected int indexOf(WeekView view) { 23 | CalendarDay week = view.getFirstViewDay(); 24 | return getRangeIndex().indexOf(week); 25 | } 26 | 27 | @Override 28 | protected boolean isInstanceOfView(Object object) { 29 | return object instanceof WeekView; 30 | } 31 | 32 | @Override 33 | protected DateRangeIndex createRangeIndex(CalendarDay min, CalendarDay max) { 34 | return new Weekly(min, max, mcv.getFirstDayOfWeek()); 35 | } 36 | 37 | public static class Weekly implements DateRangeIndex { 38 | 39 | private static final int DAYS_IN_WEEK = 7; 40 | private final CalendarDay min; 41 | private final int count; 42 | 43 | public Weekly(@NonNull CalendarDay min, @NonNull CalendarDay max, int firstDayOfWeek) { 44 | this.min = getFirstDayOfWeek(min, firstDayOfWeek); 45 | this.count = weekNumberDifference(this.min, max) + 1; 46 | } 47 | 48 | @Override 49 | public int getCount() { 50 | return count; 51 | } 52 | 53 | @Override 54 | public int indexOf(CalendarDay day) { 55 | return weekNumberDifference(min, day); 56 | } 57 | 58 | @Override 59 | public CalendarDay getItem(int position) { 60 | long minMillis = min.getDate().getTime(); 61 | long millisOffset = TimeUnit.MILLISECONDS.convert( 62 | position * DAYS_IN_WEEK, 63 | TimeUnit.DAYS); 64 | long positionMillis = minMillis + millisOffset; 65 | return CalendarDay.from(new Date(positionMillis)); 66 | } 67 | 68 | private int weekNumberDifference(@NonNull CalendarDay min, @NonNull CalendarDay max) { 69 | long millisDiff = max.getDate().getTime() - min.getDate().getTime(); 70 | 71 | int dstOffsetMax = max.getCalendar().get(Calendar.DST_OFFSET); 72 | int dstOffsetMin = min.getCalendar().get(Calendar.DST_OFFSET); 73 | 74 | long dayDiff = TimeUnit.DAYS.convert(millisDiff + dstOffsetMax - dstOffsetMin, 75 | TimeUnit.MILLISECONDS); 76 | return (int) (dayDiff / DAYS_IN_WEEK); 77 | } 78 | 79 | /* 80 | * Necessary because of how Calendar handles getting the first day of week internally. 81 | */ 82 | private CalendarDay getFirstDayOfWeek(@NonNull CalendarDay min, int wantedFirstDayOfWeek) { 83 | Calendar calendar = Calendar.getInstance(); 84 | min.copyTo(calendar); 85 | while (calendar.get(Calendar.DAY_OF_WEEK) != wantedFirstDayOfWeek) { 86 | calendar.add(Calendar.DAY_OF_WEEK, -1); 87 | } 88 | return CalendarDay.from(calendar); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/WeekView.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.support.annotation.NonNull; 5 | 6 | import java.util.Calendar; 7 | import java.util.Collection; 8 | 9 | /** 10 | * Display a week of {@linkplain DayView}s and 11 | * seven {@linkplain WeekDayView}s. 12 | */ 13 | @Experimental 14 | @SuppressLint("ViewConstructor") 15 | public class WeekView extends CalendarPagerView { 16 | 17 | public WeekView(@NonNull MaterialCalendarView view, 18 | CalendarDay firstViewDay, 19 | int firstDayOfWeek) { 20 | super(view, firstViewDay, firstDayOfWeek); 21 | } 22 | 23 | @Override 24 | protected void buildDayViews(Collection dayViews, Calendar calendar) { 25 | for (int i = 0; i < DEFAULT_DAYS_IN_WEEK; i++) { 26 | addDayView(dayViews, calendar); 27 | } 28 | } 29 | 30 | @Override 31 | protected boolean isDayEnabled(CalendarDay day) { 32 | return true; 33 | } 34 | 35 | @Override 36 | protected int getRows() { 37 | return DAY_NAMES_ROW + 1; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/ArrayWeekDayFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | /** 4 | * Use an array to supply week day labels 5 | */ 6 | public class ArrayWeekDayFormatter implements WeekDayFormatter { 7 | 8 | private final CharSequence[] weekDayLabels; 9 | 10 | /** 11 | * @param weekDayLabels an array of 7 labels, starting with Sunday 12 | */ 13 | public ArrayWeekDayFormatter(CharSequence[] weekDayLabels) { 14 | if (weekDayLabels == null) { 15 | throw new IllegalArgumentException("Cannot be null"); 16 | } 17 | if (weekDayLabels.length != 7) { 18 | throw new IllegalArgumentException("Array must contain exactly 7 elements"); 19 | } 20 | this.weekDayLabels = weekDayLabels; 21 | } 22 | 23 | /** 24 | * {@inheritDoc} 25 | */ 26 | @Override 27 | public CharSequence format(int dayOfWeek) { 28 | return weekDayLabels[dayOfWeek - 1]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/CalendarWeekDayFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | import com.benmu.widget.view.calendar.CalendarUtils; 4 | 5 | import java.util.Calendar; 6 | import java.util.Locale; 7 | 8 | /** 9 | * Use a {@linkplain Calendar} to get week day labels. 10 | * 11 | * @see Calendar#getDisplayName(int, int, Locale) 12 | */ 13 | public class CalendarWeekDayFormatter implements WeekDayFormatter { 14 | 15 | private final Calendar calendar; 16 | 17 | /** 18 | * Format with a specific calendar 19 | * 20 | * @param calendar Calendar to retrieve formatting information from 21 | */ 22 | public CalendarWeekDayFormatter(Calendar calendar) { 23 | // recompute all fields of the calendar based on current date 24 | // See "Getting and Setting Calendar Field Values" 25 | // in https://developer.android.com/reference/java/util/Calendar.html 26 | calendar.get(Calendar.DAY_OF_WEEK); // Any fields to get is OK to recompute all fields in the calendar. 27 | this.calendar = calendar; 28 | 29 | } 30 | 31 | /** 32 | * Format with a default calendar 33 | */ 34 | public CalendarWeekDayFormatter() { 35 | this(CalendarUtils.getInstance()); 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | @Override 42 | public CharSequence format(int dayOfWeek) { 43 | calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); 44 | return calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.ENGLISH); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/DateFormatDayFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.benmu.widget.view.calendar.CalendarDay; 6 | 7 | import java.text.DateFormat; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Locale; 10 | 11 | /** 12 | * Format using a {@linkplain DateFormat} instance. 13 | */ 14 | public class DateFormatDayFormatter implements DayFormatter { 15 | 16 | private final DateFormat dateFormat; 17 | 18 | /** 19 | * Format using a default format 20 | */ 21 | public DateFormatDayFormatter() { 22 | this.dateFormat = new SimpleDateFormat("d", Locale.getDefault()); 23 | } 24 | 25 | /** 26 | * Format using a specific {@linkplain DateFormat} 27 | * 28 | * @param format the format to use 29 | */ 30 | public DateFormatDayFormatter(@NonNull DateFormat format) { 31 | this.dateFormat = format; 32 | } 33 | 34 | /** 35 | * {@inheritDoc} 36 | */ 37 | @Override 38 | @NonNull 39 | public String format(@NonNull CalendarDay day) { 40 | return dateFormat.format(day.getDate()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/DateFormatTitleFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | import com.benmu.widget.view.calendar.CalendarDay; 4 | 5 | import java.text.DateFormat; 6 | import java.text.SimpleDateFormat; 7 | import java.util.Locale; 8 | 9 | /** 10 | * Format using a {@linkplain DateFormat} instance. 11 | */ 12 | public class DateFormatTitleFormatter implements TitleFormatter { 13 | 14 | private final DateFormat dateFormat; 15 | 16 | /** 17 | * Format using "LLLL yyyy" for formatting 18 | */ 19 | public DateFormatTitleFormatter() { 20 | this.dateFormat = new SimpleDateFormat( 21 | "yyyy年MM月", Locale.getDefault() 22 | ); 23 | } 24 | 25 | /** 26 | * Format using a specified {@linkplain DateFormat} 27 | * 28 | * @param format the format to use 29 | */ 30 | public DateFormatTitleFormatter(DateFormat format) { 31 | this.dateFormat = format; 32 | } 33 | 34 | /** 35 | * {@inheritDoc} 36 | */ 37 | @Override 38 | public CharSequence format(CalendarDay day) { 39 | return dateFormat.format(day.getDate()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/DayFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | 6 | import com.benmu.widget.view.calendar.CalendarDay; 7 | 8 | import java.text.SimpleDateFormat; 9 | 10 | /** 11 | * Supply labels for a given day. Default implementation is to format using a {@linkplain SimpleDateFormat} 12 | */ 13 | public interface DayFormatter { 14 | 15 | /** 16 | * Format a given day into a string 17 | * 18 | * @param day the day 19 | * @return a label for the day 20 | */ 21 | @NonNull 22 | String format(@NonNull CalendarDay day); 23 | 24 | /** 25 | * Default implementation used by {@linkplain com.prolificinteractive.materialcalendarview.MaterialCalendarView} 26 | */ 27 | public static final DayFormatter DEFAULT = new DateFormatDayFormatter(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/MonthArrayTitleFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | import android.text.SpannableStringBuilder; 4 | 5 | import com.benmu.widget.view.calendar.CalendarDay; 6 | 7 | /** 8 | * Use an array to generate a month/year label 9 | */ 10 | public class MonthArrayTitleFormatter implements TitleFormatter { 11 | 12 | private final CharSequence[] monthLabels; 13 | 14 | /** 15 | * Format using an array of month labels 16 | * 17 | * @param monthLabels an array of 12 labels to use for months, starting with January 18 | */ 19 | public MonthArrayTitleFormatter(CharSequence[] monthLabels) { 20 | if (monthLabels == null) { 21 | throw new IllegalArgumentException("Label array cannot be null"); 22 | } 23 | if (monthLabels.length < 12) { 24 | throw new IllegalArgumentException("Label array is too short"); 25 | } 26 | this.monthLabels = monthLabels; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | @Override 33 | public CharSequence format(CalendarDay day) { 34 | return new SpannableStringBuilder() 35 | .append(monthLabels[day.getMonth()]) 36 | .append(" ") 37 | .append(String.valueOf(day.getYear())); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/TitleFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | 4 | import com.benmu.widget.view.calendar.CalendarDay; 5 | 6 | /** 7 | * Used to format a {@linkplain com.prolificinteractive.materialcalendarview.CalendarDay} to a string for the month/year title 8 | */ 9 | public interface TitleFormatter { 10 | 11 | /** 12 | * Converts the supplied day to a suitable month/year title 13 | * 14 | * @param day the day containing relevant month and year information 15 | * @return a label to display for the given month/year 16 | */ 17 | CharSequence format(CalendarDay day); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/format/WeekDayFormatter.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.format; 2 | 3 | 4 | import com.benmu.widget.view.calendar.CalendarUtils; 5 | 6 | /** 7 | * Supply labels for a given day of the week 8 | */ 9 | public interface WeekDayFormatter { 10 | /** 11 | * Convert a given day of the week into a label 12 | * 13 | * @param dayOfWeek the day of the week as returned by {@linkplain java.util.Calendar#get(int)} for {@linkplain java.util.Calendar#DAY_OF_YEAR} 14 | * @return a label for the day of week 15 | */ 16 | CharSequence format(int dayOfWeek); 17 | 18 | /** 19 | * Default implementation used by {@linkplain com.prolificinteractive.materialcalendarview.MaterialCalendarView} 20 | */ 21 | public static final WeekDayFormatter DEFAULT = new CalendarWeekDayFormatter(CalendarUtils.getInstance()); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/calendar/spans/DotSpan.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.calendar.spans; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.text.style.LineBackgroundSpan; 6 | 7 | /** 8 | * Span to draw a dot centered under a section of text 9 | */ 10 | public class DotSpan implements LineBackgroundSpan { 11 | 12 | /** 13 | * Default radius used 14 | */ 15 | public static final float DEFAULT_RADIUS = 3; 16 | 17 | private final float radius; 18 | private final int color; 19 | 20 | /** 21 | * Create a span to draw a dot using default radius and color 22 | * 23 | * @see #DotSpan(float, int) 24 | * @see #DEFAULT_RADIUS 25 | */ 26 | public DotSpan() { 27 | this.radius = DEFAULT_RADIUS; 28 | this.color = 0; 29 | } 30 | 31 | /** 32 | * Create a span to draw a dot using a specified color 33 | * 34 | * @param color color of the dot 35 | * @see #DotSpan(float, int) 36 | * @see #DEFAULT_RADIUS 37 | */ 38 | public DotSpan(int color) { 39 | this.radius = DEFAULT_RADIUS; 40 | this.color = color; 41 | } 42 | 43 | /** 44 | * Create a span to draw a dot using a specified radius 45 | * 46 | * @param radius radius for the dot 47 | * @see #DotSpan(float, int) 48 | */ 49 | public DotSpan(float radius) { 50 | this.radius = radius; 51 | this.color = 0; 52 | } 53 | 54 | /** 55 | * Create a span to draw a dot using a specified radius and color 56 | * 57 | * @param radius radius for the dot 58 | * @param color color of the dot 59 | */ 60 | public DotSpan(float radius, int color) { 61 | this.radius = radius; 62 | this.color = color; 63 | } 64 | 65 | @Override 66 | public void drawBackground( 67 | Canvas canvas, Paint paint, 68 | int left, int right, int top, int baseline, int bottom, 69 | CharSequence charSequence, 70 | int start, int end, int lineNum 71 | ) { 72 | int oldColor = paint.getColor(); 73 | if (color != 0) { 74 | paint.setColor(color); 75 | } 76 | canvas.drawCircle((left + right) / 2, bottom + radius, radius, paint); 77 | paint.setColor(oldColor); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/benmu/widget/view/loading/LoadingDialog.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget.view.loading; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | import android.view.Gravity; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | import android.widget.LinearLayout; 12 | import android.widget.TextView; 13 | 14 | import com.benmu.widget.R; 15 | 16 | /** 17 | * Created by liuyuanxiao on 17/12/13. 18 | */ 19 | 20 | public class LoadingDialog { 21 | TextView tipTextView; 22 | Dialog loadingDialog; 23 | 24 | public Dialog createLoadingDialog(Context context, String msg) { 25 | LayoutInflater inflater = LayoutInflater.from(context); 26 | View v = inflater.inflate(R.layout.dialog_progress_layout, null);// 得到加载view 27 | tipTextView = (TextView) v.findViewById(R.id.tvMsg);// 提示文字 28 | tipTextView.setText(msg);// 设置加载信息 29 | 30 | loadingDialog = new Dialog(context, R.style.MyDialogStyle);// 创建自定义样式dialog 31 | loadingDialog.setCancelable(true); // 是否可以按“返回键”消失 32 | loadingDialog.setCanceledOnTouchOutside(false); // 点击加载框以外的区域 33 | loadingDialog.setContentView(v, new LinearLayout.LayoutParams( 34 | LinearLayout.LayoutParams.MATCH_PARENT, 35 | LinearLayout.LayoutParams.MATCH_PARENT));// 设置布局 36 | /** 37 | *将显示Dialog的方法封装在这里面 38 | */ 39 | Window window = loadingDialog.getWindow(); 40 | WindowManager.LayoutParams lp = window.getAttributes(); 41 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; 42 | lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 43 | window.setGravity(Gravity.CENTER); 44 | window.setAttributes(lp); 45 | window.setWindowAnimations(R.style.PopWindowAnimStyle); 46 | 47 | return loadingDialog; 48 | } 49 | 50 | public void setTipText(String msg) { 51 | if (TextUtils.isEmpty(msg)) { 52 | tipTextView.setVisibility(View.GONE); 53 | } else { 54 | tipTextView.setVisibility(View.VISIBLE); 55 | tipTextView.setText(msg); 56 | } 57 | } 58 | 59 | public void show() { 60 | if (loadingDialog.isShowing()) return; 61 | loadingDialog.show(); 62 | } 63 | 64 | public void dismiss() { 65 | if (loadingDialog.isShowing()) { 66 | loadingDialog.dismiss(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/res/anim/bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/res/anim/bottom_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /src/main/res/anim/dialog_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /src/main/res/anim/dialog_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /src/main/res/anim/svfade_in_center.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/res/anim/svfade_out_center.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/res/anim/svslide_in_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /src/main/res/anim/svslide_in_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /src/main/res/anim/svslide_out_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /src/main/res/anim/svslide_out_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /src/main/res/color/mcv_text_date_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/res/color/mcv_text_date_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_svstatus_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/ic_svstatus_error.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_svstatus_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/ic_svstatus_info.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_svstatus_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/ic_svstatus_loading.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_svstatus_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/ic_svstatus_success.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/icon_backnav.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/icon_backnav.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/loading_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/loading_icon.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/mcv_action_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/mcv_action_next.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/mcv_action_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmfe/BMWidget/ed14aef04e56b24cf51e1d3d89b3964c203ceadb/src/main/res/drawable-xhdpi/mcv_action_previous.png -------------------------------------------------------------------------------- /src/main/res/drawable/bg_black.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/res/drawable/bg_overlay_gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/res/drawable/bg_svprogresshuddefault.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/drawable/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/res/drawable/floatlayer_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/drawable/shape_round_rectange.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/res/layout/dialog_debug_error_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 18 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/res/layout/dialog_progress_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 18 | 25 | 26 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_animloading.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 25 | 26 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_custom_toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 19 | 20 | 31 | 32 | 43 | 44 | 45 | 46 | 53 | 54 | 65 | 66 | 67 | 68 | 74 | 75 | 82 | 83 | 96 | 97 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 27 | 28 | 32 | 33 | 34 | 44 | 45 | 52 | 53 | 54 | 55 | 62 | 63 | 68 | 69 | 73 | 74 | 84 | 85 | 89 | 90 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_floatlayer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_grid_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 25 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_grid_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 25 | 26 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_svprogresshud.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/res/layout/layout_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 21 | 22 | 33 | 34 | 35 | 36 | 41 | 42 | 50 | 51 | 52 | 53 | 54 | 59 | 60 | 69 | 70 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/main/res/layout/view_svprogressdefault.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 24 | 25 | 31 | 32 | 40 | 41 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/res/values/Integer.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 300 4 | -------------------------------------------------------------------------------- /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 | 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 | -------------------------------------------------------------------------------- /src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #66000000 7 | #f2f2f2 8 | #e6e6e6 9 | #46c4dd 10 | #ffff0c15 11 | #ff333333 12 | #FFdddddd 13 | #b0000000 14 | #e0e0e0 15 | #16d4b8 16 | #ffffffff 17 | #dddddd 18 | #00000000 19 | #ffffff 20 | #60000000 21 | #c0ffff00 22 | #666666 23 | #04b0ed 24 | #b2b2b2 25 | #999999 26 | #cccccc 27 | #d9d9d9 28 | #45c4dd 29 | #FF9900 30 | #6633B5E5 31 | #00d5b8 32 | #f38893 33 | #808080 34 | #4c4c4c 35 | #f5f5f5 36 | #fafafc 37 | #d2d3d9 38 | #8a8a99 39 | #00b4cb 40 | #eff3f4 41 | #777777 42 | #fafafa 43 | 44 | 45 | #60000000 46 | #95000000 47 | 48 | @android:color/white 49 | 50 | #000000 51 | #e5e5e5 52 | @android:color/black 53 | -------------------------------------------------------------------------------- /src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40 4 | 12dp 5 | 13dp 6 | 15dp 7 | 18dp 8 | 16dp 9 | 75dp 10 | 11 | 12 | 15dp 13 | 14 | 120dp 15 | 16 | 80dp 17 | 18 | 60dp 19 | 20 | 25px 21 | 22 | 15sp 23 | 10dp 24 | -------------------------------------------------------------------------------- /src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | bmWidgetLib 3 | Go to previous 4 | Go to next 5 | Calendar 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/values/style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 26 | 27 | 42 | 43 | 61 | 62 | 63 | 67 | 68 | 69 | 73 | 74 | 79 | 80 | 84 | 85 | 91 | 92 | 102 | -------------------------------------------------------------------------------- /src/test/java/com/benmu/widget/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.benmu.widget; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } --------------------------------------------------------------------------------