Commit ce60638ee9aa86f125b92b235422cbf851b45f17

Authored by YangShuai
1 parent deff69ec

1 新增switchButton

2 优化基类
Showing 23 changed files with 2185 additions and 393 deletions
app/strike/src/main/java/com/cnlive/strike/MainActivity.java
... ... @@ -9,7 +9,9 @@ import android.util.AttributeSet;
9 9 import android.util.Log;
10 10 import android.view.View;
11 11  
  12 +import com.cnlive.strike.demo.activity.DemoActivity;
12 13 import com.cnlive.strike.imgutil.ImgActivity;
  14 +import com.cnlive.strike.user.ui.activity.LoginActivity;
13 15  
14 16 import androidx.annotation.NonNull;
15 17 import androidx.annotation.Nullable;
... ... @@ -22,6 +24,7 @@ public class MainActivity extends AppCompatActivity {
22 24 protected void onCreate(@Nullable Bundle savedInstanceState) {
23 25 super.onCreate(savedInstanceState);
24 26 setContentView(R.layout.activity_main);
  27 + findViewById(R.id.go_to).setOnClickListener(view -> startActivity(new Intent(this, LoginActivity.class)) );
25 28 findViewById(R.id.to_img).setOnClickListener(view -> startActivity(new Intent(this, ImgActivity.class)) );
26 29 }
27 30  
... ...
build.gradle
... ... @@ -128,3 +128,9 @@ allprojects {
128 128 task clean(type: Delete) {
129 129 delete rootProject.buildDir
130 130 }
  131 +
  132 +//配置全局变量
  133 +ext {
  134 + libEmoj='2.0.9'
  135 + libMediaPicker = '1.5.8'
  136 +}
... ...
cloud/user/build.gradle
... ... @@ -46,7 +46,8 @@ dependencies {
46 46 androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
47 47  
48 48 implementation project(path: ':core:network')
49   -// implementation project(path: ':core:base')
  49 + implementation "com.cnlive.libs:emoj:$rootProject.libEmoj"
  50 + //图片选择器
  51 + implementation "com.cnlive.libs:mediapicker:$rootProject.libMediaPicker"
50 52  
51   - implementation 'com.cnlive.libs:emoj:2.0.9'
52 53 }
... ...
cloud/user/src/main/java/com/cnlive/strike/user/UserUtilAction.java
... ... @@ -9,15 +9,19 @@ import com.cnlive.core.network.BaseResult;
9 9 import com.cnlive.core.network.HttpConn;
10 10 import com.cnlive.strike.user.network.api.ApiBaseServiceOpen;
11 11 import com.cnlive.strike.user.network.api.BaseRequest;
  12 +import com.cnlive.strike.user.network.model.FaceData;
12 13 import com.cnlive.strike.user.network.model.TokenData;
13 14 import com.cnlive.strike.user.network.model.UserData;
14 15  
15 16 import java.io.File;
  17 +import java.util.ArrayList;
16 18 import java.util.HashMap;
  19 +import java.util.List;
17 20 import java.util.Map;
18 21  
19 22 import io.reactivex.Observable;
20 23 import okhttp3.MediaType;
  24 +import okhttp3.MultipartBody;
21 25 import okhttp3.RequestBody;
22 26  
23 27  
... ... @@ -239,35 +243,34 @@ public class UserUtilAction {
239 243 return BaseRequest.init().service(ApiBaseServiceOpen.class).accessToken(map);
240 244 }
241 245  
242   -// /**
243   -// * 上传头像
244   -// *
245   -// * @param context
246   -// * @param uid 用户ID
247   -// * @param face 头像文件
248   -// * @param token 平台TOKEN
249   -// * @param access_token
250   -// * @return
251   -// */
252   -// public static Disposable updateUserFace(Context context, String uid, File face, String token, String access_token, DataCallback<BaseInfo<FaceData>> callback) {
253   -// Map<String, RequestBody> params = new HashMap<>();
254   -//
255   -// if (TextUtils.isEmpty(token)) {
256   -// callback.callback(TOKEN_ERROR_CODE, TOKEN_ERROR_MSG, null);
257   -// }
258   -//
259   -// params.put("uid", getRequestString(uid));
260   -// params.put("clientPlat", getRequestString("a"));
261   -// params.put("token", getRequestString(token));
262   -// params.put("access_token", getRequestString(access_token));
263   -//
264   -// RequestBody requestFace = RequestBody.create(MediaType.parse("multipart/form-data"), face);
265   -// MultipartBody.Part faceBody = MultipartBody.Part.createFormData("face", face.getName(), requestFace);
266   -//
267   -// List<MultipartBody.Part> parts = new ArrayList<>();
268   -// parts.add(faceBody);
269   -//
270   -//
  246 + /**
  247 + * 上传头像
  248 + *
  249 + * @param context
  250 + * @param uid 用户ID
  251 + * @param face 头像文件
  252 + * @param token 平台TOKEN
  253 + * @param access_token
  254 + * @return
  255 + */
  256 + public static Observable<BaseResult<FaceData>> updateUserFace(Context context, String uid, File face, String token, String access_token) {
  257 + Map<String, RequestBody> params = new HashMap<>();
  258 +
  259 + if (TextUtils.isEmpty(token)) {
  260 + HttpConn.error(TOKEN_ERROR_CODE, TOKEN_ERROR_MSG);
  261 + }
  262 + params.put("uid", getRequestString(uid));
  263 + params.put("clientPlat", getRequestString("a"));
  264 + params.put("token", getRequestString(token));
  265 + params.put("access_token", getRequestString(access_token));
  266 +
  267 + RequestBody requestFace = RequestBody.create(MediaType.parse("multipart/form-data"), face);
  268 + MultipartBody.Part faceBody = MultipartBody.Part.createFormData("face", face.getName(), requestFace);
  269 +
  270 + List<MultipartBody.Part> parts = new ArrayList<>();
  271 + parts.add(faceBody);
  272 +
  273 +
271 274 // Subscriber<BaseInfo<FaceData>> subscriber = new Subscriber<BaseInfo<FaceData>>(context, ApiBaseSubscriber.subscriberBuild) {
272 275 // @Override
273 276 // public void onCompleted(String s, String s1, BaseInfo<FaceData> faceDataBaseInfo) {
... ... @@ -287,10 +290,11 @@ public class UserUtilAction {
287 290 // }
288 291 // }
289 292 // };
290   -//
291   -// return ApiBaseRequest.subscribe(ApiBaseRequest.service(ApiBaseServiceOpen.class).updateUserFace(params, parts), subscriber);
292   -//// return ApiBaseRequest.service(ApiBaseServiceOpen.class, service -> service.updateUserFace(params, parts)).subscribe(context, callback);
293   -// }
  293 +
  294 + return BaseRequest.init().service(ApiBaseServiceOpen.class).updateUserFace(params, parts);
  295 +// ApiBaseRequest.subscribe(ApiBaseRequest.service(ApiBaseServiceOpen.class).updateUserFace(params, parts), subscriber);
  296 +// return ApiBaseRequest.service(ApiBaseServiceOpen.class, service -> service.updateUserFace(params, parts)).subscribe(context, callback);
  297 + }
294 298  
295 299 private static RequestBody getRequestString(String value) {
296 300 RequestBody requestString = RequestBody.create(MediaType.parse("text/plain"), value);
... ...
cloud/user/src/main/java/com/cnlive/strike/user/frame/data/LoginData.java deleted 100644 → 0
1   -package com.cnlive.strike.user.frame.data;
2   -
3   -import androidx.databinding.BaseObservable;
4   -import androidx.databinding.Bindable;
5   -
6   -import com.cnlive.core.network.HttpConn;
7   -import com.cnlive.core.network.NetCallBack;
8   -import com.cnlive.strike.user.BR;
9   -
10   -import static android.view.View.GONE;
11   -import static android.view.View.VISIBLE;
12   -
13   -/**
14   - * Created by xiansong on 2018/1/2.
15   - */
16   -@Deprecated
17   -public class LoginData extends BaseObservable implements NetCallBack {
18   - private String name = "";
19   - private String password = "";
20   - private int loadVisibility = GONE;
21   -
22   - @Bindable
23   - public String getName() {
24   - return name;
25   - }
26   -
27   - public void setName(String name) {
28   - this.name = name;
29   - notifyPropertyChanged(BR.name);
30   - }
31   -
32   - @Bindable
33   - public String getPassword() {
34   - return password;
35   - }
36   -
37   - public void setPassword(String password) {
38   - this.password = password;
39   - notifyPropertyChanged(BR.password);
40   - }
41   -
42   - @Bindable
43   - public int getLoadVisibility() {
44   - return loadVisibility;
45   - }
46   -
47   - public void setLoadVisibility(int loadVisibility) {
48   - this.loadVisibility = loadVisibility;
49   - notifyPropertyChanged(BR.loadVisibility);
50   - }
51   -
52   -// @Override
53   -// public void onState(int state) {
54   -// loadVisibility = state == Config.STATE_LOAD ? VISIBLE : GONE;
55   -// }
56   -
57   - @Override
58   - public void onRequestState(int state) {
59   - loadVisibility = state == HttpConn.ON_START ? VISIBLE : GONE;
60   - }
61   -
62   - @Override
63   - public void onSuccess(Object data) {
64   -
65   - }
66   -
67   - @Override
68   - public void onFailure(String errCode, String errMsg) {
69   -
70   - }
71   -}
cloud/user/src/main/java/com/cnlive/strike/user/frame/presenter/FirstLoginPresenter.java
... ... @@ -11,11 +11,15 @@ import com.cnlive.strike.user.R;
11 11 import com.cnlive.strike.user.UserUtilAction;
12 12 import com.cnlive.strike.user.auth.UserService;
13 13 import com.cnlive.strike.user.frame.view.FirstLoginView;
  14 +import com.cnlive.strike.user.network.model.FaceData;
  15 +import com.cnlive.strike.user.network.model.TokenData;
14 16 import com.cnlive.strike.user.network.model.UserData;
  17 +import com.cnlive.strike.user.router.user.UserOfflineUtil;
15 18  
16 19 import java.io.File;
17 20  
18 21 import io.reactivex.ObservableSource;
  22 +import io.reactivex.disposables.Disposable;
19 23 import io.reactivex.functions.Function;
20 24  
21 25  
... ... @@ -24,6 +28,7 @@ import io.reactivex.functions.Function;
24 28 */
25 29  
26 30 public class FirstLoginPresenter extends BasePresenter<FirstLoginView> {
  31 + private Disposable updateFaceEvent;
27 32  
28 33 public void loadUserInfo(Context context) {
29 34 UserService userService = UserService.getInstance(context);
... ... @@ -112,52 +117,60 @@ public class FirstLoginPresenter extends BasePresenter&lt;FirstLoginView&gt; {
112 117 );
113 118 }
114 119  
115   -// public void uploadFile(Context context, String filePath) {
116   -// if (updateFaceEvent != null) updateFaceEvent.cencel();
117   -// if (getView() == null) return;
118   -// getView().showProgress(R.string.upload_image_dialog);
119   -// File file = new File(filePath);
120   -// UserService userService = UserService.getInstance(context);
121   -// if (file.exists()) {
122   -//
123   -// updateFaceEvent = Logic.create()
124   -// .action(UserUtilAction.accessToken(context))
125   -// .action((BaseInfo<TokenData> data, DataCallback<BaseInfo<FaceData>> callback) -> {
126   -// UserUtilAction.updateUserFace(context, UserService.getUid(context), file, userService.getAppAccount().getToken(), data.getData().getAccess_token(), callback);
127   -// return null;
128   -// })
129   -// .<BaseInfo<FaceData>>event()
130   -// .setStateCallback(state -> {
131   -// if (getView() == null) return;
132   -// if (state == STATE_LOAD) {
133   -//
134   -// } else if (state == STATE_IDLE) {
135   -// getView().hideProgress();
136   -// }
137   -// })
138   -// .setFailureCallback((state, message) -> {
139   -// if (getView() == null) return;
140   -// if (state == 8 || state == 9) {
141   -//// UserOfflineUtil.userInvalid(context);
142   -// HelperUtil.userInvalid(context);
143   -// } else if (state == UserUtilAction.TOKEN_ERROR_CODE) {
144   -// getView().showTokenErrorDialog();
145   -// } else {
146   -// getView().showToast(message);
147   -// }
148   -// })
149   -// .setSuccessCallback(data -> {
150   -// HelperUtil.modifyUserImage(context, data.getData().getFaceUrl());
151   -//// LoginManager.modifyUserImage( UserService.getUid(context), data.getData().getFaceUrl());
152   -// userService.setUserFaceUrl(data.getData().getFaceUrl(), data.getData().getBigFaceUrl());
153   -// if (getView() == null) return;
154   -// getView().photoChange(data.getData().getFaceUrl());
155   -// })
156   -// .start();
157   -// } else {
158   -// if (getView() != null) getView().showToast("文件不存在");
159   -// }
160   -// }
  120 + public void uploadFile(Context context, String filePath) {
  121 + if (updateFaceEvent != null) {
  122 + updateFaceEvent.dispose();
  123 + updateFaceEvent = null;
  124 + }
  125 + ifViewAttached(view -> view.showProgress(R.string.upload_image_dialog));
  126 + File file = new File(filePath);
  127 + UserService userService = UserService.getInstance(context);
  128 + if (file.exists()) {
  129 + updateFaceEvent =
  130 + HttpConn.action(
  131 + UserUtilAction.accessToken(context)
  132 + .concatMap(new Function<BaseResult<TokenData>, ObservableSource<BaseResult<FaceData>>>() {
  133 + @Override
  134 + public ObservableSource<BaseResult<FaceData>> apply(BaseResult<TokenData> tokenDataBaseResult) throws Exception {
  135 + if (tokenDataBaseResult.isSuccess()) {
  136 + return UserUtilAction.updateUserFace(context, UserService.getUid(context), file, userService.getAppAccount().getToken(), tokenDataBaseResult.getData().getAccess_token());
  137 + }
  138 + return HttpConn.error(tokenDataBaseResult);
  139 + }
  140 + }), new NetCallBack<BaseResult<FaceData>>() {
  141 + @Override
  142 + public void onRequestState(int state) {
  143 + if (HttpConn.ON_END == state) {
  144 + ifViewAttached(view -> view.hideProgress());
  145 + }
  146 + }
  147 +
  148 + @Override
  149 + public void onSuccess(BaseResult<FaceData> data) {
  150 +// HelperUtil.modifyUserImage(context, data.getData().getFaceUrl());
  151 +// LoginManager.modifyUserImage( UserService.getUid(context), data.getData().getFaceUrl());
  152 + userService.setUserFaceUrl(data.getData().getFaceUrl(), data.getData().getBigFaceUrl());
  153 + ifViewAttached(view -> view.photoChange(data.getData().getFaceUrl()));
  154 + }
  155 +
  156 + @Override
  157 + public void onFailure(String errCode, String errMsg) {
  158 + if ("8".equals(errCode) || "9".equals(errCode)) {
  159 + UserOfflineUtil.userInvalid(context);
  160 +// HelperUtil.userInvalid(context);
  161 + } else if (UserUtilAction.TOKEN_ERROR_CODE.equals(errCode)) {
  162 + ifViewAttached(view -> view.showTokenErrorDialog());
  163 + } else {
  164 + ifViewAttached(view -> view.showToast(errMsg));
  165 + }
  166 + }
  167 + }
  168 + );
  169 +
  170 + } else {
  171 + ifViewAttached(view -> view.showToast("文件不存在"));
  172 + }
  173 + }
161 174  
162 175 }
163 176  
... ...
cloud/user/src/main/java/com/cnlive/strike/user/frame/presenter/QuickLoginPresenter.java
... ... @@ -39,19 +39,18 @@ import io.reactivex.disposables.Disposable;
39 39 public class QuickLoginPresenter extends BasePresenter<QuickLoginView> {
40 40 private TimerUtil timerUtil; //计时工具类
41 41  
42   - // private Event<CNLiveUserInfo> event;
  42 + private Disposable event;
43 43 private Disposable sendMsgBefQuickLogin;
44 44 // private Event otherEvent;
45 45  
46 46 public void deleteUserInfo(Context context, String userName) {
47 47 UserService.getInstance(context).removeUser(userName, future -> {
48   - if (getView() == null) return;
49 48 //获取已登录用户列表
50 49 Account[] accounts = UserService.getInstance(context).getUserList();
51 50 //倒序输出最近登录用户
52 51  
53 52 Collections.reverse(Arrays.asList(accounts));
54   - getView().uploadListData(accounts);
  53 + ifViewAttached(view -> view.uploadListData(accounts));
55 54 });
56 55 }
57 56  
... ... @@ -67,10 +66,10 @@ public class QuickLoginPresenter extends BasePresenter&lt;QuickLoginView&gt; {
67 66 String name = account == null ? "" : account.name;
68 67 //页面填充数据
69 68 if (!TextUtils.isEmpty(name)) {
70   - getView().uploadInputData(name);
  69 + ifViewAttached(view -> view.uploadInputData(name));
71 70 }
72 71 if (null != accounts) {
73   - getView().uploadListData(accounts);
  72 + ifViewAttached(view -> view.uploadListData(accounts));
74 73 }
75 74 }
76 75  
... ... @@ -84,29 +83,28 @@ public class QuickLoginPresenter extends BasePresenter&lt;QuickLoginView&gt; {
84 83 timerUtil.setTimerLiener(new TimerUtil.TimeListener() {
85 84 @Override
86 85 public void onFinish() {
87   - if (getView() != null) getView().onTimerFinish();
  86 + ifViewAttached(view -> view.onTimerFinish());
88 87 }
89 88  
90 89 @Override
91 90 public void onInterval(long remainTime) {
92   - if (getView() != null) getView().onTimerInterval(remainTime / 1000);
  91 + ifViewAttached(view -> view.onTimerInterval(remainTime / 1000));
93 92 }
94 93 });
95 94 }
96 95  
97 96 public void sendCode(Context context, String phone, String countryCode) {
98   - if (getView() == null) return;
99 97 if (TimeEvent.debounce(2_000)) return;
100 98 if (TextUtils.isEmpty(phone)) {
101   - getView().showToast(context.getString(R.string.user_toast_quick_login_empty_phone));
  99 + ifViewAttached(view -> view.showToast(context.getString(R.string.user_toast_quick_login_empty_phone)));
102 100 return;
103 101 }
104 102 if ("86".equals(countryCode) && !RegexUtils.isMobileSimple(phone)) {
105   - getView().showToast(context.getString(R.string.user_toast_quick_login_error_phone));
  103 + ifViewAttached(view -> view.showToast(context.getString(R.string.user_toast_quick_login_error_phone)));
106 104 return;
107 105 }
108 106 if (sendMsgBefQuickLogin != null && !sendMsgBefQuickLogin.isDisposed()) {
109   - getView().showToast("处理中请稍后...");
  107 + ifViewAttached(view -> view.showToast("处理中请稍后..."));
110 108 } else {
111 109 sendMsgBefQuickLogin = HttpConn.action(UserUtilAction.sendMsgBefQuickLoginAction(context, phone, countryCode), new NetCallBack<BaseResult>() {
112 110 @Override
... ... @@ -137,7 +135,7 @@ public class QuickLoginPresenter extends BasePresenter&lt;QuickLoginView&gt; {
137 135 } else {
138 136 context.isLogin = true;
139 137 // UserAction.mobileLogin(context.getActivity(), data.getPhone(), data.getCode(), countryCode, initLoginEvent(context, true));
140   - UserAction.mobileLogin(context.getActivity(), data.getPhone(), data.getCode(), countryCode, new UserAction.CallBack() {
  138 + event = UserAction.mobileLogin(context.getActivity(), data.getPhone(), data.getCode(), countryCode, new UserAction.CallBack() {
141 139 @Override
142 140 public void callback(int state, String msg, Object object) {
143 141 initLoginEvent(context, state, msg, true, null);
... ... @@ -229,7 +227,7 @@ public class QuickLoginPresenter extends BasePresenter&lt;QuickLoginView&gt; {
229 227 // }
230 228  
231 229  
232   -//
  230 + //
233 231 // private Event<CNLiveUserInfo> initLoginEvent(QuickLoginFragment context, Boolean retry, OtherUserInfo otherUserInfo) {
234 232 // return event = new Event<CNLiveUserInfo>()
235 233 // .setStateCallback(state -> {
... ... @@ -304,5 +302,12 @@ public class QuickLoginPresenter extends BasePresenter&lt;QuickLoginView&gt; {
304 302 // if (context != null) context.isLogin = false;
305 303 // });
306 304 // }
307   -
  305 + public void cancelLoad() {
  306 + if (event != null) {
  307 + if (!event.isDisposed()) {
  308 + event.dispose();
  309 + }
  310 + event = null;
  311 + }
  312 + }
308 313 }
... ...
cloud/user/src/main/java/com/cnlive/strike/user/router/user/UserAction.java
... ... @@ -19,6 +19,7 @@ import com.cnlive.strike.user.network.model.UserData;
19 19  
20 20 import io.reactivex.Observable;
21 21 import io.reactivex.ObservableSource;
  22 +import io.reactivex.disposables.Disposable;
22 23 import io.reactivex.functions.Function;
23 24  
24 25  
... ... @@ -49,13 +50,13 @@ public class UserAction {
49 50 // .start(event);
50 51 // }
51 52  
52   - public static void mobileLogin(Context context, String phone, String code, String countryCode, CallBack callBack) {
  53 + public static Disposable mobileLogin(Context context, String phone, String code, String countryCode, CallBack callBack) {
53 54 // Logic.create()
54 55 // .action(UserUtilAction.quickLoginAction(context, phone, code, countryCode))
55 56 // .action(loginAppAction(context, phone, code))
56 57 //// .action(loginIMAction())
57 58 // .start(event);
58   - HttpConn.action(
  59 + return HttpConn.action(
59 60 UserUtilAction.quickLoginAction(context, phone, code, countryCode)
60 61 , new NetCallBack<BaseResult<UserData>>() {
61 62 @Override
... ... @@ -86,7 +87,7 @@ public class UserAction {
86 87 if (null != callBack) {
87 88 callBack.callback(-5, "获取账户信息失败 请重试.", null);
88 89 }
89   - } else if (TextUtils.isEmpty(data.getData().getMobile())) {
  90 + } else if (TextUtils.isEmpty(data.getData().getMobile())) {
90 91 callBack.callback(-5, "绑定手机号已失效..", null);
91 92 } else {
92 93 //更新登录用户信息
... ...
cloud/user/src/main/java/com/cnlive/strike/user/router/user/UserOfflineUtil.java
... ... @@ -5,9 +5,14 @@ import android.content.Context;
5 5  
6 6 import androidx.appcompat.app.AppCompatActivity;
7 7  
  8 +import com.cnlive.core.libs.base.ui.widget.BottomSheet;
8 9 import com.cnlive.core.libs.base.util.AlertUtil;
  10 +import com.cnlive.core.libs.base.util.AppManager;
9 11 import com.cnlive.strike.user.auth.UserService;
  12 +import com.cnlive.strike.user.ui.activity.LoginActivity;
10 13 import com.kongzue.dialog.interfaces.OnMenuItemClickListener;
  14 +import com.kongzue.dialog.util.DialogSettings;
  15 +import com.kongzue.dialog.util.TextInfo;
11 16 import com.kongzue.dialog.v3.BottomMenu;
12 17  
13 18 import org.greenrobot.eventbus.EventBus;
... ... @@ -37,40 +42,44 @@ public class UserOfflineUtil {
37 42 }
38 43  
39 44 public static void userInvalid(Context context) {
40   - AlertUtil.showDeftToast(context, "用户 已失效.");
  45 + AlertUtil.showDeftToast(context, "用户已失效.");
41 46 appLogout(context);
42 47 }
43 48  
44 49 //todo BottomSheet
45 50 public static void logout(Context context) {
46   -
47   - BottomMenu.show((AppCompatActivity) context, new String[]{"菜单1", "菜单2", "菜单3"}, new OnMenuItemClickListener() {
48   - @Override
49   - public void onClick(String text, int index) {
50   - //返回参数 text 即菜单名称,index 即菜单索引
51   - }
52   - });
53   -// new BottomSheet.BottomListSheetBuilder(context)
54   -// .addItem("退出登录", "", true)
55   -// .addItem("取消")
56   -// .setOnSheetItemClickListener((dialog, itemView, position, tag) -> {
57   -// switch (position) {
58   -// case 0:
59   -// appLogout(context);
60   -// break;
61   -// }
62   -// dialog.dismiss(false);
63   -// }).build().show();
  51 + DialogSettings.style = (DialogSettings.STYLE.STYLE_IOS);
  52 + TextInfo textInfo = new TextInfo();
  53 + textInfo.setFontSize(15);
  54 + DialogSettings.menuTextInfo = textInfo;
  55 +// BottomMenu.show((AppCompatActivity) context, new String[]{"退出登录"}, new OnMenuItemClickListener() {
  56 +// @Override
  57 +// public void onClick(String text, int index) {
  58 +// //返回参数 text 即菜单名称,index 即菜单索引
  59 +// }
  60 +// }).setCancelable(false);
  61 + new BottomSheet.BottomListSheetBuilder(context)
  62 + .addItem("退出登录", "", true)
  63 + .addItem("取消")
  64 + .setOnSheetItemClickListener((dialog, itemView, position, tag) -> {
  65 + switch (position) {
  66 + case 0:
  67 + appLogout(context);
  68 + break;
  69 + }
  70 + dialog.dismiss(false);
  71 + }).build().show();
64 72 }
65 73  
66 74 public static void appLogout(Context context) {
67 75 String uid = UserService.getUid(context);
68 76 UserService.getInstance(context).logoutAppAccount();
69 77 //退出登录
70   -
  78 + AppManager.getInstance().killAllActivity();
  79 + LoginActivity.start(context);
71 80 // LoginManager.logout(uid, null, context);
72 81 // if (ActivityLifecycle.isActive()) {
73   -// LoginActivity.start(context);
  82 +
74 83 // } else {
75 84 // Log.e("FinishAllActivity", "FinishAllActivity-----logout");
76 85 // FinishAllActivity.finish(context);
... ...
cloud/user/src/main/java/com/cnlive/strike/user/ui/activity/LoginActivity.java
... ... @@ -19,8 +19,11 @@ import com.cnlive.strike.user.ui.fragment.QuickLoginFragment;
19 19 * @author Lynn
20 20 * @date 2017/12/22
21 21 */
22   -@Route(path = "/cnmain/LoginActivity")
  22 +//@Route(path = "/cnmain/LoginActivity")
  23 +@Route(path = LoginActivity.PATH)
23 24 public class LoginActivity extends BaseActivity {
  25 + public static final String PATH = "/cloud/user/ui/activity/login";
  26 +
24 27 public static String FROM_TOKEN_ERROR = "from_token_error";
25 28 public static String TAG_MSG = "tagMsg";
26 29 public static String TAG_SHARE = "tagValue";
... ...
cloud/user/src/main/java/com/cnlive/strike/user/ui/fragment/FirstLoginFragment.java
... ... @@ -5,6 +5,7 @@ import android.app.Activity;
5 5 import android.app.Dialog;
6 6 import android.app.ProgressDialog;
7 7 import android.content.Intent;
  8 +import android.os.Environment;
8 9 import android.text.Editable;
9 10 import android.text.TextUtils;
10 11 import android.text.TextWatcher;
... ... @@ -14,9 +15,13 @@ import androidx.annotation.NonNull;
14 15 import androidx.appcompat.app.AppCompatActivity;
15 16  
16 17 import com.cnlive.core.libs.base.frame.fragment.BaseFragment;
  18 +import com.cnlive.core.libs.base.interfaces.OnPermissionResponseListener;
17 19 import com.cnlive.core.libs.base.util.AlertUtil;
18 20 import com.cnlive.core.libs.base.util.GlideUtil;
19 21 import com.cnlive.core.libs.base.util.StatusBarConfig;
  22 +import com.cnlive.media.picker.ImagePicker;
  23 +import com.cnlive.media.picker.PickerConfig;
  24 +import com.cnlive.media.picker.entity.Media;
20 25 import com.cnlive.strike.user.R;
21 26 import com.cnlive.strike.user.auth.UserService;
22 27 import com.cnlive.strike.user.databinding.FragmentFirstLoginBinding;
... ... @@ -28,6 +33,10 @@ import com.cnlive.strike.user.router.user.UserOfflineUtil;
28 33 import com.cnlive.strike.user.ui.activity.FirstLoginActivity;
29 34 import com.cnlive.strike.user.ui.activity.LoginActivity;
30 35  
  36 +import java.util.List;
  37 +
  38 +import static com.cnlive.media.picker.ui.PickerActivity.REQUEST_CODE_PERMISSION_CAMERA;
  39 +
31 40  
32 41 /**
33 42 * Created by xiansong on 2018/1/19.
... ... @@ -128,31 +137,37 @@ public class FirstLoginFragment extends BaseFragment&lt;FirstLoginView, FirstLoginP
128 137  
129 138  
130 139 public void selectPhoto(boolean fromResult) {
131   -// boolean hasPermission = fromResult ? AppUtil.checkPermissions(getActivity(), PERMISSION) : PermissionChecker.checkPermissions(getActivity()
132   -// , PERMISSION, REQUEST_CODE_PERMISSION_CAMERA, com.cnlive.media.picker.R.string.dialog_imagepicker_permission_sdcard_message);
133   -//
134   -// if (hasPermission) {
135   -// ImagePicker imagePicker = new ImagePicker.Builder()
136   -// //最大选择图片数目,默认40张
137   -// .maxNum(1)
138   -// .setSelectGif(false)
139   -// //是否需要相机(默认false)
140   -// .needCamera(true)
141   -// //选择模式(默认PICKER_IMAGE_VIDEO) PICKER_IMAGE-->图片 PICKER_VIDEO-->视频 PICKER_IMAGE_VIDEO-->图片&视频
142   -// .selectMode(PickerConfig.PICKER_IMAGE)
143   -// //拍照或录像存储位置 默认Environment.getExternalStorageDirectory().getPath() + File.separator + "JCamera"
144   -// .cachePath(Environment.getExternalStorageDirectory() + "/strike/file/")
145   -// //是否为发朋友圈模式(默认false) true-->图片与视频不能同时选择
146   -// .isFriendCircle(false)
147   -// //裁剪 只有在单选模式下才可以使用
148   -// .doCrop(1, 1, 300, 300)
149   -// //是否开启单选模式(默认false)
150   -// .isSinglePick(true)
151   -// .builder();
152   -//
153   -// //跳转到图片选择页面 activity 请求码 结果码
154   -// imagePicker.start(getActivity(), PickerConfig.PICKER_IMAGE, PickerConfig.DEFAULT_RESULT_CODE);
155   -// }
  140 + requestPermission(REQUEST_CODE_PERMISSION_CAMERA,
  141 + R.string.dialog_imagepicker_permission_sdcard_message,
  142 + PERMISSION, new OnPermissionResponseListener() {
  143 + @Override
  144 + public void onSuccess(String[] permissions) {
  145 + ImagePicker imagePicker = new ImagePicker.Builder()
  146 + //最大选择图片数目,默认40张
  147 + .maxNum(1)
  148 + .setSelectGif(false)
  149 + //是否需要相机(默认false)
  150 + .needCamera(true)
  151 + //选择模式(默认PICKER_IMAGE_VIDEO) PICKER_IMAGE-->图片 PICKER_VIDEO-->视频 PICKER_IMAGE_VIDEO-->图片&视频
  152 + .selectMode(PickerConfig.PICKER_IMAGE)
  153 + //拍照或录像存储位置 默认Environment.getExternalStorageDirectory().getPath() + File.separator + "JCamera"
  154 + .cachePath(Environment.getExternalStorageDirectory() + "/strike/file/")
  155 + //是否为发朋友圈模式(默认false) true-->图片与视频不能同时选择
  156 + .isFriendCircle(false)
  157 + //裁剪 只有在单选模式下才可以使用
  158 + .doCrop(1, 1, 300, 300)
  159 + //是否开启单选模式(默认false)
  160 + .isSinglePick(true)
  161 + .builder();
  162 + //跳转到图片选择页面 activity 请求码 结果码
  163 + imagePicker.start(getActivity(), PickerConfig.PICKER_IMAGE, PickerConfig.DEFAULT_RESULT_CODE);
  164 + }
  165 +
  166 + @Override
  167 + public void onFail() {
  168 +
  169 + }
  170 + });
156 171  
157 172 }
158 173  
... ... @@ -163,31 +178,17 @@ public class FirstLoginFragment extends BaseFragment&lt;FirstLoginView, FirstLoginP
163 178 if (tokenErrorDialog != null) tokenErrorDialog.dismiss();
164 179 }
165 180  
166   - @Override
167   - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
168   - super.onRequestPermissionsResult(requestCode, permissions, grantResults);
169   - Activity activity = getActivity();
170   - if (activity == null) return;
171   -// if (requestCode == REQUEST_CODE_PERMISSION_CAMERA) {
172   -// boolean hasPermission = PermissionChecker.onRequestPermissionsResult(
173   -// activity, requestCode, permissions, grantResults, false,
174   -// com.cnlive.media.picker.R.string.dialog_imagepicker_permission_sdcard_message)[0];
175   -// if (hasPermission) {
176   -// selectPhoto(true);
177   -// }
178   -// }
179   - }
180 181  
181 182 @Override
182 183 public void onActivityResult(int requestCode, int resultCode, Intent data) {
183 184 super.onActivityResult(requestCode, resultCode, data);
184   -// if (resultCode == PickerConfig.DEFAULT_RESULT_CODE) {
185   -// List<Media> list = data.getParcelableArrayListExtra(PickerConfig.EXTRA_RESULT);
186   -// if (list == null) return;
187   -// if (getPresenter() == null) return;
188   -// if (list.size() > 0 && list.get(0) != null && !TextUtils.isEmpty(list.get(0).path))
189   -// getPresenter().uploadFile(FirstLoginFragment.this.getActivity(), list.get(0).path);
190   -//
191   -// }
  185 + if (resultCode == PickerConfig.DEFAULT_RESULT_CODE) {
  186 + List<Media> list = data.getParcelableArrayListExtra(PickerConfig.EXTRA_RESULT);
  187 + if (list == null) return;
  188 + if (getPresenter() == null) return;
  189 + if (list.size() > 0 && list.get(0) != null && !TextUtils.isEmpty(list.get(0).path))
  190 + getPresenter().uploadFile(FirstLoginFragment.this.getActivity(), list.get(0).path);
  191 +
  192 + }
192 193 }
193 194 }
... ...
cloud/user/src/main/java/com/cnlive/strike/user/ui/fragment/QuickLoginFragment.java
... ... @@ -114,7 +114,7 @@ public class QuickLoginFragment extends BaseFragment&lt;QuickLoginView, QuickLoginP
114 114 mProgressDialog.setMessage(getResources().getString(R.string.login_dialog));
115 115 mProgressDialog.setCanceledOnTouchOutside(false);
116 116 mProgressDialog.setOnCancelListener(dialog -> {
117   -// getPresenter().cancelLoad();
  117 + getPresenter().cancelLoad();
118 118 });
119 119 getMvpView().setAgreementText();
120 120 binding.setClickListener(v -> onClick(v.getId()));
... ...
core/base/src/main/java/com/cnlive/core/libs/base/frame/activity/BaseActivity.java
... ... @@ -23,12 +23,14 @@ import androidx.annotation.ColorRes;
23 23 import androidx.annotation.IntRange;
24 24 import androidx.annotation.NonNull;
25 25 import androidx.annotation.Nullable;
  26 +import androidx.annotation.StringRes;
26 27 import androidx.appcompat.app.AlertDialog;
27 28 import androidx.core.app.ActivityCompat;
28 29 import androidx.core.content.ContextCompat;
29 30 import androidx.databinding.DataBindingUtil;
30 31 import androidx.databinding.ViewDataBinding;
31 32  
  33 +import com.alibaba.android.arouter.launcher.ARouter;
32 34 import com.cnlive.core.libs.base.frame.presenter.BasePresenter;
33 35 import com.cnlive.core.libs.base.frame.view.BaseView;
34 36 import com.cnlive.core.libs.base.interfaces.OnPermissionResponseListener;
... ... @@ -89,6 +91,8 @@ public abstract class BaseActivity&lt;V extends BaseView, P extends BasePresenter&lt;V
89 91 context = this;
90 92 me = this;
91 93 this.savedInstanceState = savedInstanceState;
  94 + //路由参数初始化
  95 + ARouter.getInstance().inject(this);
92 96 //activity管理
93 97 AppManager.getInstance().pushActivity(me);
94 98 //创建订阅映射
... ... @@ -446,6 +450,29 @@ public abstract class BaseActivity&lt;V extends BaseView, P extends BasePresenter&lt;V
446 450 }
447 451  
448 452 /**
  453 + * 自定义权限申请码和提示信息
  454 + * 可传入string字符串
  455 + *
  456 + * @param requestCode
  457 + * @param permissions
  458 + * @param onPermissionResponseListener
  459 + */
  460 + public void requestPermission(int requestCode, @StringRes int defineTip, String[]
  461 + permissions, OnPermissionResponseListener onPermissionResponseListener) {
  462 + this.onPermissionResponseListener = onPermissionResponseListener;
  463 + this.USER_SET_REQUEST_CODE_PERMISSION = requestCode;
  464 + this.userSetDefinePermissionTip = getResources().getString(defineTip);
  465 + if (checkPermissions(permissions)) {
  466 + if (onPermissionResponseListener != null) {
  467 + onPermissionResponseListener.onSuccess(permissions);
  468 + }
  469 + } else {
  470 + List<String> needPermissions = getDeniedPermissions(permissions);
  471 + ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]), USER_SET_REQUEST_CODE_PERMISSION);
  472 + }
  473 + }
  474 +
  475 + /**
449 476 * 获取权限集中需要申请权限的列表
450 477 *
451 478 * @param permissions
... ...
core/base/src/main/java/com/cnlive/core/libs/base/frame/fragment/BaseFragment.java
... ... @@ -10,10 +10,12 @@ import androidx.annotation.ColorRes;
10 10 import androidx.annotation.IntRange;
11 11 import androidx.annotation.NonNull;
12 12 import androidx.annotation.Nullable;
  13 +import androidx.annotation.StringRes;
13 14 import androidx.core.content.ContextCompat;
14 15 import androidx.databinding.DataBindingUtil;
15 16 import androidx.databinding.ViewDataBinding;
16 17  
  18 +import com.alibaba.android.arouter.launcher.ARouter;
17 19 import com.cnlive.core.libs.base.frame.activity.BaseActivity;
18 20 import com.cnlive.core.libs.base.frame.presenter.BasePresenter;
19 21 import com.cnlive.core.libs.base.interfaces.OnPermissionResponseListener;
... ... @@ -45,6 +47,7 @@ public abstract class BaseFragment&lt;V extends BaseView, P extends BasePresenter&lt;V
45 47 super.onCreate(savedInstanceState);
46 48 if (getActivity() == null) return;
47 49 me = (BaseActivity) getActivity();
  50 + ARouter.getInstance().inject(this);
48 51 initFragmentStyle();
49 52 initParams();
50 53 }
... ... @@ -220,6 +223,11 @@ public abstract class BaseFragment&lt;V extends BaseView, P extends BasePresenter&lt;V
220 223 me.requestPermission(requestCode, defineTip, permissions, onPermissionResponseListener);
221 224 }
222 225  
  226 + public void requestPermission(int requestCode, @StringRes int defineTip, String[]
  227 + permissions, OnPermissionResponseListener onPermissionResponseListener) {
  228 + me.requestPermission(requestCode, defineTip, permissions, onPermissionResponseListener);
  229 + }
  230 +
223 231 //多权限检查
224 232 public boolean checkPermissions(String[] permissions) {
225 233 return me.checkPermissions(permissions);
... ...
core/base/src/main/java/com/cnlive/core/libs/base/ui/widget/BottomSheet.java 0 → 100644
  1 +package com.cnlive.core.libs.base.ui.widget;
  2 +
  3 +import android.app.Dialog;
  4 +import android.content.Context;
  5 +import android.os.Bundle;
  6 +import android.text.TextUtils;
  7 +import android.util.Log;
  8 +import android.view.Gravity;
  9 +import android.view.LayoutInflater;
  10 +import android.view.View;
  11 +import android.view.ViewGroup;
  12 +import android.view.WindowManager;
  13 +import android.view.animation.AlphaAnimation;
  14 +import android.view.animation.Animation;
  15 +import android.view.animation.AnimationSet;
  16 +import android.view.animation.DecelerateInterpolator;
  17 +import android.view.animation.TranslateAnimation;
  18 +import android.widget.BaseAdapter;
  19 +import android.widget.ListView;
  20 +import android.widget.TextView;
  21 +
  22 +import androidx.annotation.NonNull;
  23 +
  24 +import com.cnlive.core.libs.base.util.DensityUtils;
  25 +import com.cnlive.strike.base.R;
  26 +import com.qmuiteam.qmui.QMUILog;
  27 +import com.qmuiteam.qmui.util.QMUIDisplayHelper;
  28 +import com.qmuiteam.qmui.util.QMUILangHelper;
  29 +import com.qmuiteam.qmui.util.QMUIResHelper;
  30 +
  31 +import java.util.ArrayList;
  32 +import java.util.List;
  33 +
  34 +/**
  35 + * QMUIBottomSheet 在 {@link Dialog} 的基础上重新定制了 {@link #show()} 和 {@link #hide()} 时的动画效果, 使 {@link Dialog} 在界面底部升起和降下。
  36 + * <p>
  37 + * 提供了以下两种面板样式:
  38 + * <ul>
  39 + * <li>列表样式, 使用 {@link BottomSheet.BottomListSheetBuilder} 生成。</li>
  40 + * </ul>
  41 + * </p>
  42 + */
  43 +public class BottomSheet extends Dialog {
  44 + private static final String TAG = "QMUIBottomSheet";
  45 + // 动画时长
  46 + private final static int mAnimationDuration = 200;
  47 + // 持有 ContentView,为了做动画
  48 + private View mContentView;
  49 + private boolean mIsAnimating = false;
  50 +
  51 + private OnBottomSheetShowListener mOnBottomSheetShowListener;
  52 +
  53 + public BottomSheet(Context context) {
  54 + super(context, com.qmuiteam.qmui.R.style.QMUI_BottomSheet);
  55 + }
  56 +
  57 + public void setOnBottomSheetShowListener(OnBottomSheetShowListener onBottomSheetShowListener) {
  58 + mOnBottomSheetShowListener = onBottomSheetShowListener;
  59 + }
  60 +
  61 + @Override
  62 + protected void onCreate(Bundle savedInstanceState) {
  63 + super.onCreate(savedInstanceState);
  64 +
  65 + //noinspection ConstantConditions
  66 + getWindow().getDecorView().setPadding(0, 0, 0, 0);
  67 +
  68 + // 在底部,宽度撑满
  69 + WindowManager.LayoutParams params = getWindow().getAttributes();
  70 + params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
  71 + params.gravity = Gravity.BOTTOM | Gravity.CENTER;
  72 +
  73 + int screenWidth = QMUIDisplayHelper.getScreenWidth(getContext());
  74 + int screenHeight = QMUIDisplayHelper.getScreenHeight(getContext());
  75 + params.width = screenWidth < screenHeight ? screenWidth : screenHeight;
  76 + getWindow().setAttributes(params);
  77 + setCanceledOnTouchOutside(true);
  78 + }
  79 +
  80 + @Override
  81 + public void setContentView(int layoutResID) {
  82 + mContentView = LayoutInflater.from(getContext()).inflate(layoutResID, null);
  83 + super.setContentView(mContentView);
  84 + }
  85 +
  86 + @Override
  87 + public void setContentView(@NonNull View view, ViewGroup.LayoutParams params) {
  88 + mContentView = view;
  89 + super.setContentView(view, params);
  90 + }
  91 +
  92 + public View getContentView() {
  93 + return mContentView;
  94 + }
  95 +
  96 + @Override
  97 + public void setContentView(@NonNull View view) {
  98 + mContentView = view;
  99 + super.setContentView(view);
  100 + }
  101 +
  102 + /**
  103 + * BottomSheet升起动画
  104 + */
  105 + private void animateUp() {
  106 + if (mContentView == null) {
  107 + return;
  108 + }
  109 + TranslateAnimation translate = new TranslateAnimation(
  110 + Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,
  111 + Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 0f
  112 + );
  113 + AlphaAnimation alpha = new AlphaAnimation(0, 1);
  114 + AnimationSet set = new AnimationSet(true);
  115 + set.addAnimation(translate);
  116 + set.addAnimation(alpha);
  117 + set.setInterpolator(new DecelerateInterpolator());
  118 + set.setDuration(mAnimationDuration);
  119 + set.setFillAfter(true);
  120 + mContentView.startAnimation(set);
  121 + }
  122 +
  123 + /**
  124 + * BottomSheet降下动画
  125 + */
  126 + private void animateDown() {
  127 + if (mContentView == null) {
  128 + return;
  129 + }
  130 + TranslateAnimation translate = new TranslateAnimation(
  131 + Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,
  132 + Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1f
  133 + );
  134 + AlphaAnimation alpha = new AlphaAnimation(1, 0);
  135 + AnimationSet set = new AnimationSet(true);
  136 + set.addAnimation(translate);
  137 + set.addAnimation(alpha);
  138 + set.setInterpolator(new DecelerateInterpolator());
  139 + set.setDuration(mAnimationDuration);
  140 + set.setFillAfter(true);
  141 + set.setAnimationListener(new Animation.AnimationListener() {
  142 + @Override
  143 + public void onAnimationStart(Animation animation) {
  144 + mIsAnimating = true;
  145 + }
  146 +
  147 + @Override
  148 + public void onAnimationEnd(Animation animation) {
  149 + mIsAnimating = false;
  150 + /**
  151 + * Bugfix: Attempting to destroy the window while drawing!
  152 + */
  153 + mContentView.post(new Runnable() {
  154 + @Override
  155 + public void run() {
  156 + // java.lang.IllegalArgumentException: View=com.android.internal.policy.PhoneWindow$DecorView{22dbf5b V.E...... R......D 0,0-1080,1083} not attached to window manager
  157 + // 在dismiss的时候可能已经detach了,简单try-catch一下
  158 + try {
  159 + BottomSheet.super.dismiss();
  160 + } catch (Exception e) {
  161 + QMUILog.w(TAG, "dismiss error\n" + Log.getStackTraceString(e));
  162 + }
  163 + }
  164 + });
  165 + }
  166 +
  167 + @Override
  168 + public void onAnimationRepeat(Animation animation) {
  169 +
  170 + }
  171 + });
  172 + mContentView.startAnimation(set);
  173 + }
  174 +
  175 + @Override
  176 + public void show() {
  177 + super.show();
  178 + animateUp();
  179 + if (mOnBottomSheetShowListener != null) {
  180 + mOnBottomSheetShowListener.onShow();
  181 + }
  182 + }
  183 +
  184 + @Override
  185 + public void dismiss() {
  186 + if (mIsAnimating) {
  187 + return;
  188 + }
  189 + animateDown();
  190 + }
  191 +
  192 + /**
  193 + * 销毁提示窗
  194 + *
  195 + * @param animation 是否启动动画
  196 + */
  197 + public void dismiss(boolean animation) {
  198 + if (animation) {
  199 + dismiss();
  200 + } else {
  201 + super.dismiss();
  202 + }
  203 + }
  204 +
  205 + public interface OnBottomSheetShowListener {
  206 + void onShow();
  207 + }
  208 +
  209 + /**
  210 + * 生成列表类型的 {@link BottomSheet} 对话框。
  211 + */
  212 + public static class BottomListSheetBuilder {
  213 +
  214 + private Context mContext;
  215 +
  216 + private BottomSheet mDialog;
  217 + private List<BottomSheetListItemData> mItems;
  218 + private BaseAdapter mAdapter;
  219 + private List<View> mHeaderViews;
  220 + private ListView mContainerView;
  221 + private int mCheckedIndex;
  222 + private String mTitle;
  223 + private TextView mTitleTv;
  224 + private OnSheetItemClickListener mOnSheetItemClickListener;
  225 + private OnDismissListener mOnBottomDialogDismissListener;
  226 +
  227 +
  228 + public BottomListSheetBuilder(Context context) {
  229 + mContext = context;
  230 + mItems = new ArrayList<>();
  231 + mHeaderViews = new ArrayList<>();
  232 + }
  233 +
  234 + public BottomListSheetBuilder setCheckedIndex(int checkedIndex) {
  235 + mCheckedIndex = checkedIndex;
  236 + return this;
  237 + }
  238 +
  239 + /**
  240 + * @param textAndTag Item 的文字内容,同时会把内容设置为 tag。
  241 + */
  242 + public BottomListSheetBuilder addItem(String textAndTag) {
  243 + mItems.add(new BottomSheetListItemData(textAndTag, textAndTag, null, false));
  244 + return this;
  245 + }
  246 +
  247 + /**
  248 + * @param textAndTag Item 的文字内容,同时会把内容设置为 tag。
  249 + * @param split 是否显示分割线
  250 + */
  251 + public BottomListSheetBuilder addItem(String textAndTag, boolean split) {
  252 + mItems.add(new BottomSheetListItemData(textAndTag, textAndTag, null, split));
  253 + return this;
  254 + }
  255 +
  256 + /**
  257 + * @param subTitle 副标题文字内容
  258 + * @param textAndTag Item 的文字内容,同时会把内容设置为 tag。
  259 + * @param split 是否显示分割线
  260 + * @return
  261 + */
  262 + public BottomListSheetBuilder addItem(String textAndTag, String subTitle, boolean split) {
  263 + mItems.add(new BottomSheetListItemData(textAndTag, textAndTag, subTitle, split));
  264 + return this;
  265 + }
  266 +
  267 + /**
  268 + * @param text Item 的文字内容。
  269 + * @param tag item 的 tag。
  270 + */
  271 + public BottomListSheetBuilder addItem(String text, String tag) {
  272 + mItems.add(new BottomSheetListItemData(text, tag));
  273 + return this;
  274 + }
  275 +
  276 +
  277 + public BottomListSheetBuilder setOnSheetItemClickListener(OnSheetItemClickListener onSheetItemClickListener) {
  278 + mOnSheetItemClickListener = onSheetItemClickListener;
  279 + return this;
  280 + }
  281 +
  282 + public BottomListSheetBuilder setOnBottomDialogDismissListener(OnDismissListener listener) {
  283 + mOnBottomDialogDismissListener = listener;
  284 + return this;
  285 + }
  286 +
  287 + public BottomListSheetBuilder addHeaderView(View view) {
  288 + if (view != null) {
  289 + mHeaderViews.add(view);
  290 + }
  291 + return this;
  292 + }
  293 +
  294 + public BottomListSheetBuilder setTitle(String title) {
  295 + mTitle = title;
  296 + return this;
  297 + }
  298 +
  299 + public BottomListSheetBuilder setTitle(int resId) {
  300 + mTitle = mContext.getResources().getString(resId);
  301 + return this;
  302 + }
  303 +
  304 + public BottomSheet build() {
  305 + mDialog = new BottomSheet(mContext);
  306 + View contentView = buildViews();
  307 + mDialog.setContentView(contentView,
  308 + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
  309 + if (mOnBottomDialogDismissListener != null) {
  310 + mDialog.setOnDismissListener(mOnBottomDialogDismissListener);
  311 + }
  312 + return mDialog;
  313 + }
  314 +
  315 + private View buildViews() {
  316 + View wrapperView = View.inflate(mContext, getContentViewLayoutId(), null);
  317 + mTitleTv = (TextView) wrapperView.findViewById(com.qmuiteam.qmui.R.id.title);
  318 + mContainerView = (ListView) wrapperView.findViewById(com.qmuiteam.qmui.R.id.listview);
  319 + if (mTitle != null && mTitle.length() != 0) {
  320 + mTitleTv.setVisibility(View.VISIBLE);
  321 + mTitleTv.setText(mTitle);
  322 + } else {
  323 + mTitleTv.setVisibility(View.GONE);
  324 + }
  325 + if (mHeaderViews.size() > 0) {
  326 + for (View headerView : mHeaderViews) {
  327 + mContainerView.addHeaderView(headerView);
  328 + }
  329 + }
  330 + if (needToScroll()) {
  331 + mContainerView.getLayoutParams().height = getListMaxHeight();
  332 + mDialog.setOnBottomSheetShowListener(new OnBottomSheetShowListener() {
  333 + @Override
  334 + public void onShow() {
  335 + mContainerView.setSelection(mCheckedIndex);
  336 + }
  337 + });
  338 + }
  339 +
  340 + mAdapter = new ListAdapter();
  341 + mContainerView.setAdapter(mAdapter);
  342 + return wrapperView;
  343 + }
  344 +
  345 + private boolean needToScroll() {
  346 + int itemHeight = QMUIResHelper.getAttrDimen(mContext, com.qmuiteam.qmui.R.attr.qmui_bottom_sheet_list_item_height);
  347 + int totalHeight = mItems.size() * itemHeight;
  348 + if (mHeaderViews.size() > 0) {
  349 + for (View view : mHeaderViews) {
  350 + if (view.getMeasuredHeight() == 0) {
  351 + view.measure(0, 0);
  352 + }
  353 + totalHeight += view.getMeasuredHeight();
  354 + }
  355 + }
  356 + if (mTitleTv != null && !QMUILangHelper.isNullOrEmpty(mTitle)) {
  357 + totalHeight += QMUIResHelper.getAttrDimen(mContext, com.qmuiteam.qmui.R.attr.qmui_bottom_sheet_title_height);
  358 + }
  359 + return totalHeight > getListMaxHeight();
  360 + }
  361 +
  362 + /**
  363 + * 注意:这里只考虑List的高度,如果有title或者headerView,不计入考虑中
  364 + */
  365 + protected int getListMaxHeight() {
  366 + return (int) (QMUIDisplayHelper.getScreenHeight(mContext) * 0.5);
  367 + }
  368 +
  369 + public void notifyDataSetChanged() {
  370 + if (mAdapter != null) {
  371 + mAdapter.notifyDataSetChanged();
  372 + }
  373 + if (needToScroll()) {
  374 + mContainerView.getLayoutParams().height = getListMaxHeight();
  375 + mContainerView.setSelection(mCheckedIndex);
  376 + }
  377 + }
  378 +
  379 + protected int getContentViewLayoutId() {
  380 + return R.layout.qmui_bottom_sheet_list;
  381 + }
  382 +
  383 + public interface OnSheetItemClickListener {
  384 + void onClick(BottomSheet dialog, View itemView, int position, String tag);
  385 + }
  386 +
  387 + private static class BottomSheetListItemData {
  388 +
  389 + String text;
  390 + String tag = "";
  391 + boolean isDisabled = false;
  392 + boolean split = false;
  393 + String subTitle;
  394 +
  395 + public BottomSheetListItemData(String text, String tag, String subTitle, boolean split) {
  396 + this.text = text;
  397 + this.tag = tag;
  398 + this.split = split;
  399 + this.subTitle = subTitle;
  400 + }
  401 +
  402 + public BottomSheetListItemData(String text, String tag) {
  403 + this.text = text;
  404 + this.tag = tag;
  405 + }
  406 +
  407 +
  408 + }
  409 +
  410 + private static class ViewHolder {
  411 + TextView textView;
  412 + TextView subTextView;
  413 + View bottonBar;
  414 + View itemView;
  415 + }
  416 +
  417 + private class ListAdapter extends BaseAdapter {
  418 +
  419 + @Override
  420 + public int getCount() {
  421 + return mItems.size();
  422 + }
  423 +
  424 + @Override
  425 + public BottomSheetListItemData getItem(int position) {
  426 + return mItems.get(position);
  427 + }
  428 +
  429 + @Override
  430 + public long getItemId(int position) {
  431 + return 0;
  432 + }
  433 +
  434 + @Override
  435 + public View getView(final int position, View convertView, ViewGroup parent) {
  436 + final BottomSheetListItemData data = getItem(position);
  437 + final ViewHolder holder;
  438 + if (convertView == null) {
  439 + LayoutInflater inflater = LayoutInflater.from(mContext);
  440 + convertView = inflater.inflate(R.layout.qmui_bottom_sheet_list_item, parent, false);
  441 + holder = new ViewHolder();
  442 + holder.textView = (TextView) convertView.findViewById(com.qmuiteam.qmui.R.id.bottom_dialog_list_item_title);
  443 + holder.bottonBar = convertView.findViewById(R.id.botton_bar);
  444 + holder.subTextView = convertView.findViewById(R.id.bottom_dialog_list_item_subtitle);
  445 + holder.itemView = convertView.findViewById(R.id.itemView);
  446 + convertView.setTag(holder);
  447 + } else {
  448 + holder = (ViewHolder) convertView.getTag();
  449 + }
  450 + // holder.textView.setTextColor(mContext.getResources().getColor(R.color.qmui_config_color_red));
  451 + if (data.text.equals("取消")){
  452 + holder.textView.setTextColor(mContext.getResources().getColor(R.color.gay));
  453 + }else {
  454 + holder.textView.setTextColor(mContext.getResources().getColor(R.color.green));
  455 + }
  456 + holder.textView.setText(data.text);
  457 +
  458 + if (data.isDisabled) {
  459 + holder.textView.setEnabled(false);
  460 + convertView.setEnabled(false);
  461 + } else {
  462 + holder.textView.setEnabled(true);
  463 + convertView.setEnabled(true);
  464 + }
  465 +
  466 + if (data.split) {
  467 + holder.bottonBar.setVisibility(View.VISIBLE);
  468 + } else {
  469 + holder.bottonBar.setVisibility(View.GONE);
  470 + }
  471 + if (TextUtils.isEmpty(data.subTitle)) {
  472 + holder.subTextView.setText("");
  473 + holder.subTextView.setVisibility(View.GONE);
  474 + holder.itemView.getLayoutParams().height = DensityUtils.dp2px(56);
  475 + } else {
  476 + holder.itemView.getLayoutParams().height = DensityUtils.dp2px(61);
  477 + holder.subTextView.setText(data.subTitle);
  478 + holder.subTextView.setVisibility(View.VISIBLE);
  479 + }
  480 +
  481 + convertView.setOnClickListener(new View.OnClickListener() {
  482 + @Override
  483 + public void onClick(View v) {
  484 + if (mOnSheetItemClickListener != null) {
  485 + mOnSheetItemClickListener.onClick(mDialog, v, position, data.tag);
  486 + }
  487 + }
  488 + });
  489 + return convertView;
  490 + }
  491 + }
  492 +
  493 + }
  494 +
  495 +}
... ...
core/base/src/main/java/com/cnlive/core/libs/base/ui/widget/switchbutton/ColorUtils.java 0 → 100644
  1 +package com.cnlive.core.libs.base.ui.widget.switchbutton;
  2 +
  3 +import android.content.res.ColorStateList;
  4 +
  5 +/**
  6 + * Generate thumb and background color state list use tintColor
  7 + * Created by kyle on 15/11/4.
  8 + */
  9 +public class ColorUtils {
  10 + private static final int ENABLE_ATTR = android.R.attr.state_enabled;
  11 + private static final int CHECKED_ATTR = android.R.attr.state_checked;
  12 + private static final int PRESSED_ATTR = android.R.attr.state_pressed;
  13 +
  14 + static ColorStateList generateThumbColorWithTintColor(final int tintColor) {
  15 + int[][] states = new int[][]{
  16 + {-ENABLE_ATTR, CHECKED_ATTR},
  17 + {-ENABLE_ATTR},
  18 + {PRESSED_ATTR, -CHECKED_ATTR},
  19 + {PRESSED_ATTR, CHECKED_ATTR},
  20 + {CHECKED_ATTR},
  21 + {-CHECKED_ATTR}
  22 + };
  23 +
  24 + int[] colors = new int[]{
  25 + tintColor - 0xAA000000,
  26 + 0xFFBABABA,
  27 + tintColor - 0x99000000,
  28 + tintColor - 0x99000000,
  29 + tintColor | 0xFF000000,
  30 + 0xFFEEEEEE
  31 + };
  32 + return new ColorStateList(states, colors);
  33 + }
  34 +
  35 + static ColorStateList generateBackColorWithTintColor(final int tintColor) {
  36 + int[][] states = new int[][]{
  37 + {-ENABLE_ATTR, CHECKED_ATTR},
  38 + {-ENABLE_ATTR},
  39 + {CHECKED_ATTR, PRESSED_ATTR},
  40 + {-CHECKED_ATTR, PRESSED_ATTR},
  41 + {CHECKED_ATTR},
  42 + {-CHECKED_ATTR}
  43 + };
  44 +
  45 + int[] colors = new int[]{
  46 + tintColor - 0xE1000000,
  47 + 0x10000000,
  48 + tintColor - 0xD0000000,
  49 + 0x20000000,
  50 + tintColor - 0xD0000000,
  51 + 0x20000000
  52 + };
  53 + return new ColorStateList(states, colors);
  54 + }
  55 +}
... ...
core/base/src/main/java/com/cnlive/core/libs/base/ui/widget/switchbutton/SwitchButton.java 0 → 100644
  1 +package com.cnlive.core.libs.base.ui.widget.switchbutton;
  2 +
  3 +import android.animation.ObjectAnimator;
  4 +import android.annotation.SuppressLint;
  5 +import android.content.Context;
  6 +import android.content.res.ColorStateList;
  7 +import android.content.res.Resources;
  8 +import android.content.res.TypedArray;
  9 +import android.graphics.Canvas;
  10 +import android.graphics.Color;
  11 +import android.graphics.Paint;
  12 +import android.graphics.PointF;
  13 +import android.graphics.RectF;
  14 +import android.graphics.drawable.Drawable;
  15 +import android.graphics.drawable.StateListDrawable;
  16 +import android.os.Parcel;
  17 +import android.os.Parcelable;
  18 +import android.text.Layout;
  19 +import android.text.StaticLayout;
  20 +import android.text.TextPaint;
  21 +import android.text.TextUtils;
  22 +import android.util.AttributeSet;
  23 +import android.util.TypedValue;
  24 +import android.view.MotionEvent;
  25 +import android.view.SoundEffectConstants;
  26 +import android.view.ViewConfiguration;
  27 +import android.view.ViewParent;
  28 +import android.view.animation.AccelerateDecelerateInterpolator;
  29 +import android.widget.CompoundButton;
  30 +
  31 +import androidx.core.content.ContextCompat;
  32 +
  33 +import com.cnlive.strike.base.R;
  34 +
  35 +
  36 +/**
  37 + * SwitchButton
  38 + *
  39 + * @author kyleduo
  40 + * @since 2014-09-24
  41 + */
  42 +
  43 +@SuppressWarnings("unused")
  44 +public class SwitchButton extends CompoundButton {
  45 + public static final float DEFAULT_THUMB_RANGE_RATIO = 1.8f;
  46 + public static final int DEFAULT_THUMB_SIZE_DP = 20;
  47 + public static final int DEFAULT_THUMB_MARGIN_DP = 2;
  48 + public static final int DEFAULT_ANIMATION_DURATION = 250;
  49 + public static final int DEFAULT_TINT_COLOR = 0x327FC2;
  50 +
  51 + private static int[] CHECKED_PRESSED_STATE = new int[]{android.R.attr.state_checked, android.R.attr.state_enabled, android.R.attr.state_pressed};
  52 + private static int[] UNCHECKED_PRESSED_STATE = new int[]{-android.R.attr.state_checked, android.R.attr.state_enabled, android.R.attr.state_pressed};
  53 +
  54 + private Drawable mThumbDrawable, mBackDrawable;
  55 + private ColorStateList mBackColor, mThumbColor;
  56 + private float mThumbRadius, mBackRadius;
  57 + private RectF mThumbMargin;
  58 + private float mThumbRangeRatio;
  59 + private long mAnimationDuration;
  60 + // fade back drawable or color when dragging or animating
  61 + private boolean mFadeBack;
  62 + private int mTintColor;
  63 + private int mThumbWidth;
  64 + private int mThumbHeight;
  65 + private int mBackWidth;
  66 + private int mBackHeight;
  67 +
  68 + private int mCurrThumbColor, mCurrBackColor, mNextBackColor, mOnTextColor, mOffTextColor;
  69 + private Drawable mCurrentBackDrawable, mNextBackDrawable;
  70 + private RectF mThumbRectF, mBackRectF, mSafeRectF, mTextOnRectF, mTextOffRectF;
  71 + private Paint mPaint;
  72 + // whether using Drawable for thumb or back
  73 + private boolean mIsThumbUseDrawable, mIsBackUseDrawable;
  74 + private boolean mDrawDebugRect = false;
  75 + private ObjectAnimator mProgressAnimator;
  76 + // animation control
  77 + private float mProgress;
  78 + // temp position of thumb when dragging or animating
  79 + private RectF mPresentThumbRectF;
  80 + private float mStartX, mStartY, mLastX;
  81 + private int mTouchSlop;
  82 + private int mClickTimeout;
  83 + private Paint mRectPaint;
  84 + private CharSequence mTextOn;
  85 + private CharSequence mTextOff;
  86 + private TextPaint mTextPaint;
  87 + private Layout mOnLayout;
  88 + private Layout mOffLayout;
  89 + private float mTextWidth;
  90 + private float mTextHeight;
  91 + private int mTextThumbInset;
  92 + private int mTextExtra;
  93 + private int mTextAdjust;
  94 + // FIX #78,#85 : When restoring saved states, setChecked() called by super. So disable
  95 + // animation and event listening when restoring.
  96 + private boolean mRestoring = false;
  97 + private boolean mReady = false;
  98 + private boolean mCatch = false;
  99 +
  100 + private OnCheckedChangeListener mChildOnCheckedChangeListener;
  101 +
  102 + public SwitchButton(Context context, AttributeSet attrs, int defStyle) {
  103 + super(context, attrs, defStyle);
  104 + init(attrs);
  105 + }
  106 +
  107 + public SwitchButton(Context context, AttributeSet attrs) {
  108 + super(context, attrs);
  109 + init(attrs);
  110 + }
  111 +
  112 + public SwitchButton(Context context) {
  113 + super(context);
  114 + init(null);
  115 + }
  116 +
  117 + private void init(AttributeSet attrs) {
  118 + mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  119 + mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout();
  120 +
  121 + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  122 + mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  123 + mRectPaint.setStyle(Paint.Style.STROKE);
  124 + mRectPaint.setStrokeWidth(getResources().getDisplayMetrics().density);
  125 +
  126 + mTextPaint = getPaint();
  127 +
  128 + mThumbRectF = new RectF();
  129 + mBackRectF = new RectF();
  130 + mSafeRectF = new RectF();
  131 + mThumbMargin = new RectF();
  132 + mTextOnRectF = new RectF();
  133 + mTextOffRectF = new RectF();
  134 +
  135 + mProgressAnimator = ObjectAnimator.ofFloat(this, "progress", 0, 0).setDuration(DEFAULT_ANIMATION_DURATION);
  136 + mProgressAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
  137 +
  138 + mPresentThumbRectF = new RectF();
  139 +
  140 + Resources res = getResources();
  141 + float density = res.getDisplayMetrics().density;
  142 +
  143 + Drawable thumbDrawable = null;
  144 + ColorStateList thumbColor = null;
  145 + float margin = density * DEFAULT_THUMB_MARGIN_DP;
  146 + float marginLeft = 0;
  147 + float marginRight = 0;
  148 + float marginTop = 0;
  149 + float marginBottom = 0;
  150 + float thumbWidth = 0;
  151 + float thumbHeight = 0;
  152 + float thumbRadius = -1;
  153 + float backRadius = -1;
  154 + Drawable backDrawable = null;
  155 + ColorStateList backColor = null;
  156 + float thumbRangeRatio = DEFAULT_THUMB_RANGE_RATIO;
  157 + int animationDuration = DEFAULT_ANIMATION_DURATION;
  158 + boolean fadeBack = true;
  159 + int tintColor = 0;
  160 + String textOn = null;
  161 + String textOff = null;
  162 + int textThumbInset = 0;
  163 + int textExtra = 0;
  164 + int textAdjust = 0;
  165 +
  166 + TypedArray ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, R.styleable.SwitchButton);
  167 + if (ta != null) {
  168 + thumbDrawable = ta.getDrawable(R.styleable.SwitchButton_kswThumbDrawable);
  169 + thumbColor = ta.getColorStateList(R.styleable.SwitchButton_kswThumbColor);
  170 + margin = ta.getDimension(R.styleable.SwitchButton_kswThumbMargin, margin);
  171 + marginLeft = ta.getDimension(R.styleable.SwitchButton_kswThumbMarginLeft, margin);
  172 + marginRight = ta.getDimension(R.styleable.SwitchButton_kswThumbMarginRight, margin);
  173 + marginTop = ta.getDimension(R.styleable.SwitchButton_kswThumbMarginTop, margin);
  174 + marginBottom = ta.getDimension(R.styleable.SwitchButton_kswThumbMarginBottom, margin);
  175 + thumbWidth = ta.getDimension(R.styleable.SwitchButton_kswThumbWidth, thumbWidth);
  176 + thumbHeight = ta.getDimension(R.styleable.SwitchButton_kswThumbHeight, thumbHeight);
  177 + thumbRadius = ta.getDimension(R.styleable.SwitchButton_kswThumbRadius, thumbRadius);
  178 + backRadius = ta.getDimension(R.styleable.SwitchButton_kswBackRadius, backRadius);
  179 + backDrawable = ta.getDrawable(R.styleable.SwitchButton_kswBackDrawable);
  180 + backColor = ta.getColorStateList(R.styleable.SwitchButton_kswBackColor);
  181 + thumbRangeRatio = ta.getFloat(R.styleable.SwitchButton_kswThumbRangeRatio, thumbRangeRatio);
  182 + animationDuration = ta.getInteger(R.styleable.SwitchButton_kswAnimationDuration, animationDuration);
  183 + fadeBack = ta.getBoolean(R.styleable.SwitchButton_kswFadeBack, true);
  184 + tintColor = ta.getColor(R.styleable.SwitchButton_kswTintColor, tintColor);
  185 + textOn = ta.getString(R.styleable.SwitchButton_kswTextOn);
  186 + textOff = ta.getString(R.styleable.SwitchButton_kswTextOff);
  187 + textThumbInset = ta.getDimensionPixelSize(R.styleable.SwitchButton_kswTextThumbInset, 0);
  188 + textExtra = ta.getDimensionPixelSize(R.styleable.SwitchButton_kswTextExtra, 0);
  189 + textAdjust = ta.getDimensionPixelSize(R.styleable.SwitchButton_kswTextAdjust, 0);
  190 + ta.recycle();
  191 + }
  192 +
  193 + // click
  194 + ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, new int[]{android.R.attr.focusable, android.R.attr.clickable});
  195 + if (ta != null) {
  196 + boolean focusable = ta.getBoolean(0, true);
  197 + //noinspection ResourceType
  198 + @SuppressLint("ResourceType")
  199 + boolean clickable = ta.getBoolean(1, focusable);
  200 + setFocusable(focusable);
  201 + setClickable(clickable);
  202 + ta.recycle();
  203 + } else {
  204 + setFocusable(true);
  205 + setClickable(true);
  206 + }
  207 +
  208 + // text
  209 + mTextOn = textOn;
  210 + mTextOff = textOff;
  211 + mTextThumbInset = textThumbInset;
  212 + mTextExtra = textExtra;
  213 + mTextAdjust = textAdjust;
  214 +
  215 + // thumb drawable and color
  216 + mThumbDrawable = thumbDrawable;
  217 + mThumbColor = thumbColor;
  218 + mIsThumbUseDrawable = mThumbDrawable != null;
  219 + mTintColor = tintColor;
  220 + if (mTintColor == 0) {
  221 + TypedValue typedValue = new TypedValue();
  222 + boolean found = getContext().getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true);
  223 + if (found) {
  224 + mTintColor = typedValue.data;
  225 + } else {
  226 + mTintColor = DEFAULT_TINT_COLOR;
  227 + }
  228 + }
  229 + if (!mIsThumbUseDrawable && mThumbColor == null) {
  230 + mThumbColor = ColorUtils.generateThumbColorWithTintColor(mTintColor);
  231 + mCurrThumbColor = mThumbColor.getDefaultColor();
  232 + }
  233 +
  234 + // thumbSize
  235 + mThumbWidth = ceil(thumbWidth);
  236 + mThumbHeight = ceil(thumbHeight);
  237 +
  238 + // back drawable and color
  239 + mBackDrawable = backDrawable;
  240 + mBackColor = backColor;
  241 + mIsBackUseDrawable = mBackDrawable != null;
  242 + if (!mIsBackUseDrawable && mBackColor == null) {
  243 + mBackColor = ColorUtils.generateBackColorWithTintColor(mTintColor);
  244 + mCurrBackColor = mBackColor.getDefaultColor();
  245 + mNextBackColor = mBackColor.getColorForState(CHECKED_PRESSED_STATE, mCurrBackColor);
  246 + }
  247 +
  248 + // margin
  249 + mThumbMargin.set(marginLeft, marginTop, marginRight, marginBottom);
  250 +
  251 + // size & measure params must larger than 1
  252 + mThumbRangeRatio = mThumbMargin.width() >= 0 ? Math.max(thumbRangeRatio, 1) : thumbRangeRatio;
  253 +
  254 + mThumbRadius = thumbRadius;
  255 + mBackRadius = backRadius;
  256 + mAnimationDuration = animationDuration;
  257 + mFadeBack = fadeBack;
  258 +
  259 + mProgressAnimator.setDuration(mAnimationDuration);
  260 +
  261 + // sync checked status
  262 + if (isChecked()) {
  263 + setProgress(1);
  264 + }
  265 + }
  266 +
  267 +
  268 + private Layout makeLayout(CharSequence text) {
  269 + return new StaticLayout(text, mTextPaint, (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)), Layout.Alignment.ALIGN_CENTER, 1.f, 0, false);
  270 + }
  271 +
  272 + @Override
  273 + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  274 + /*
  275 + * ensure textLayout
  276 + */
  277 + if (mOnLayout == null && !TextUtils.isEmpty(mTextOn)) {
  278 + mOnLayout = makeLayout(mTextOn);
  279 + }
  280 + if (mOffLayout == null && !TextUtils.isEmpty(mTextOff)) {
  281 + mOffLayout = makeLayout(mTextOff);
  282 + }
  283 +
  284 + float onWidth = mOnLayout != null ? mOnLayout.getWidth() : 0;
  285 + float offWidth = mOffLayout != null ? mOffLayout.getWidth() : 0;
  286 + if (onWidth != 0 || offWidth != 0) {
  287 + mTextWidth = Math.max(onWidth, offWidth);
  288 + } else {
  289 + mTextWidth = 0;
  290 + }
  291 +
  292 + float onHeight = mOnLayout != null ? mOnLayout.getHeight() : 0;
  293 + float offHeight = mOffLayout != null ? mOffLayout.getHeight() : 0;
  294 + if (onHeight != 0 || offHeight != 0) {
  295 + mTextHeight = Math.max(onHeight, offHeight);
  296 + } else {
  297 + mTextHeight = 0;
  298 + }
  299 +
  300 + setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
  301 + }
  302 +
  303 + /**
  304 + * SwitchButton use this formula to determine the final size of thumb, background and itself.
  305 + *
  306 + * textWidth = max(onWidth, offWidth)
  307 + * thumbRange = thumbWidth * rangeRatio
  308 + * textExtraSpace = textWidth + textExtra - (moveRange - thumbWidth + max(thumbMargin.left, thumbMargin.right) + textThumbInset)
  309 + * backWidth = thumbRange + thumbMargin.left + thumbMargin.right + max(textExtraSpace, 0)
  310 + * contentSize = thumbRange + max(thumbMargin.left, 0) + max(thumbMargin.right, 0) + max(textExtraSpace, 0)
  311 + *
  312 + * @param widthMeasureSpec widthMeasureSpec
  313 + * @return measuredWidth
  314 + */
  315 + private int measureWidth(int widthMeasureSpec) {
  316 + int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  317 + int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  318 + int measuredWidth = widthSize;
  319 +
  320 + if (mThumbWidth == 0 && mIsThumbUseDrawable) {
  321 + mThumbWidth = mThumbDrawable.getIntrinsicWidth();
  322 + }
  323 +
  324 + int moveRange;
  325 + int textWidth = ceil(mTextWidth);
  326 + // how much the background should extend to fit text.
  327 + int textExtraSpace;
  328 + int contentSize;
  329 +
  330 + if (mThumbRangeRatio == 0) {
  331 + mThumbRangeRatio = DEFAULT_THUMB_RANGE_RATIO;
  332 + }
  333 +
  334 + if (widthMode == MeasureSpec.EXACTLY) {
  335 + contentSize = widthSize - getPaddingLeft() - getPaddingRight();
  336 +
  337 + if (mThumbWidth != 0) {
  338 + moveRange = ceil(mThumbWidth * mThumbRangeRatio);
  339 + textExtraSpace = textWidth + mTextExtra - (moveRange - mThumbWidth + ceil(Math.max(mThumbMargin.left, mThumbMargin.right)));
  340 + mBackWidth = ceil(moveRange + mThumbMargin.left + mThumbMargin.right + Math.max(textExtraSpace, 0));
  341 + if (mBackWidth < 0) {
  342 + mThumbWidth = 0;
  343 + }
  344 + if (moveRange + Math.max(mThumbMargin.left, 0) + Math.max(mThumbMargin.right, 0) + Math.max(textExtraSpace, 0) > contentSize) {
  345 + mThumbWidth = 0;
  346 + }
  347 + }
  348 +
  349 + if (mThumbWidth == 0) {
  350 + contentSize = widthSize - getPaddingLeft() - getPaddingRight();
  351 + moveRange = ceil(contentSize - Math.max(mThumbMargin.left, 0) - Math.max(mThumbMargin.right, 0));
  352 + if (moveRange < 0) {
  353 + mThumbWidth = 0;
  354 + mBackWidth = 0;
  355 + return measuredWidth;
  356 + }
  357 + mThumbWidth = ceil(moveRange / mThumbRangeRatio);
  358 + mBackWidth = ceil(moveRange + mThumbMargin.left + mThumbMargin.right);
  359 + if (mBackWidth < 0) {
  360 + mThumbWidth = 0;
  361 + mBackWidth = 0;
  362 + return measuredWidth;
  363 + }
  364 + textExtraSpace = textWidth + mTextExtra - (moveRange - mThumbWidth + ceil(Math.max(mThumbMargin.left, mThumbMargin.right)));
  365 + if (textExtraSpace > 0) {
  366 + // since backWidth is determined by view width, so we can only reduce thumbSize.
  367 + mThumbWidth = mThumbWidth - textExtraSpace;
  368 + }
  369 + if (mThumbWidth < 0) {
  370 + mThumbWidth = 0;
  371 + mBackWidth = 0;
  372 + return measuredWidth;
  373 + }
  374 + }
  375 + } else {
  376 + /*
  377 + If parent view want SwitchButton to determine it's size itself, we calculate the minimal
  378 + size of it's content. Further more, we ignore the limitation of widthSize since we want
  379 + to display SwitchButton in its actual size rather than compress the shape.
  380 + */
  381 + if (mThumbWidth == 0) {
  382 + /*
  383 + If thumbWidth is not set, use the default one.
  384 + */
  385 + mThumbWidth = ceil(getResources().getDisplayMetrics().density * DEFAULT_THUMB_SIZE_DP);
  386 + }
  387 + if (mThumbRangeRatio == 0) {
  388 + mThumbRangeRatio = DEFAULT_THUMB_RANGE_RATIO;
  389 + }
  390 +
  391 + moveRange = ceil(mThumbWidth * mThumbRangeRatio);
  392 + textExtraSpace = ceil(textWidth + mTextExtra - (moveRange - mThumbWidth + Math.max(mThumbMargin.left, mThumbMargin.right) + mTextThumbInset));
  393 + mBackWidth = ceil(moveRange + mThumbMargin.left + mThumbMargin.right + Math.max(0, textExtraSpace));
  394 + if (mBackWidth < 0) {
  395 + mThumbWidth = 0;
  396 + mBackWidth = 0;
  397 + return measuredWidth;
  398 + }
  399 + contentSize = ceil(moveRange + Math.max(0, mThumbMargin.left) + Math.max(0, mThumbMargin.right) + Math.max(0, textExtraSpace));
  400 +
  401 + measuredWidth = Math.max(contentSize, contentSize + getPaddingLeft() + getPaddingRight());
  402 + }
  403 + return measuredWidth;
  404 + }
  405 +
  406 + private int measureHeight(int heightMeasureSpec) {
  407 + int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  408 + int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  409 + int measuredHeight = heightSize;
  410 +
  411 + if (mThumbHeight == 0 && mIsThumbUseDrawable) {
  412 + mThumbHeight = mThumbDrawable.getIntrinsicHeight();
  413 + }
  414 + int contentSize;
  415 + int textExtraSpace;
  416 + if (heightMode == MeasureSpec.EXACTLY) {
  417 + if (mThumbHeight != 0) {
  418 + /*
  419 + If thumbHeight has been set, we calculate backHeight and check if there is enough room.
  420 + */
  421 + mBackHeight = ceil(mThumbHeight + mThumbMargin.top + mThumbMargin.bottom);
  422 + mBackHeight = ceil(Math.max(mBackHeight, mTextHeight));
  423 + if (mBackHeight + getPaddingTop() + getPaddingBottom() - Math.min(0, mThumbMargin.top) - Math.min(0, mThumbMargin.bottom) > heightSize) {
  424 + // No enough room, we set thumbHeight to zero to calculate these value again.
  425 + mThumbHeight = 0;
  426 + }
  427 + }
  428 +
  429 + if (mThumbHeight == 0) {
  430 + mBackHeight = ceil(heightSize - getPaddingTop() - getPaddingBottom() + Math.min(0, mThumbMargin.top) + Math.min(0, mThumbMargin.bottom));
  431 + if (mBackHeight < 0) {
  432 + mBackHeight = 0;
  433 + mThumbHeight = 0;
  434 + return measuredHeight;
  435 + }
  436 + mThumbHeight = ceil(mBackHeight - mThumbMargin.top - mThumbMargin.bottom);
  437 + }
  438 + if (mThumbHeight < 0) {
  439 + mBackHeight = 0;
  440 + mThumbHeight = 0;
  441 + return measuredHeight;
  442 + }
  443 + } else {
  444 + if (mThumbHeight == 0) {
  445 + mThumbHeight = ceil(getResources().getDisplayMetrics().density * DEFAULT_THUMB_SIZE_DP);
  446 + }
  447 + mBackHeight = ceil(mThumbHeight + mThumbMargin.top + mThumbMargin.bottom);
  448 + if (mBackHeight < 0) {
  449 + mBackHeight = 0;
  450 + mThumbHeight = 0;
  451 + return measuredHeight;
  452 + }
  453 + textExtraSpace = ceil(mTextHeight - mBackHeight);
  454 + if (textExtraSpace > 0) {
  455 + mBackHeight += textExtraSpace;
  456 + mThumbHeight += textExtraSpace;
  457 + }
  458 + contentSize = Math.max(mThumbHeight, mBackHeight);
  459 +
  460 + measuredHeight = Math.max(contentSize, contentSize + getPaddingTop() + getPaddingBottom());
  461 + measuredHeight = Math.max(measuredHeight, getSuggestedMinimumHeight());
  462 + }
  463 +
  464 + return measuredHeight;
  465 + }
  466 +
  467 + @Override
  468 + protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  469 + super.onSizeChanged(w, h, oldw, oldh);
  470 + if (w != oldw || h != oldh) {
  471 + setup();
  472 + }
  473 + }
  474 +
  475 + private int ceil(double dimen) {
  476 + return (int) Math.ceil(dimen);
  477 + }
  478 +
  479 + /**
  480 + * set up the rect of back and thumb
  481 + */
  482 + private void setup() {
  483 + if (mThumbWidth == 0 || mThumbHeight == 0 || mBackWidth == 0 || mBackHeight == 0) {
  484 + return;
  485 + }
  486 +
  487 + if (mThumbRadius == -1) {
  488 + mThumbRadius = Math.min(mThumbWidth, mThumbHeight) / 2;
  489 + }
  490 + if (mBackRadius == -1) {
  491 + mBackRadius = Math.min(mBackWidth, mBackHeight) / 2;
  492 + }
  493 +
  494 + int contentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
  495 + int contentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
  496 +
  497 + // max range of drawing content, when thumbMargin is negative, drawing range is larger than backWidth
  498 + int drawingWidth = ceil(mBackWidth - Math.min(0, mThumbMargin.left) - Math.min(0, mThumbMargin.right));
  499 + int drawingHeight = ceil(mBackHeight - Math.min(0, mThumbMargin.top) - Math.min(0, mThumbMargin.bottom));
  500 +
  501 + float thumbTop;
  502 + if (contentHeight <= drawingHeight) {
  503 + thumbTop = getPaddingTop() + Math.max(0, mThumbMargin.top);
  504 + } else {
  505 + // center vertical in content area
  506 + thumbTop = getPaddingTop() + Math.max(0, mThumbMargin.top) + (contentHeight - drawingHeight + 1) / 2;
  507 + }
  508 +
  509 + float thumbLeft;
  510 + if (contentWidth <= mBackWidth) {
  511 + thumbLeft = getPaddingLeft() + Math.max(0, mThumbMargin.left);
  512 + } else {
  513 + thumbLeft = getPaddingLeft() + Math.max(0, mThumbMargin.left) + (contentWidth - drawingWidth + 1) / 2;
  514 + }
  515 +
  516 + mThumbRectF.set(thumbLeft, thumbTop, thumbLeft + mThumbWidth, thumbTop + mThumbHeight);
  517 +
  518 + float backLeft = mThumbRectF.left - mThumbMargin.left;
  519 + mBackRectF.set(backLeft,
  520 + mThumbRectF.top - mThumbMargin.top,
  521 + backLeft + mBackWidth,
  522 + mThumbRectF.top - mThumbMargin.top + mBackHeight);
  523 +
  524 + mSafeRectF.set(mThumbRectF.left, 0, mBackRectF.right - mThumbMargin.right - mThumbRectF.width(), 0);
  525 +
  526 + float minBackRadius = Math.min(mBackRectF.width(), mBackRectF.height()) / 2.f;
  527 + mBackRadius = Math.min(minBackRadius, mBackRadius);
  528 +
  529 + if (mBackDrawable != null) {
  530 + mBackDrawable.setBounds((int) mBackRectF.left, (int) mBackRectF.top, ceil(mBackRectF.right), ceil(mBackRectF.bottom));
  531 + }
  532 +
  533 + if (mOnLayout != null) {
  534 + float onLeft = mBackRectF.left + (mBackRectF.width() + mTextThumbInset - mThumbWidth - mThumbMargin.right - mOnLayout.getWidth()) / 2f - mTextAdjust;
  535 + float onTop = mBackRectF.top + (mBackRectF.height() - mOnLayout.getHeight()) / 2;
  536 + mTextOnRectF.set(onLeft, onTop, onLeft + mOnLayout.getWidth(), onTop + mOnLayout.getHeight());
  537 + }
  538 +
  539 + if (mOffLayout != null) {
  540 + float offLeft = mBackRectF.right - (mBackRectF.width() + mTextThumbInset - mThumbWidth - mThumbMargin.left - mOffLayout.getWidth()) / 2f - mOffLayout.getWidth() + mTextAdjust;
  541 + float offTop = mBackRectF.top + (mBackRectF.height() - mOffLayout.getHeight()) / 2;
  542 + mTextOffRectF.set(offLeft, offTop, offLeft + mOffLayout.getWidth(), offTop + mOffLayout.getHeight());
  543 + }
  544 +
  545 + mReady = true;
  546 + }
  547 +
  548 + @Override
  549 + protected void onDraw(Canvas canvas) {
  550 + super.onDraw(canvas);
  551 +
  552 + if (!mReady) {
  553 + setup();
  554 + }
  555 + if (!mReady) {
  556 + return;
  557 + }
  558 +
  559 + // fade back
  560 + if (mIsBackUseDrawable) {
  561 + if (mFadeBack && mCurrentBackDrawable != null && mNextBackDrawable != null) {
  562 + // fix #75, 70%A + 30%B != 30%B + 70%A, order matters when mix two layer of different alpha.
  563 + // So make sure the order of on/off layers never change during slide from one endpoint to another.
  564 + Drawable below = isChecked() ? mCurrentBackDrawable : mNextBackDrawable;
  565 + Drawable above = isChecked() ? mNextBackDrawable : mCurrentBackDrawable;
  566 +
  567 + int alpha = (int) (255 * getProgress());
  568 + below.setAlpha(alpha);
  569 + below.draw(canvas);
  570 + alpha = 255 - alpha;
  571 + above.setAlpha(alpha);
  572 + above.draw(canvas);
  573 + } else {
  574 + mBackDrawable.setAlpha(255);
  575 + mBackDrawable.draw(canvas);
  576 + }
  577 + } else {
  578 + if (mFadeBack) {
  579 + int alpha;
  580 + int colorAlpha;
  581 +
  582 + // fix #75
  583 + int belowColor = isChecked() ? mCurrBackColor : mNextBackColor;
  584 + int aboveColor = isChecked() ? mNextBackColor : mCurrBackColor;
  585 +
  586 + // curr back
  587 + alpha = (int) (255 * getProgress());
  588 + colorAlpha = Color.alpha(belowColor);
  589 + colorAlpha = colorAlpha * alpha / 255;
  590 + mPaint.setARGB(colorAlpha, Color.red(belowColor), Color.green(belowColor), Color.blue(belowColor));
  591 + canvas.drawRoundRect(mBackRectF, mBackRadius, mBackRadius, mPaint);
  592 +
  593 + // next back
  594 + alpha = 255 - alpha;
  595 + colorAlpha = Color.alpha(aboveColor);
  596 + colorAlpha = colorAlpha * alpha / 255;
  597 + mPaint.setARGB(colorAlpha, Color.red(aboveColor), Color.green(aboveColor), Color.blue(aboveColor));
  598 + canvas.drawRoundRect(mBackRectF, mBackRadius, mBackRadius, mPaint);
  599 +
  600 + mPaint.setAlpha(255);
  601 + } else {
  602 + mPaint.setColor(mCurrBackColor);
  603 + canvas.drawRoundRect(mBackRectF, mBackRadius, mBackRadius, mPaint);
  604 + }
  605 + }
  606 +
  607 + // text
  608 + Layout switchText = getProgress() > 0.5 ? mOnLayout : mOffLayout;
  609 + RectF textRectF = getProgress() > 0.5 ? mTextOnRectF : mTextOffRectF;
  610 + if (switchText != null && textRectF != null) {
  611 + int alpha = (int) (255 * (getProgress() >= 0.75 ? getProgress() * 4 - 3 : (getProgress() < 0.25 ? 1 - getProgress() * 4 : 0)));
  612 + int textColor = getProgress() > 0.5 ? mOnTextColor : mOffTextColor;
  613 + int colorAlpha = Color.alpha(textColor);
  614 + colorAlpha = colorAlpha * alpha / 255;
  615 + switchText.getPaint().setARGB(colorAlpha, Color.red(textColor), Color.green(textColor), Color.blue(textColor));
  616 + canvas.save();
  617 + canvas.translate(textRectF.left, textRectF.top);
  618 + switchText.draw(canvas);
  619 + canvas.restore();
  620 + }
  621 +
  622 + // thumb
  623 + mPresentThumbRectF.set(mThumbRectF);
  624 + mPresentThumbRectF.offset(mProgress * mSafeRectF.width(), 0);
  625 + if (mIsThumbUseDrawable) {
  626 + mThumbDrawable.setBounds((int) mPresentThumbRectF.left, (int) mPresentThumbRectF.top, ceil(mPresentThumbRectF.right), ceil(mPresentThumbRectF.bottom));
  627 + mThumbDrawable.draw(canvas);
  628 + } else {
  629 + mPaint.setColor(mCurrThumbColor);
  630 + canvas.drawRoundRect(mPresentThumbRectF, mThumbRadius, mThumbRadius, mPaint);
  631 + }
  632 +
  633 + if (mDrawDebugRect) {
  634 + mRectPaint.setColor(Color.parseColor("#AA0000"));
  635 + canvas.drawRect(mBackRectF, mRectPaint);
  636 + mRectPaint.setColor(Color.parseColor("#0000FF"));
  637 + canvas.drawRect(mPresentThumbRectF, mRectPaint);
  638 + mRectPaint.setColor(Color.parseColor("#000000"));
  639 + canvas.drawLine(mSafeRectF.left, mThumbRectF.top, mSafeRectF.right, mThumbRectF.top, mRectPaint);
  640 + mRectPaint.setColor(Color.parseColor("#00CC00"));
  641 + canvas.drawRect(getProgress() > 0.5 ? mTextOnRectF : mTextOffRectF, mRectPaint);
  642 + }
  643 + }
  644 +
  645 + @Override
  646 + protected void drawableStateChanged() {
  647 + super.drawableStateChanged();
  648 +
  649 + if (!mIsThumbUseDrawable && mThumbColor != null) {
  650 + mCurrThumbColor = mThumbColor.getColorForState(getDrawableState(), mCurrThumbColor);
  651 + } else {
  652 + setDrawableState(mThumbDrawable);
  653 + }
  654 +
  655 + int[] nextState = isChecked() ? UNCHECKED_PRESSED_STATE : CHECKED_PRESSED_STATE;
  656 + ColorStateList textColors = getTextColors();
  657 + if (textColors != null) {
  658 + int defaultTextColor = textColors.getDefaultColor();
  659 + mOnTextColor = textColors.getColorForState(CHECKED_PRESSED_STATE, defaultTextColor);
  660 + mOffTextColor = textColors.getColorForState(UNCHECKED_PRESSED_STATE, defaultTextColor);
  661 + }
  662 + if (!mIsBackUseDrawable && mBackColor != null) {
  663 + mCurrBackColor = mBackColor.getColorForState(getDrawableState(), mCurrBackColor);
  664 + mNextBackColor = mBackColor.getColorForState(nextState, mCurrBackColor);
  665 + } else {
  666 + if (mBackDrawable instanceof StateListDrawable && mFadeBack) {
  667 + mBackDrawable.setState(nextState);
  668 + mNextBackDrawable = mBackDrawable.getCurrent().mutate();
  669 + } else {
  670 + mNextBackDrawable = null;
  671 + }
  672 + setDrawableState(mBackDrawable);
  673 + if (mBackDrawable != null) {
  674 + mCurrentBackDrawable = mBackDrawable.getCurrent().mutate();
  675 + }
  676 + }
  677 + }
  678 +
  679 + @Override
  680 + public boolean onTouchEvent(MotionEvent event) {
  681 +
  682 + if (!isEnabled() || !isClickable() || !isFocusable() || !mReady) {
  683 + return false;
  684 + }
  685 +
  686 + int action = event.getAction();
  687 +
  688 + float deltaX = event.getX() - mStartX;
  689 + float deltaY = event.getY() - mStartY;
  690 +
  691 + switch (action) {
  692 + case MotionEvent.ACTION_DOWN:
  693 + mStartX = event.getX();
  694 + mStartY = event.getY();
  695 + mLastX = mStartX;
  696 + setPressed(true);
  697 + break;
  698 +
  699 + case MotionEvent.ACTION_MOVE:
  700 + float x = event.getX();
  701 + setProgress(getProgress() + (x - mLastX) / mSafeRectF.width());
  702 + if (!mCatch && (Math.abs(deltaX) > mTouchSlop / 2 || Math.abs(deltaY) > mTouchSlop / 2)) {
  703 + if (deltaY == 0 || Math.abs(deltaX) > Math.abs(deltaY)) {
  704 + catchView();
  705 + } else if (Math.abs(deltaY) > Math.abs(deltaX)) {
  706 + return false;
  707 + }
  708 + }
  709 + mLastX = x;
  710 + break;
  711 +
  712 + case MotionEvent.ACTION_CANCEL:
  713 + case MotionEvent.ACTION_UP:
  714 + mCatch = false;
  715 + setPressed(false);
  716 + float time = event.getEventTime() - event.getDownTime();
  717 + if (Math.abs(deltaX) < mTouchSlop && Math.abs(deltaY) < mTouchSlop && time < mClickTimeout) {
  718 + performClick();
  719 + } else {
  720 + boolean nextStatus = getStatusBasedOnPos();
  721 + if (nextStatus != isChecked()) {
  722 + playSoundEffect(SoundEffectConstants.CLICK);
  723 + setChecked(nextStatus);
  724 + } else {
  725 + animateToState(nextStatus);
  726 + }
  727 + }
  728 + break;
  729 +
  730 + default:
  731 + break;
  732 + }
  733 + return true;
  734 + }
  735 +
  736 +
  737 + /**
  738 + * return the status based on position of thumb
  739 + *
  740 + * @return whether checked or not
  741 + */
  742 + private boolean getStatusBasedOnPos() {
  743 + return getProgress() > 0.5f;
  744 + }
  745 +
  746 + private float getProgress() {
  747 + return mProgress;
  748 + }
  749 +
  750 + private void setProgress(final float progress) {
  751 + float tp = progress;
  752 + if (tp > 1) {
  753 + tp = 1;
  754 + } else if (tp < 0) {
  755 + tp = 0;
  756 + }
  757 + this.mProgress = tp;
  758 + invalidate();
  759 + }
  760 +
  761 + @Override
  762 + public boolean performClick() {
  763 + return super.performClick();
  764 + }
  765 +
  766 + /**
  767 + * processing animation
  768 + *
  769 + * @param checked checked or unChecked
  770 + */
  771 + protected void animateToState(boolean checked) {
  772 + if (mProgressAnimator == null) {
  773 + return;
  774 + }
  775 + if (mProgressAnimator.isRunning()) {
  776 + mProgressAnimator.cancel();
  777 + }
  778 + mProgressAnimator.setDuration(mAnimationDuration);
  779 + if (checked) {
  780 + mProgressAnimator.setFloatValues(mProgress, 1f);
  781 + } else {
  782 + mProgressAnimator.setFloatValues(mProgress, 0);
  783 + }
  784 + mProgressAnimator.start();
  785 + }
  786 +
  787 + private void catchView() {
  788 + ViewParent parent = getParent();
  789 + if (parent != null) {
  790 + parent.requestDisallowInterceptTouchEvent(true);
  791 + }
  792 + mCatch = true;
  793 + }
  794 +
  795 + @Override
  796 + public void setChecked(final boolean checked) {
  797 + // animate before super.setChecked() become user may call setChecked again in OnCheckedChangedListener
  798 + if (isChecked() != checked) {
  799 + animateToState(checked);
  800 + }
  801 + if (mRestoring) {
  802 + setCheckedImmediatelyNoEvent(checked);
  803 + } else {
  804 + super.setChecked(checked);
  805 + }
  806 + }
  807 +
  808 + public void setCheckedNoEvent(final boolean checked) {
  809 + if (mChildOnCheckedChangeListener == null) {
  810 + setChecked(checked);
  811 + } else {
  812 + super.setOnCheckedChangeListener(null);
  813 + setChecked(checked);
  814 + super.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  815 + }
  816 + }
  817 +
  818 + public void setCheckedImmediatelyNoEvent(boolean checked) {
  819 + if (mChildOnCheckedChangeListener == null) {
  820 + setCheckedImmediately(checked);
  821 + } else {
  822 + super.setOnCheckedChangeListener(null);
  823 + setCheckedImmediately(checked);
  824 + super.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  825 + }
  826 + }
  827 +
  828 + public void toggleNoEvent() {
  829 + if (mChildOnCheckedChangeListener == null) {
  830 + toggle();
  831 + } else {
  832 + super.setOnCheckedChangeListener(null);
  833 + toggle();
  834 + super.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  835 + }
  836 + }
  837 +
  838 + public void toggleImmediatelyNoEvent() {
  839 + if (mChildOnCheckedChangeListener == null) {
  840 + toggleImmediately();
  841 + } else {
  842 + super.setOnCheckedChangeListener(null);
  843 + toggleImmediately();
  844 + super.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
  845 + }
  846 + }
  847 +
  848 + @Override
  849 + public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
  850 + super.setOnCheckedChangeListener(onCheckedChangeListener);
  851 + mChildOnCheckedChangeListener = onCheckedChangeListener;
  852 + }
  853 +
  854 + public void setCheckedImmediately(boolean checked) {
  855 + super.setChecked(checked);
  856 + if (mProgressAnimator != null && mProgressAnimator.isRunning()) {
  857 + mProgressAnimator.cancel();
  858 + }
  859 + setProgress(checked ? 1 : 0);
  860 + invalidate();
  861 + }
  862 +
  863 + public void toggleImmediately() {
  864 + setCheckedImmediately(!isChecked());
  865 + }
  866 +
  867 + private void setDrawableState(Drawable drawable) {
  868 + if (drawable != null) {
  869 + int[] myDrawableState = getDrawableState();
  870 + drawable.setState(myDrawableState);
  871 + invalidate();
  872 + }
  873 + }
  874 +
  875 + public boolean isDrawDebugRect() {
  876 + return mDrawDebugRect;
  877 + }
  878 +
  879 + public void setDrawDebugRect(boolean drawDebugRect) {
  880 + mDrawDebugRect = drawDebugRect;
  881 + invalidate();
  882 + }
  883 +
  884 + public long getAnimationDuration() {
  885 + return mAnimationDuration;
  886 + }
  887 +
  888 + public void setAnimationDuration(long animationDuration) {
  889 + mAnimationDuration = animationDuration;
  890 + }
  891 +
  892 + public Drawable getThumbDrawable() {
  893 + return mThumbDrawable;
  894 + }
  895 +
  896 + public void setThumbDrawable(Drawable thumbDrawable) {
  897 + mThumbDrawable = thumbDrawable;
  898 + mIsThumbUseDrawable = mThumbDrawable != null;
  899 + refreshDrawableState();
  900 + mReady = false;
  901 + requestLayout();
  902 + invalidate();
  903 + }
  904 +
  905 + public void setThumbDrawableRes(int thumbDrawableRes) {
  906 + setThumbDrawable(ContextCompat.getDrawable(getContext(), thumbDrawableRes));
  907 + }
  908 +
  909 + public Drawable getBackDrawable() {
  910 + return mBackDrawable;
  911 + }
  912 +
  913 + public void setBackDrawable(Drawable backDrawable) {
  914 + mBackDrawable = backDrawable;
  915 + mIsBackUseDrawable = mBackDrawable != null;
  916 + refreshDrawableState();
  917 + mReady = false;
  918 + requestLayout();
  919 + invalidate();
  920 + }
  921 +
  922 + public void setBackDrawableRes(int backDrawableRes) {
  923 + setBackDrawable(ContextCompat.getDrawable(getContext(), backDrawableRes));
  924 + }
  925 +
  926 + public ColorStateList getBackColor() {
  927 + return mBackColor;
  928 + }
  929 +
  930 + public void setBackColor(ColorStateList backColor) {
  931 + mBackColor = backColor;
  932 + if (mBackColor != null) {
  933 + setBackDrawable(null);
  934 + }
  935 + invalidate();
  936 + }
  937 +
  938 + public void setBackColorRes(int backColorRes) {
  939 + setBackColor(ContextCompat.getColorStateList(getContext(), backColorRes));
  940 + }
  941 +
  942 + public ColorStateList getThumbColor() {
  943 + return mThumbColor;
  944 + }
  945 +
  946 + public void setThumbColor(ColorStateList thumbColor) {
  947 + mThumbColor = thumbColor;
  948 + if (mThumbColor != null) {
  949 + setThumbDrawable(null);
  950 + }
  951 + invalidate();
  952 + }
  953 +
  954 + public void setThumbColorRes(int thumbColorRes) {
  955 + setThumbColor(ContextCompat.getColorStateList(getContext(), thumbColorRes));
  956 + }
  957 +
  958 + public float getThumbRangeRatio() {
  959 + return mThumbRangeRatio;
  960 + }
  961 +
  962 + public void setThumbRangeRatio(float thumbRangeRatio) {
  963 + mThumbRangeRatio = thumbRangeRatio;
  964 + // We need to mark "ready" to false since requestLayout may not cause size changed.
  965 + mReady = false;
  966 + requestLayout();
  967 + }
  968 +
  969 + public RectF getThumbMargin() {
  970 + return mThumbMargin;
  971 + }
  972 +
  973 + public void setThumbMargin(RectF thumbMargin) {
  974 + if (thumbMargin == null) {
  975 + setThumbMargin(0, 0, 0, 0);
  976 + } else {
  977 + setThumbMargin(thumbMargin.left, thumbMargin.top, thumbMargin.right, thumbMargin.bottom);
  978 + }
  979 + }
  980 +
  981 + public void setThumbMargin(float left, float top, float right, float bottom) {
  982 + mThumbMargin.set(left, top, right, bottom);
  983 + mReady = false;
  984 + requestLayout();
  985 + }
  986 +
  987 + public void setThumbSize(int width, int height) {
  988 + mThumbWidth = width;
  989 + mThumbHeight = height;
  990 + mReady = false;
  991 + requestLayout();
  992 + }
  993 +
  994 + public float getThumbWidth() {
  995 + return mThumbWidth;
  996 + }
  997 +
  998 + public float getThumbHeight() {
  999 + return mThumbHeight;
  1000 + }
  1001 +
  1002 + public float getThumbRadius() {
  1003 + return mThumbRadius;
  1004 + }
  1005 +
  1006 + public void setThumbRadius(float thumbRadius) {
  1007 + mThumbRadius = thumbRadius;
  1008 + if (!mIsThumbUseDrawable) {
  1009 + invalidate();
  1010 + }
  1011 + }
  1012 +
  1013 + public PointF getBackSizeF() {
  1014 + return new PointF(mBackRectF.width(), mBackRectF.height());
  1015 + }
  1016 +
  1017 + public float getBackRadius() {
  1018 + return mBackRadius;
  1019 + }
  1020 +
  1021 + public void setBackRadius(float backRadius) {
  1022 + mBackRadius = backRadius;
  1023 + if (!mIsBackUseDrawable) {
  1024 + invalidate();
  1025 + }
  1026 + }
  1027 +
  1028 + public boolean isFadeBack() {
  1029 + return mFadeBack;
  1030 + }
  1031 +
  1032 + public void setFadeBack(boolean fadeBack) {
  1033 + mFadeBack = fadeBack;
  1034 + }
  1035 +
  1036 + public int getTintColor() {
  1037 + return mTintColor;
  1038 + }
  1039 +
  1040 + public void setTintColor(@SuppressWarnings("SameParameterValue") int tintColor) {
  1041 + mTintColor = tintColor;
  1042 + mThumbColor = ColorUtils.generateThumbColorWithTintColor(mTintColor);
  1043 + mBackColor = ColorUtils.generateBackColorWithTintColor(mTintColor);
  1044 + mIsBackUseDrawable = false;
  1045 + mIsThumbUseDrawable = false;
  1046 + // call this method to refresh color states
  1047 + refreshDrawableState();
  1048 + invalidate();
  1049 + }
  1050 +
  1051 + public void setText(CharSequence onText, CharSequence offText) {
  1052 + mTextOn = onText;
  1053 + mTextOff = offText;
  1054 +
  1055 + mOnLayout = null;
  1056 + mOffLayout = null;
  1057 +
  1058 + mReady = false;
  1059 + requestLayout();
  1060 + invalidate();
  1061 + }
  1062 +
  1063 + public CharSequence getTextOn() {
  1064 + return mTextOn;
  1065 + }
  1066 +
  1067 + public CharSequence getTextOff() {
  1068 + return mTextOff;
  1069 + }
  1070 +
  1071 + public void setTextThumbInset(int textThumbInset) {
  1072 + mTextThumbInset = textThumbInset;
  1073 + mReady = false;
  1074 + requestLayout();
  1075 + invalidate();
  1076 + }
  1077 +
  1078 + public void setTextExtra(int textExtra) {
  1079 + mTextExtra = textExtra;
  1080 + mReady = false;
  1081 + requestLayout();
  1082 + invalidate();
  1083 + }
  1084 +
  1085 + public void setTextAdjust(int textAdjust) {
  1086 + mTextAdjust = textAdjust;
  1087 + mReady = false;
  1088 + requestLayout();
  1089 + invalidate();
  1090 + }
  1091 +
  1092 + @Override
  1093 + public Parcelable onSaveInstanceState() {
  1094 + Parcelable superState = super.onSaveInstanceState();
  1095 + SavedState ss = new SavedState(superState);
  1096 + ss.onText = mTextOn;
  1097 + ss.offText = mTextOff;
  1098 + return ss;
  1099 + }
  1100 +
  1101 + @Override
  1102 + public void onRestoreInstanceState(Parcelable state) {
  1103 + SavedState ss = (SavedState) state;
  1104 + setText(ss.onText, ss.offText);
  1105 + mRestoring = true;
  1106 + super.onRestoreInstanceState(ss.getSuperState());
  1107 + mRestoring = false;
  1108 + }
  1109 +
  1110 + static class SavedState extends BaseSavedState {
  1111 + CharSequence onText;
  1112 + CharSequence offText;
  1113 +
  1114 + SavedState(Parcelable superState) {
  1115 + super(superState);
  1116 + }
  1117 +
  1118 + private SavedState(Parcel in) {
  1119 + super(in);
  1120 + onText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
  1121 + offText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
  1122 + }
  1123 +
  1124 + @Override
  1125 + public void writeToParcel(Parcel out, int flags) {
  1126 + super.writeToParcel(out, flags);
  1127 + TextUtils.writeToParcel(onText, out, flags);
  1128 + TextUtils.writeToParcel(offText, out, flags);
  1129 + }
  1130 +
  1131 + public static final Creator<SavedState> CREATOR
  1132 + = new Creator<SavedState>() {
  1133 + public SavedState createFromParcel(Parcel in) {
  1134 + return new SavedState(in);
  1135 + }
  1136 +
  1137 + public SavedState[] newArray(int size) {
  1138 + return new SavedState[size];
  1139 + }
  1140 + };
  1141 + }
  1142 +}
0 1143 \ No newline at end of file
... ...
core/base/src/main/java/com/kongzue/dialog/v3/BottomMenu.java
... ... @@ -48,18 +48,18 @@ import static com.kongzue.dialog.util.DialogSettings.menuTitleInfo;
48 48 * CreateTime: 2019/4/12 18:33
49 49 */
50 50 public class BottomMenu extends BaseDialog {
51   -
  51 +
52 52 private BaseAdapter customAdapter; //允许用户自定义 Menu 的 Adapter
53   -
  53 +
54 54 private List<String> menuTextList;
55 55 private String title;
56 56 private String cancelButtonText = DialogSettings.defaultCancelButtonText;
57 57 private boolean showCancelButton = true;
58 58 private OnMenuItemClickListener onMenuItemClickListener;
59   -
  59 +
60 60 private TextInfo menuTitleTextInfo;
61 61 private TextInfo cancelButtonTextInfo;
62   -
  62 +
63 63 private LinearLayout boxBody;
64 64 private RelativeLayout boxList;
65 65 private TextView txtTitle;
... ... @@ -69,16 +69,16 @@ public class BottomMenu extends BaseDialog {
69 69 private ViewGroup boxCancel;
70 70 private TextView btnCancel;
71 71 private TextInfo menuTextInfo;
72   -
  72 +
73 73 private BottomMenu() {
74 74 }
75   -
  75 +
76 76 public static BottomMenu build(@NonNull AppCompatActivity context) {
77 77 synchronized (BottomMenu.class) {
78 78 BottomMenu bottomMenu = new BottomMenu();
79 79 bottomMenu.log("装载底部菜单: " + bottomMenu.toString());
80 80 bottomMenu.context = new WeakReference<AppCompatActivity>(context);
81   -
  81 +
82 82 switch (bottomMenu.style) {
83 83 case STYLE_IOS:
84 84 bottomMenu.build(bottomMenu, R.layout.bottom_menu_ios);
... ... @@ -93,7 +93,7 @@ public class BottomMenu extends BaseDialog {
93 93 return bottomMenu;
94 94 }
95 95 }
96   -
  96 +
97 97 public static BottomMenu show(@NonNull AppCompatActivity context, List<String> menuTextList, OnMenuItemClickListener onMenuItemClickListener) {
98 98 BottomMenu bottomMenu = build(context);
99 99 bottomMenu.menuTextList = menuTextList;
... ... @@ -101,7 +101,7 @@ public class BottomMenu extends BaseDialog {
101 101 bottomMenu.showDialog();
102 102 return bottomMenu;
103 103 }
104   -
  104 +
105 105 public static BottomMenu show(@NonNull AppCompatActivity context, BaseAdapter customAdapter, OnMenuItemClickListener onMenuItemClickListener) {
106 106 BottomMenu bottomMenu = build(context);
107 107 bottomMenu.customAdapter = customAdapter;
... ... @@ -109,7 +109,7 @@ public class BottomMenu extends BaseDialog {
109 109 bottomMenu.showDialog();
110 110 return bottomMenu;
111 111 }
112   -
  112 +
113 113 public static BottomMenu show(@NonNull AppCompatActivity context, String title, List<String> menuTextList, OnMenuItemClickListener onMenuItemClickListener) {
114 114 BottomMenu bottomMenu = build(context);
115 115 bottomMenu.menuTextList = menuTextList;
... ... @@ -118,7 +118,7 @@ public class BottomMenu extends BaseDialog {
118 118 bottomMenu.showDialog();
119 119 return bottomMenu;
120 120 }
121   -
  121 +
122 122 public static BottomMenu show(@NonNull AppCompatActivity context, String title, BaseAdapter customAdapter, OnMenuItemClickListener onMenuItemClickListener) {
123 123 BottomMenu bottomMenu = build(context);
124 124 bottomMenu.customAdapter = customAdapter;
... ... @@ -127,7 +127,7 @@ public class BottomMenu extends BaseDialog {
127 127 bottomMenu.showDialog();
128 128 return bottomMenu;
129 129 }
130   -
  130 +
131 131 public static BottomMenu show(@NonNull AppCompatActivity context, String[] menuTexts, OnMenuItemClickListener onMenuItemClickListener) {
132 132 BottomMenu bottomMenu = build(context);
133 133 List<String> list = new ArrayList<>();
... ... @@ -139,7 +139,7 @@ public class BottomMenu extends BaseDialog {
139 139 bottomMenu.showDialog();
140 140 return bottomMenu;
141 141 }
142   -
  142 +
143 143 public static BottomMenu show(@NonNull AppCompatActivity context, String title, String[] menuTexts, OnMenuItemClickListener onMenuItemClickListener) {
144 144 BottomMenu bottomMenu = build(context);
145 145 List<String> list = new ArrayList<>();
... ... @@ -152,9 +152,9 @@ public class BottomMenu extends BaseDialog {
152 152 bottomMenu.showDialog();
153 153 return bottomMenu;
154 154 }
155   -
  155 +
156 156 private View rootView;
157   -
  157 +
158 158 @Override
159 159 public void bindView(View rootView) {
160 160 log("启动底部菜单 -> " + toString());
... ... @@ -168,10 +168,10 @@ public class BottomMenu extends BaseDialog {
168 168 listMenu = rootView.findViewById(R.id.list_menu);
169 169 boxCancel = rootView.findViewById(R.id.box_cancel);
170 170 btnCancel = rootView.findViewById(R.id.btn_cancel);
171   -
  171 +
172 172 switch (style) {
173 173 case STYLE_MATERIAL:
174   - boxCancel.setVisibility(View.GONE);
  174 +// boxCancel.setVisibility(View.GONE);
175 175 boxBody.setY(getRootHeight());
176 176 boxBody.setVisibility(View.VISIBLE);
177 177 boxBody.post(new Runnable() {
... ... @@ -187,7 +187,7 @@ public class BottomMenu extends BaseDialog {
187 187 });
188 188 listMenu.setOnTouchListener(listViewTouchListener);
189 189 boxBody.setOnTouchListener(listViewTouchListener);
190   -
  190 +
191 191 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
192 192 Window window = dialog.get().getDialog().getWindow();
193 193 window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
... ... @@ -197,6 +197,9 @@ public class BottomMenu extends BaseDialog {
197 197 }
198 198 break;
199 199 case STYLE_KONGZUE:
  200 + if (null != menuTextInfo) {
  201 + btnCancel.setTextSize(menuTextInfo.getFontSize());
  202 + }
200 203 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
201 204 Window window = dialog.get().getDialog().getWindow();
202 205 window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
... ... @@ -204,7 +207,7 @@ public class BottomMenu extends BaseDialog {
204 207 window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
205 208 window.setNavigationBarColor(Color.WHITE);
206 209 boxBody.setPadding(0, 0, 0, getNavigationBarHeight());
207   -
  210 +
208 211 //设置底部导航栏按钮暗色,无效,悬赏解决————
209 212 View decorView = window.getDecorView();
210 213 int vis = decorView.getSystemUiVisibility();
... ... @@ -216,6 +219,9 @@ public class BottomMenu extends BaseDialog {
216 219 break;
217 220 case STYLE_IOS:
218 221 final int bkgResId, blurFrontColor;
  222 + if (null != menuTextInfo) {
  223 + btnCancel.setTextSize(menuTextInfo.getFontSize());
  224 + }
219 225 if (theme == DialogSettings.THEME.LIGHT) {
220 226 bkgResId = R.drawable.rect_menu_bkg_ios;
221 227 blurFrontColor = Color.argb(blurAlpha, 244, 245, 246);
... ... @@ -258,31 +264,31 @@ public class BottomMenu extends BaseDialog {
258 264 }
259 265 break;
260 266 }
261   -
  267 +
262 268 refreshView();
263 269 if (onShowListener != null) onShowListener.onShow(this);
264 270 }
265   -
  271 +
266 272 private BlurView blurList;
267 273 private BlurView blurCancel;
268   -
  274 +
269 275 @Override
270 276 public void refreshView() {
271 277 if (cancelButtonTextInfo == null) cancelButtonTextInfo = menuTextInfo;
272 278 if (menuTitleTextInfo == null) menuTitleTextInfo = menuTitleInfo;
273 279 if (menuTextInfo == null) menuTextInfo = DialogSettings.menuTextInfo;
274 280 if (cancelButtonText == null) cancelButtonText = "取消";
275   -
  281 +
276 282 if (rootView != null) {
277 283 btnCancel.setText(cancelButtonText);
278   -
  284 +
279 285 ((ViewGroup) boxBody.getParent()).setOnClickListener(new View.OnClickListener() {
280 286 @Override
281 287 public void onClick(View v) {
282 288 doDismiss();
283 289 }
284 290 });
285   -
  291 +
286 292 switch (style) {
287 293 case STYLE_MATERIAL:
288 294 if (customAdapter != null) {
... ... @@ -317,7 +323,7 @@ public class BottomMenu extends BaseDialog {
317 323 menuArrayAdapter = new IOSMenuArrayAdapter(context.get(), R.layout.item_bottom_menu_ios, menuTextList);
318 324 }
319 325 listMenu.setAdapter(menuArrayAdapter);
320   -
  326 +
321 327 break;
322 328 }
323 329 if (customView != null) {
... ... @@ -329,13 +335,13 @@ public class BottomMenu extends BaseDialog {
329 335 } else {
330 336 boxCustom.setVisibility(View.GONE);
331 337 }
332   -
  338 +
333 339 if (!isNull(title)) {
334 340 txtTitle.setText(title);
335 341 txtTitle.setVisibility(View.VISIBLE);
336 342 if (titleSplitLine != null) titleSplitLine.setVisibility(View.VISIBLE);
337 343 }
338   -
  344 +
339 345 listMenu.setOnItemClickListener(new AdapterView.OnItemClickListener() {
340 346 @Override
341 347 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
... ... @@ -349,7 +355,7 @@ public class BottomMenu extends BaseDialog {
349 355 doDismiss();
350 356 }
351 357 });
352   -
  358 +
353 359 btnCancel.setOnClickListener(new View.OnClickListener() {
354 360 @Override
355 361 public void onClick(View v) {
... ... @@ -357,24 +363,24 @@ public class BottomMenu extends BaseDialog {
357 363 }
358 364 });
359 365 }
360   -
  366 +
361 367 useTextInfo(txtTitle, menuTitleTextInfo);
362 368 useTextInfo(btnCancel, cancelButtonTextInfo);
363 369 }
364   -
  370 +
365 371 @Override
366 372 public void show() {
367 373 showDialog();
368 374 }
369   -
  375 +
370 376 private BaseAdapter menuArrayAdapter;
371   -
  377 +
372 378 public class IOSMenuArrayAdapter extends NormalMenuArrayAdapter {
373   -
  379 +
374 380 public IOSMenuArrayAdapter(Context context, int resourceId, List<String> objects) {
375 381 super(context, resourceId, objects);
376 382 }
377   -
  383 +
378 384 @Override
379 385 public View getView(int position, View convertView, ViewGroup parent) {
380 386 ViewHolder viewHolder = null;
... ... @@ -390,9 +396,9 @@ public class BottomMenu extends BaseDialog {
390 396 String text = objects.get(position);
391 397 if (null != text) {
392 398 viewHolder.textView.setText(text);
393   -
  399 +
394 400 useTextInfo(viewHolder.textView, menuTextInfo);
395   -
  401 +
396 402 if (objects.size() == 1) {
397 403 if (theme == DialogSettings.THEME.LIGHT) {
398 404 if (title != null && !title.trim().isEmpty()) {
... ... @@ -451,42 +457,42 @@ public class BottomMenu extends BaseDialog {
451 457 }
452 458 }
453 459 }
454   -
  460 +
455 461 return convertView;
456 462 }
457 463 }
458   -
  464 +
459 465 public class NormalMenuArrayAdapter extends ArrayAdapter {
460 466 public int resoureId;
461 467 public List<String> objects;
462 468 public Context context;
463   -
  469 +
464 470 public NormalMenuArrayAdapter(Context context, int resourceId, List<String> objects) {
465 471 super(context, resourceId, objects);
466 472 this.objects = objects;
467 473 this.resoureId = resourceId;
468 474 this.context = context;
469 475 }
470   -
  476 +
471 477 public class ViewHolder {
472 478 TextView textView;
473 479 }
474   -
  480 +
475 481 @Override
476 482 public int getCount() {
477 483 return objects.size();
478 484 }
479   -
  485 +
480 486 @Override
481 487 public String getItem(int position) {
482 488 return objects.get(position);
483 489 }
484   -
  490 +
485 491 @Override
486 492 public long getItemId(int position) {
487 493 return position;
488 494 }
489   -
  495 +
490 496 @Override
491 497 public View getView(int position, View convertView, ViewGroup parent) {
492 498 ViewHolder viewHolder = null;
... ... @@ -502,25 +508,25 @@ public class BottomMenu extends BaseDialog {
502 508 String text = objects.get(position);
503 509 if (null != text) {
504 510 viewHolder.textView.setText(text);
505   -
  511 +
506 512 useTextInfo(viewHolder.textView, menuTextInfo);
507 513 }
508   -
  514 +
509 515 return convertView;
510 516 }
511 517 }
512   -
  518 +
513 519 //其他设置
514 520 public List<String> getMenuTextList() {
515 521 return menuTextList;
516 522 }
517   -
  523 +
518 524 public BottomMenu setMenuTextList(List<String> menuTextList) {
519 525 this.menuTextList = menuTextList;
520 526 refreshView();
521 527 return this;
522 528 }
523   -
  529 +
524 530 public BottomMenu setMenuTextList(String[] menuTexts) {
525 531 List<String> list = new ArrayList<>();
526 532 for (String s : menuTexts) {
... ... @@ -530,150 +536,150 @@ public class BottomMenu extends BaseDialog {
530 536 refreshView();
531 537 return this;
532 538 }
533   -
  539 +
534 540 public String getTitle() {
535 541 return title;
536 542 }
537   -
  543 +
538 544 public BottomMenu setTitle(String title) {
539 545 this.title = title;
540 546 refreshView();
541 547 return this;
542 548 }
543   -
  549 +
544 550 public BottomMenu setTitle(int titleResId) {
545 551 this.title = context.get().getString(titleResId);
546 552 refreshView();
547 553 return this;
548 554 }
549   -
  555 +
550 556 public String getCancelButtonText() {
551 557 return cancelButtonText;
552 558 }
553   -
  559 +
554 560 public BottomMenu setCancelButtonText(String cancelButtonText) {
555 561 this.cancelButtonText = cancelButtonText;
556 562 refreshView();
557 563 return this;
558 564 }
559   -
  565 +
560 566 public BottomMenu setCancelButtonText(int cancelButtonTextResId) {
561 567 this.cancelButtonText = context.get().getString(cancelButtonTextResId);
562 568 refreshView();
563 569 return this;
564 570 }
565   -
  571 +
566 572 public boolean isShowCancelButton() {
567 573 return showCancelButton;
568 574 }
569   -
  575 +
570 576 public BottomMenu setShowCancelButton(boolean showCancelButton) {
571 577 this.showCancelButton = showCancelButton;
572 578 refreshView();
573 579 return this;
574 580 }
575   -
  581 +
576 582 public OnMenuItemClickListener getOnMenuItemClickListener() {
577 583 return onMenuItemClickListener;
578 584 }
579   -
  585 +
580 586 public BottomMenu setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) {
581 587 this.onMenuItemClickListener = onMenuItemClickListener;
582 588 refreshView();
583 589 return this;
584 590 }
585   -
  591 +
586 592 public TextInfo getMenuTitleTextInfo() {
587 593 return menuTitleTextInfo;
588 594 }
589   -
  595 +
590 596 public BottomMenu setMenuTitleTextInfo(TextInfo menuTitleTextInfo) {
591 597 this.menuTitleTextInfo = menuTitleTextInfo;
592 598 refreshView();
593 599 return this;
594 600 }
595   -
  601 +
596 602 public TextInfo getMenuTextInfo() {
597 603 return menuTextInfo;
598 604 }
599   -
  605 +
600 606 public BottomMenu setMenuTextInfo(TextInfo menuTextInfo) {
601 607 this.menuTextInfo = menuTextInfo;
602 608 refreshView();
603 609 return this;
604 610 }
605   -
  611 +
606 612 public TextInfo getCancelButtonTextInfo() {
607 613 return cancelButtonTextInfo;
608 614 }
609   -
  615 +
610 616 public BottomMenu setCancelButtonTextInfo(TextInfo cancelButtonTextInfo) {
611 617 this.cancelButtonTextInfo = cancelButtonTextInfo;
612 618 refreshView();
613 619 return this;
614 620 }
615   -
  621 +
616 622 public OnDismissListener getOnDismissListener() {
617 623 return onDismissListener == null ? new OnDismissListener() {
618 624 @Override
619 625 public void onDismiss() {
620   -
  626 +
621 627 }
622 628 } : onDismissListener;
623 629 }
624   -
  630 +
625 631 public BottomMenu setOnDismissListener(OnDismissListener onDismissListener) {
626 632 this.onDismissListener = onDismissListener;
627 633 return this;
628 634 }
629   -
  635 +
630 636 public OnShowListener getOnShowListener() {
631 637 return onShowListener == null ? new OnShowListener() {
632 638 @Override
633 639 public void onShow(BaseDialog dialog) {
634   -
  640 +
635 641 }
636 642 } : onShowListener;
637 643 }
638   -
  644 +
639 645 public BottomMenu setOnShowListener(OnShowListener onShowListener) {
640 646 this.onShowListener = onShowListener;
641 647 return this;
642 648 }
643   -
  649 +
644 650 public View getCustomView() {
645 651 return customView;
646 652 }
647   -
  653 +
648 654 public BottomMenu setCustomView(View customView) {
649 655 this.customView = customView;
650 656 refreshView();
651 657 return this;
652 658 }
653   -
  659 +
654 660 private OnBindView onBindView;
655   -
  661 +
656 662 public BottomMenu setCustomView(int customViewLayoutId, OnBindView onBindView) {
657 663 customView = LayoutInflater.from(context.get()).inflate(customViewLayoutId, null);
658 664 this.onBindView = onBindView;
659 665 refreshView();
660 666 return this;
661 667 }
662   -
  668 +
663 669 public interface OnBindView {
664 670 void onBind(BottomMenu bottomMenu, View v);
665 671 }
666   -
  672 +
667 673 public DialogSettings.STYLE getStyle() {
668 674 return style;
669 675 }
670   -
  676 +
671 677 public BottomMenu setStyle(DialogSettings.STYLE style) {
672 678 if (isAlreadyShown) {
673 679 error("必须使用 build(...) 方法创建时,才可以使用 setStyle(...) 来修改对话框主题或风格。");
674 680 return this;
675 681 }
676   -
  682 +
677 683 this.style = style;
678 684 switch (this.style) {
679 685 case STYLE_IOS:
... ... @@ -686,40 +692,40 @@ public class BottomMenu extends BaseDialog {
686 692 build(this, R.layout.bottom_menu_material);
687 693 break;
688 694 }
689   -
  695 +
690 696 return this;
691 697 }
692   -
  698 +
693 699 public DialogSettings.THEME getTheme() {
694 700 return theme;
695 701 }
696   -
  702 +
697 703 public BottomMenu setTheme(DialogSettings.THEME theme) {
698   -
  704 +
699 705 if (isAlreadyShown) {
700 706 error("必须使用 build(...) 方法创建时,才可以使用 setTheme(...) 来修改对话框主题或风格。");
701 707 return this;
702 708 }
703   -
  709 +
704 710 this.theme = theme;
705 711 refreshView();
706 712 return this;
707 713 }
708   -
  714 +
709 715 public BaseAdapter getCustomAdapter() {
710 716 return customAdapter;
711 717 }
712   -
  718 +
713 719 public BottomMenu setCustomAdapter(BaseAdapter customAdapter) {
714 720 this.customAdapter = customAdapter;
715 721 return this;
716 722 }
717   -
  723 +
718 724 private float boxBodyOldY;
719 725 private int step;
720 726 private boolean isTouchDown;
721 727 private float touchDownY;
722   -
  728 +
723 729 private View.OnTouchListener listViewTouchListener = new View.OnTouchListener() {
724 730 @Override
725 731 public boolean onTouch(View v, MotionEvent event) {
... ... @@ -749,7 +755,7 @@ public class BottomMenu extends BaseDialog {
749 755 case MotionEvent.ACTION_CANCEL:
750 756 if (isTouchDown) {
751 757 float deltaY = boxBody.getY() - boxBodyOldY;
752   -
  758 +
753 759 if (deltaY >= -dip2px(50) && deltaY <= dip2px(50)) {
754 760 //几乎没动,回到原来位置
755 761 boxBody.animate().setDuration(300).translationY(boxBodyOldY);
... ... @@ -802,7 +808,7 @@ public class BottomMenu extends BaseDialog {
802 808 }
803 809 }
804 810 };
805   -
  811 +
806 812 private boolean isListViewOnTop() {
807 813 View c = listMenu.getChildAt(0);
808 814 if (c == null) {
... ... @@ -815,7 +821,7 @@ public class BottomMenu extends BaseDialog {
815 821 int top = c.getTop();
816 822 return top == 0;
817 823 }
818   -
  824 +
819 825 public BottomMenu setCustomDialogStyleId(int customDialogStyleId) {
820 826 if (isAlreadyShown) {
821 827 error("必须使用 build(...) 方法创建时,才可以使用 setTheme(...) 来修改对话框主题或风格。");
... ... @@ -824,24 +830,24 @@ public class BottomMenu extends BaseDialog {
824 830 this.customDialogStyleId = customDialogStyleId;
825 831 return this;
826 832 }
827   -
  833 +
828 834 public String toString() {
829 835 return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
830 836 }
831   -
  837 +
832 838 public OnBackClickListener getOnBackClickListener() {
833 839 return onBackClickListener;
834 840 }
835   -
  841 +
836 842 public BottomMenu setOnBackClickListener(OnBackClickListener onBackClickListener) {
837 843 this.onBackClickListener = onBackClickListener;
838 844 return this;
839 845 }
840   -
  846 +
841 847 public boolean getCancelable() {
842 848 return cancelable == BOOLEAN.TRUE;
843 849 }
844   -
  850 +
845 851 public BottomMenu setCancelable(boolean enable) {
846 852 this.cancelable = enable ? BOOLEAN.TRUE : BOOLEAN.FALSE;
847 853 if (dialog != null) dialog.get().setCancelable(cancelable == BOOLEAN.TRUE);
... ...
core/base/src/main/res/layout/bottom_menu_ios.xml
... ... @@ -94,8 +94,8 @@
94 94 android:paddingRight="15dp"
95 95 android:singleLine="true"
96 96 android:text="CANCEL"
97   - android:textColor="@color/dialogButtonIOSNormal"
98   - android:textSize="19dp"
  97 + android:textColor="@color/btnCancel"
  98 + android:textSize="16dp"
99 99 android:textStyle="bold" />
100 100  
101 101 </RelativeLayout>
... ...
core/base/src/main/res/values/attrs.xml
1 1 <?xml version="1.0" encoding="utf-8"?>
2   -<resources>
  2 +<resources xmlns:tools="http://schemas.android.com/tools">
3 3 <attr name="iconSize" format="dimension" />
4 4 <attr name="iconMargin" format="dimension" />
5 5 <attr name="iconSrc" format="reference" />
... ... @@ -80,4 +80,30 @@
80 80  
81 81 <attr name="SwipeBackLayoutStyle" format="reference" />
82 82 <!-- -->
  83 + <!-- swithbutton-->
  84 + <declare-styleable name="SwitchButton" tools:ignore="ResourceName">
  85 + <attr name="kswThumbDrawable" format="reference" />
  86 + <attr name="kswThumbColor" format="color|reference" />
  87 + <attr name="kswThumbMargin" format="dimension|reference" />
  88 + <attr name="kswThumbMarginTop" format="dimension|reference" />
  89 + <attr name="kswThumbMarginBottom" format="dimension|reference" />
  90 + <attr name="kswThumbMarginLeft" format="dimension|reference" />
  91 + <attr name="kswThumbMarginRight" format="dimension|reference" />
  92 + <attr name="kswThumbWidth" format="dimension|reference" />
  93 + <attr name="kswThumbHeight" format="dimension|reference" />
  94 + <attr name="kswThumbRadius" format="dimension|reference" />
  95 + <attr name="kswBackRadius" format="dimension|reference" />
  96 + <attr name="kswBackDrawable" format="reference" />
  97 + <attr name="kswBackColor" format="color|reference" />
  98 + <attr name="kswFadeBack" format="boolean" />
  99 + <attr name="kswThumbRangeRatio" format="float" />
  100 + <attr name="kswAnimationDuration" format="integer" />
  101 + <attr name="kswTintColor" format="color|reference" />
  102 + <attr name="kswTextOn" format="string" />
  103 + <attr name="kswTextOff" format="string" />
  104 + <attr name="kswTextThumbInset" format="dimension" />
  105 + <attr name="kswTextExtra" format="dimension" />
  106 + <attr name="kswTextAdjust" format="dimension" />
  107 + </declare-styleable>
  108 + <!---->
83 109 </resources>
84 110 \ No newline at end of file
... ...
core/base/src/main/res/values/colors.xml
... ... @@ -51,6 +51,8 @@
51 51 <color name="blackAlpha30">#4D000000</color>
52 52 <color name="blackAlpha10">#1A000000</color>
53 53 <color name="notificationNormal">#353535</color>
  54 + <color name="btnCancel">#FF6A6A</color>
  55 +
54 56 <color name="notificationError">#e74e63</color>
55 57 <color name="notificationWarning">#ef8e43</color>
56 58 <color name="notificationFinish">#53ae5d</color>
... ...
core/imageload/build.gradle
1 1 apply plugin: 'com.android.library'
2 2  
3 3 android {
4   - compileSdkVersion 29
5   - buildToolsVersion "29.0.2"
6   -
7   -
8   - defaultConfig {
9   - minSdkVersion 16
10   - targetSdkVersion 29
11   - versionCode 1
12   - versionName "1.0"
13   -
14   - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15   - consumerProguardFiles 'consumer-rules.pro'
16   - }
17   -
18   - buildTypes {
19   - release {
20   - minifyEnabled false
21   - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22   - }
23   - }
  4 +// compileSdkVersion 29
  5 +// buildToolsVersion "29.0.2"
  6 +//
  7 +//
  8 +// defaultConfig {
  9 +// minSdkVersion 16
  10 +// targetSdkVersion 29
  11 +// versionCode 1
  12 +// versionName "1.0"
  13 +//
  14 +// testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  15 +// consumerProguardFiles 'consumer-rules.pro'
  16 +// }
  17 +//
  18 +// buildTypes {
  19 +// release {
  20 +// minifyEnabled false
  21 +// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  22 +// }
  23 +// }
24 24  
25 25 }
26 26  
... ...
core/network/src/main/java/com/cnlive/core/network/HttpConn.java
1 1 package com.cnlive.core.network;
2 2  
3 3  
  4 +import android.content.Context;
4 5 import android.util.Log;
5 6  
  7 +import com.cnlive.core.libs.base.application.BaseApplication;
6 8 import com.cnlive.core.libs.base.network.manager.SubscriptionManager;
  9 +import com.cnlive.core.network.util.NetworkUtil;
7 10  
8 11 import java.util.ArrayList;
9 12 import java.util.HashMap;
... ... @@ -22,7 +25,9 @@ public class HttpConn {
22 25 public static final int ON_START = -6006;
23 26 public static final int ON_END = -6007;
24 27 public static final String EXCEPTION_CODE = "-1";//异常
25   -// public <T> Observable
  28 + private static String API_ERROR_NETWORK = "-3";
  29 + private static String API_MESSAGE_NETWORK = "网络连接失败";
  30 +
26 31  
27 32 /**
28 33 * 统一处理单个请求
... ... @@ -35,48 +40,99 @@ public class HttpConn {
35 40  
36 41 public static <T> Disposable action(Observable<T> observable, final NetCallBack<T> callBack) {
37 42 disposable = null;
38   - observable
39   - .subscribeOn(Schedulers.io())
40   - .observeOn(AndroidSchedulers.mainThread())
41   - .subscribe(new BaseObserver<T>() {
42   - @Override
43   - protected void onRequestStart(Disposable d) {
44   - super.onRequestStart(d);
45   - disposable = d;
46   - if (null != callBack) {
47   - callBack.onRequestState(ON_START);
  43 + if (null != BaseApplication.getInstance()) {
  44 + if (!NetworkUtil.isConnectInternet(BaseApplication.getInstance())) {
  45 + if (null != callBack) {
  46 + callBack.onFailure(API_ERROR_NETWORK, API_MESSAGE_NETWORK);
  47 + }
  48 + } else {
  49 + observable
  50 + .subscribeOn(Schedulers.io())
  51 + .observeOn(AndroidSchedulers.mainThread())
  52 + .subscribe(new BaseObserver<T>() {
  53 + @Override
  54 + protected void onRequestStart(Disposable d) {
  55 + super.onRequestStart(d);
  56 + disposable = d;
  57 + if (null != callBack) {
  58 + callBack.onRequestState(ON_START);
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + protected void onRequestEnd() {
  64 + super.onRequestEnd();
  65 + if (null != callBack) {
  66 + callBack.onRequestState(ON_END);
  67 + }
  68 + }
  69 +
  70 + @Override
  71 + protected void onSuccess(T result) throws Exception {
  72 + if (null != callBack) {
  73 + callBack.onSuccess(result);
  74 + }
  75 + }
  76 +
  77 + @Override
  78 + protected void onCodeError(String errCode, String errMsg) throws Exception {
  79 + if (null != callBack) {
  80 + callBack.onFailure(errCode, errMsg);
  81 + }
  82 + }
  83 +
  84 + @Override
  85 + protected void onFailure(String errCode, String errMsg) throws Exception {
  86 + if (null != callBack) {
  87 + callBack.onFailure(errCode, errMsg);
  88 + }
  89 + }
  90 + });
  91 + }
  92 + } else {
  93 + observable
  94 + .subscribeOn(Schedulers.io())
  95 + .observeOn(AndroidSchedulers.mainThread())
  96 + .subscribe(new BaseObserver<T>() {
  97 + @Override
  98 + protected void onRequestStart(Disposable d) {
  99 + super.onRequestStart(d);
  100 + disposable = d;
  101 + if (null != callBack) {
  102 + callBack.onRequestState(ON_START);
  103 + }
48 104 }
49   - }
50 105  
51   - @Override
52   - protected void onRequestEnd() {
53   - super.onRequestEnd();
54   - if (null != callBack) {
55   - callBack.onRequestState(ON_END);
  106 + @Override
  107 + protected void onRequestEnd() {
  108 + super.onRequestEnd();
  109 + if (null != callBack) {
  110 + callBack.onRequestState(ON_END);
  111 + }
56 112 }
57   - }
58 113  
59   - @Override
60   - protected void onSuccess(T result) throws Exception {
61   - if (null != callBack) {
62   - callBack.onSuccess(result);
  114 + @Override
  115 + protected void onSuccess(T result) throws Exception {
  116 + if (null != callBack) {
  117 + callBack.onSuccess(result);
  118 + }
63 119 }
64   - }
65 120  
66   - @Override
67   - protected void onCodeError(String errCode, String errMsg) throws Exception {
68   - if (null != callBack) {
69   - callBack.onFailure(errCode, errMsg);
  121 + @Override
  122 + protected void onCodeError(String errCode, String errMsg) throws Exception {
  123 + if (null != callBack) {
  124 + callBack.onFailure(errCode, errMsg);
  125 + }
70 126 }
71   - }
72 127  
73   - @Override
74   - protected void onFailure(String errCode, String errMsg) throws Exception {
75   - if (null != callBack) {
76   - callBack.onFailure(errCode, errMsg);
  128 + @Override
  129 + protected void onFailure(String errCode, String errMsg) throws Exception {
  130 + if (null != callBack) {
  131 + callBack.onFailure(errCode, errMsg);
  132 + }
77 133 }
78   - }
79   - });
  134 + });
  135 + }
80 136 // .doOnSubscribe(new Consumer<Disposable>() {
81 137 // @Override
82 138 // public void accept(Disposable disposable) throws Exception {
... ...