" + text + "
")); 213 | if (sharingIntent.resolveActivity(context.getPackageManager()) != null) { 214 | if (context instanceof Activity) { 215 | 216 | } else { 217 | sharingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 218 | } 219 | context.startActivity(Intent.createChooser(sharingIntent, "Share using")); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /core/src/main/java/net/angrycode/toolkit/PreferenceHelper.java: -------------------------------------------------------------------------------- 1 | package net.angrycode.toolkit; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.os.Build; 7 | 8 | /** 9 | * SharedPreferences操作工具包23 | * author: Blankj 24 | * blog : http://blankj.com 25 | * time : 2016/10/18 26 | * desc : 进程相关工具类 27 | *28 | */ 29 | public final class ProcessUtils { 30 | 31 | private ProcessUtils() { 32 | throw new UnsupportedOperationException("u can't instantiate me..."); 33 | } 34 | 35 | /** 36 | * 获取前台线程包名 37 | *
当不是查看当前App,且SDK大于21时,
38 | * 需添加权限 {@code
需添加权限 {@code
需添加权限 {@code
需添加权限 {@code
移动:134(0-8)、135、136、137、138、139、147、150、151、152、157、158、159、178、182、183、184、187、188
12 | *联通:130、131、132、145、155、156、171、175、176、185、186
13 | *电信:133、153、173、177、180、181、189
14 | *全球星:1349
15 | *虚拟运营商:170
16 | */ 17 | public static final String REGEX_MOBILE_EXACT = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,1,3,5-8])|(18[0-9])|(147))\\d{8}$"; 18 | /** 19 | * 正则:电话号码 20 | */ 21 | public static final String REGEX_TEL = "^0\\d{2,3}[- ]?\\d{7,8}"; 22 | /** 23 | * 正则:身份证号码15位 24 | */ 25 | public static final String REGEX_ID_CARD15 = "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$"; 26 | /** 27 | * 正则:身份证号码18位 28 | */ 29 | public static final String REGEX_ID_CARD18 = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$"; 30 | /** 31 | * 正则:邮箱 32 | */ 33 | public static final String REGEX_EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"; 34 | /** 35 | * 正则:URL 36 | */ 37 | public static final String REGEX_URL = "[a-zA-z]+://[^\\s]*"; 38 | /** 39 | * 正则:汉字 40 | */ 41 | public static final String REGEX_ZH = "^[\\u4e00-\\u9fa5]+$"; 42 | /** 43 | * 正则:用户名,取值范围为a-z,A-Z,0-9,"_",汉字,不能以"_"结尾,用户名必须是6-20位 44 | */ 45 | public static final String REGEX_USERNAME = "^[\\w\\u4e00-\\u9fa5]{6,20}(? 0 ? (x > layout.getLineMax(line) ? offset : offset - 1) : offset; 82 | } 83 | 84 | @Override 85 | public boolean onTouchEvent(MotionEvent event) { 86 | boolean superResult = super.onTouchEvent(event); 87 | 88 | switch (event.getAction()) { 89 | case MotionEvent.ACTION_UP: 90 | case MotionEvent.ACTION_CANCEL: 91 | tryRemoveLinkSpan(); 92 | break; 93 | 94 | case MotionEvent.ACTION_MOVE: 95 | if (getMovementMethod() != null) { 96 | Spannable buffer = (Spannable) getText(); 97 | int x = (int) event.getX(); 98 | int y = (int) event.getY(); 99 | 100 | x -= this.getTotalPaddingLeft(); 101 | y -= this.getTotalPaddingTop(); 102 | 103 | x += this.getScrollX(); 104 | y += this.getScrollY(); 105 | 106 | Layout layout = this.getLayout(); 107 | int line = layout.getLineForVertical(y); 108 | int off = layout.getOffsetForHorizontal(line, x); 109 | 110 | ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); 111 | if (link.length != 0) { 112 | 113 | ClickableSpan cs = link[0]; 114 | int csStart = buffer.getSpanStart(cs); 115 | int csEnd = buffer.getSpanEnd(cs); 116 | 117 | int top = layout.getLineTop(line); 118 | int bottom = layout.getLineBottom(line); 119 | float eventY = event.getY() - this.getTotalPaddingTop(); 120 | 121 | 122 | if (off < csStart || off >= csEnd || eventY < top || eventY > bottom) { 123 | // System.out.println("点击之前:" + line + " " + csStart + " " + csEnd + " " + off + " " + link.length + " " + top + " " + bottom + " " + eventY); 124 | tryRemoveLinkSpan(); 125 | } 126 | } 127 | } 128 | break; 129 | } 130 | 131 | return superResult; 132 | } 133 | 134 | @Override 135 | protected void onDetachedFromWindow() { 136 | super.onDetachedFromWindow(); 137 | // if (mLocalLinkMovementMethod != null) { 138 | // mLocalLinkMovementMethod.setLinkTextView(null); 139 | // mLocalLinkMovementMethod.setOnLinkClickListener(null); 140 | // } 141 | } 142 | 143 | private void tryRemoveLinkSpan() { 144 | if (getMovementMethod() != null) { 145 | Spannable buffer = (Spannable) getText(); 146 | //移除按下的背景颜色 147 | BackgroundColorSpan[] backgroundColorSpans = buffer.getSpans(0, buffer.length(), 148 | BackgroundColorSpan.class); 149 | for (BackgroundColorSpan span : backgroundColorSpans) { 150 | buffer.removeSpan(span); 151 | this.setText(buffer); 152 | } 153 | } 154 | } 155 | 156 | public interface OnLinkClickListener { 157 | boolean onLinkClick(LinkTextView textView, String link, MotionEvent event); 158 | 159 | boolean onNormalClick(LinkTextView textView, MotionEvent event); 160 | } 161 | 162 | public static class LocalLinkMovementMethod extends LinkMovementMethod { 163 | 164 | private static LocalLinkMovementMethod sInstance; 165 | 166 | private LinkTextView mLinkTextView; 167 | private OnLinkClickListener mOnLinkClickListener; 168 | 169 | public static LocalLinkMovementMethod getInstance() { 170 | if (sInstance == null) { 171 | sInstance = new LocalLinkMovementMethod(); 172 | } 173 | return sInstance; 174 | } 175 | 176 | public void setLinkTextView(LinkTextView linkTextView) { 177 | mLinkTextView = linkTextView; 178 | } 179 | 180 | public void setOnLinkClickListener(OnLinkClickListener listener) { 181 | mOnLinkClickListener = listener; 182 | } 183 | 184 | @Override 185 | public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { 186 | int action = event.getAction(); 187 | 188 | switch (action) { 189 | case MotionEvent.ACTION_CANCEL: 190 | //移除按下的背景颜色 191 | BackgroundColorSpan[] backgroundColorSpans = buffer.getSpans(0, buffer.length(), 192 | BackgroundColorSpan.class); 193 | for (BackgroundColorSpan span : backgroundColorSpans) { 194 | buffer.removeSpan(span); 195 | widget.setText(buffer); 196 | } 197 | return true; 198 | 199 | case MotionEvent.ACTION_MOVE: 200 | // return super.onTouchEvent(widget, buffer, event); 201 | return true; 202 | 203 | case MotionEvent.ACTION_DOWN: 204 | case MotionEvent.ACTION_UP: 205 | 206 | int x = (int) event.getX(); 207 | int y = (int) event.getY(); 208 | 209 | x -= widget.getTotalPaddingLeft(); 210 | y -= widget.getTotalPaddingTop(); 211 | 212 | x += widget.getScrollX(); 213 | y += widget.getScrollY(); 214 | 215 | Layout layout = widget.getLayout(); 216 | int line = layout.getLineForVertical(y); 217 | int off = layout.getOffsetForHorizontal(line, x); 218 | 219 | ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); 220 | 221 | 222 | if (link.length != 0) { 223 | 224 | ClickableSpan cs = link[0]; 225 | int csStart = buffer.getSpanStart(cs); 226 | int csEnd = buffer.getSpanEnd(cs); 227 | 228 | int top = layout.getLineTop(line); 229 | int bottom = layout.getLineBottom(line); 230 | float eventY = event.getY() - widget.getTotalPaddingTop(); 231 | 232 | // System.out.println("点击之前:" + line + " " + csStart + " " + csEnd + " " + off + " " + link.length + " " + top + " " + bottom + " " + eventY); 233 | if (off >= csStart && off < csEnd && eventY >= top && eventY <= bottom) { 234 | if (action == MotionEvent.ACTION_UP) { 235 | //移除按下的背景颜色 236 | BackgroundColorSpan[] spans = buffer.getSpans(0, buffer.length(), 237 | BackgroundColorSpan.class); 238 | for (BackgroundColorSpan span : spans) { 239 | buffer.removeSpan(span); 240 | widget.setText(buffer); 241 | } 242 | 243 | 244 | String url = null; 245 | if (link[0] instanceof LinkClickable) { 246 | url = ((LinkClickable) link[0]).getUrl(); 247 | } 248 | 249 | if (mOnLinkClickListener == null || !mOnLinkClickListener.onLinkClick(mLinkTextView, url, event)) { 250 | link[0].onClick(widget); 251 | } 252 | 253 | // System.out.println("点击到链接:" + url + " " + csStart + " " + csEnd + " " + off + " " + link.length + " " + top + " " + bottom); 254 | } else { 255 | //增加按下的背景颜色 256 | BackgroundColorSpan span = new BackgroundColorSpan( 257 | widget.getResources().getColor(R.color.color_selected)); 258 | buffer.setSpan(span, csStart, csEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 259 | widget.setText(buffer); 260 | Selection.setSelection(buffer, 261 | buffer.getSpanStart(link[0]), 262 | buffer.getSpanEnd(link[0])); 263 | } 264 | 265 | 266 | } else { 267 | if (action == MotionEvent.ACTION_UP) { 268 | if (mOnLinkClickListener == null || !mOnLinkClickListener.onNormalClick(mLinkTextView, event)) { 269 | 270 | } 271 | } 272 | } 273 | 274 | return true; 275 | } else { 276 | Selection.removeSelection(buffer); 277 | Touch.onTouchEvent(widget, buffer, event); 278 | if (action == MotionEvent.ACTION_UP) { 279 | if (mOnLinkClickListener == null || !mOnLinkClickListener.onNormalClick(mLinkTextView, event)) { 280 | 281 | } 282 | } 283 | return false; 284 | } 285 | 286 | } 287 | 288 | return Touch.onTouchEvent(widget, buffer, event); 289 | } 290 | } 291 | 292 | public static class LinkSpannableString extends SpannableString { 293 | 294 | public LinkSpannableString(CharSequence source) { 295 | super(source); 296 | String s = source.toString(); 297 | if (s.contains("@")) { 298 | parseEmailLink(s); 299 | } else { 300 | gatherLink(source); 301 | } 302 | } 303 | 304 | private void parseEmailLink(String source) { 305 | String[] ss = source.split(" " + "|" + "\\n"); 306 | Matcher matcher; 307 | for (String s1 : ss) { 308 | if (s1.contains("@")) { 309 | Pattern emailPattern = Pattern.compile(_EMIAL); 310 | matcher = emailPattern.matcher(s1); 311 | } else { 312 | Pattern emailPattern = Pattern.compile(URL_REGEX); 313 | matcher = emailPattern.matcher(s1); 314 | } 315 | boolean result = matcher.find(); 316 | while (result) { 317 | final String find = matcher.group(); 318 | int start = matcher.start(); 319 | int end = matcher.end(); 320 | int index = source.indexOf(s1); 321 | final String url = find; 322 | setSpan(new LinkClickable(url, new OnClickListener() { 323 | @Override 324 | public void onClick(View v) { 325 | if (url.contains("@")) { 326 | //TODO 327 | // Utils.sendMail( , url , null , null); 328 | } else { 329 | // WebUtils.openUrlInCommon(, url); 330 | } 331 | } 332 | }), start + index, end + index, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 333 | 334 | result = matcher.find(); 335 | } 336 | } 337 | } 338 | 339 | private void gatherLink(CharSequence source) { 340 | Pattern defaultPattern = Pattern.compile(URL_REGEX); 341 | Matcher matcher = defaultPattern.matcher(source); 342 | 343 | boolean result = matcher.find(); 344 | while (result) { 345 | final String find = matcher.group(); 346 | 347 | int start = matcher.start(); 348 | int end = matcher.end(); 349 | 350 | int length = find.length(); 351 | int index = -1; 352 | for (int i = 0; i < length; i++) { 353 | if (find.charAt(i) >= 127) { 354 | index = i; 355 | break; 356 | } 357 | } 358 | final String url; 359 | if (index > 0) { 360 | url = find.substring(0, index); 361 | end = start + url.length(); 362 | } else { 363 | url = find; 364 | } 365 | 366 | setSpan(new LinkClickable(url, new OnClickListener() { 367 | @Override 368 | public void onClick(View v) { 369 | // TODO 370 | // v -> WebUtils.openUrlInCommon(, url) 371 | } 372 | }), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 373 | 374 | result = matcher.find(); 375 | } 376 | 377 | } 378 | 379 | } 380 | 381 | public static class LinkClickable extends ClickableSpan { 382 | 383 | private final View.OnClickListener mListener; 384 | 385 | private String mUrl; 386 | 387 | public LinkClickable(String url, View.OnClickListener l) { 388 | mUrl = url; 389 | mListener = l; 390 | } 391 | 392 | public String getUrl() { 393 | return mUrl; 394 | } 395 | 396 | @Override 397 | public void onClick(View widget) { 398 | mListener.onClick(widget); 399 | } 400 | 401 | @Override 402 | public void updateDrawState(TextPaint ds) { 403 | super.updateDrawState(ds); 404 | ds.setUnderlineText(false); 405 | ds.setColor(Color.parseColor("#FF00a8ff")); 406 | } 407 | } 408 | 409 | } -------------------------------------------------------------------------------- /core/src/main/java/net/angrycode/toolkit/widget/QuickClearEditText.java: -------------------------------------------------------------------------------- 1 | package net.angrycode.toolkit.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Build; 7 | import android.support.v4.content.ContextCompat; 8 | import android.text.Editable; 9 | import android.text.InputType; 10 | import android.text.TextWatcher; 11 | import android.util.AttributeSet; 12 | import android.util.TypedValue; 13 | import android.view.Gravity; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.EditText; 17 | import android.widget.FrameLayout; 18 | import android.widget.ImageView; 19 | 20 | import net.angrycode.core.R; 21 | 22 | public class QuickClearEditText extends FrameLayout { 23 | private static final String LOG_TAG = "QuickClearEditText"; 24 | 25 | /** 26 | * 输入内容距离清除按钮之间的距离 27 | */ 28 | private static final int QUICK_CLEAR_MARGIN = 0; 29 | 30 | protected EditText mEditText; 31 | 32 | private FrameLayout mQuickClearBtnWrapper; 33 | private ImageView mQuickClearBtn; 34 | 35 | /** 36 | * 允许使用快速清除功能 37 | */ 38 | private boolean mAllowQuickClear; 39 | 40 | private Drawable mQuickClearDrawable; 41 | 42 | private int mHeight = 0; 43 | 44 | public QuickClearEditText(Context context) { 45 | this(context, null); 46 | } 47 | 48 | public QuickClearEditText(Context context, AttributeSet attrs) { 49 | this(context, attrs, 0); 50 | } 51 | 52 | public QuickClearEditText(Context context, AttributeSet attrs, int defStyleAttr) { 53 | super(context, attrs, defStyleAttr); 54 | init(context, attrs); 55 | } 56 | 57 | private void init(Context context, AttributeSet attrs) { 58 | // 让设置的背景作用于EditText上 59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 60 | setBackground(null); 61 | } else { 62 | setBackgroundDrawable(null); 63 | } 64 | setPadding(0, 0, 0, 0); 65 | 66 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QuickClearEditText); 67 | final boolean allowQuickClear = array.getBoolean(R.styleable.QuickClearEditText_quickClear, true); 68 | mQuickClearDrawable = array.getDrawable(R.styleable.QuickClearEditText_quickClearIcon); 69 | array.recycle(); 70 | 71 | // 设置一个默认的清除按钮 72 | if (mQuickClearDrawable == null) { 73 | mQuickClearDrawable = ContextCompat.getDrawable(getContext(), R.mipmap.ic_clear); 74 | } 75 | 76 | addEditText(context, attrs); 77 | 78 | setAllowQuickClear(allowQuickClear); 79 | 80 | } 81 | 82 | private void addEditText(Context context, AttributeSet attrs) { 83 | mEditText = onCreateEditText(context, attrs); 84 | mEditText.setId(ViewUtils.generateViewId()); 85 | 86 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 87 | addView(mEditText, lp); 88 | mEditText.addTextChangedListener(new InputContentChangeListener()); 89 | } 90 | 91 | private void addQuickClearBtn(Context context) { 92 | mQuickClearBtn = new ImageView(context); 93 | mQuickClearBtnWrapper = new FrameLayout(context); 94 | 95 | TypedValue outValue = new TypedValue(); 96 | context.getTheme().resolveAttribute(R.attr.selectableItemBackgroundBorderless, outValue, true); 97 | if (outValue.resourceId != -1) { 98 | mQuickClearBtn.setBackgroundResource(outValue.resourceId); 99 | } 100 | 101 | mQuickClearBtn.setScaleType(ImageView.ScaleType.CENTER); 102 | mQuickClearBtn.setImageDrawable(mQuickClearDrawable); 103 | 104 | int w = mHeight != 0 ? mHeight / 2 : LayoutParams.WRAP_CONTENT; 105 | int h = mHeight != 0 ? mHeight / 2 : LayoutParams.WRAP_CONTENT; 106 | FrameLayout.LayoutParams btnLp = new FrameLayout.LayoutParams(w, h); 107 | btnLp.gravity = Gravity.CENTER; 108 | mQuickClearBtnWrapper.addView(mQuickClearBtn, btnLp); 109 | 110 | int width = mHeight != 0 ? mHeight : LayoutParams.WRAP_CONTENT; 111 | int height = mHeight != 0 ? mHeight : LayoutParams.WRAP_CONTENT; 112 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(width, height); 113 | lp.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT; 114 | addView(mQuickClearBtnWrapper, lp); 115 | 116 | if (mEditText.getText().length() > 0) { 117 | mQuickClearBtnWrapper.setVisibility(View.VISIBLE); 118 | } else { 119 | mQuickClearBtnWrapper.setVisibility(View.INVISIBLE); 120 | } 121 | 122 | setEditTextPadding(); 123 | 124 | mQuickClearBtnWrapper.setOnClickListener(new QuickClearClickListener()); 125 | } 126 | 127 | private void removeQuickClearBtn() { 128 | if (mQuickClearBtn != null) { 129 | removeView(mQuickClearBtn); 130 | mQuickClearBtn = null; 131 | } 132 | } 133 | 134 | private void resizeQuickClearBtnIfNeed() { 135 | if (mAllowQuickClear && mQuickClearBtnWrapper != null) { 136 | ViewGroup.LayoutParams lp = mQuickClearBtn.getLayoutParams(); 137 | if (lp != null) { 138 | lp.width = mHeight / 2; 139 | lp.height = mHeight / 2; 140 | mQuickClearBtn.setLayoutParams(lp); 141 | } 142 | 143 | lp = mQuickClearBtnWrapper.getLayoutParams(); 144 | if (lp != null) { 145 | lp.width = mHeight; 146 | lp.height = mHeight; 147 | mQuickClearBtnWrapper.setLayoutParams(lp); 148 | } 149 | setEditTextPadding(); 150 | } 151 | } 152 | 153 | private void setEditTextPadding() { 154 | if (mHeight > 0) { 155 | int l = mEditText.getPaddingLeft(); 156 | int t = mEditText.getPaddingTop(); 157 | int r = mEditText.getPaddingRight() + mHeight + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, QUICK_CLEAR_MARGIN, getResources().getDisplayMetrics()); 158 | int b = mEditText.getPaddingBottom(); 159 | mEditText.setPadding(l, t, r, b); 160 | } 161 | } 162 | 163 | public void setAllowQuickClear(boolean allow) { 164 | if (allow != mAllowQuickClear) { 165 | mAllowQuickClear = allow; 166 | if (mAllowQuickClear) { 167 | addQuickClearBtn(getContext()); 168 | } else { 169 | removeQuickClearBtn(); 170 | } 171 | } 172 | } 173 | 174 | public void setText(final CharSequence text) { 175 | mEditText.setText(text); 176 | } 177 | 178 | public int length() { 179 | return mEditText.length(); 180 | } 181 | 182 | public void setSelection(int index) { 183 | mEditText.setSelection(index); 184 | } 185 | 186 | public Editable getText() { 187 | return mEditText.getText(); 188 | } 189 | 190 | public EditText getEditText() { 191 | return mEditText; 192 | } 193 | 194 | public void append(final CharSequence text) { 195 | mEditText.append(text); 196 | } 197 | 198 | @Override 199 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 200 | super.onSizeChanged(w, h, oldw, oldh); 201 | mHeight = h; 202 | resizeQuickClearBtnIfNeed(); 203 | // Logger.d(LOG_TAG, "height:" + mHeight); 204 | } 205 | 206 | public void setPasswordVisible(boolean visible) { 207 | if (visible) {//显示明文 208 | mEditText.setInputType(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); 209 | } else {//显示密文 210 | mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); 211 | } 212 | mEditText.setSelection(mEditText.getText().length()); 213 | 214 | } 215 | 216 | protected EditText onCreateEditText(Context context, AttributeSet attrs) { 217 | 218 | return new EditText(context, attrs); 219 | } 220 | 221 | /** 222 | * 供子类使用,免得子类再去添加一个监听器 223 | */ 224 | protected void onTextChanged(CharSequence s, int start, int before, int count) { 225 | } 226 | 227 | private class InputContentChangeListener implements TextWatcher { 228 | 229 | @Override 230 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 231 | if (mTextWatcher != null) { 232 | mTextWatcher.beforeTextChanged(s, start, count, after); 233 | } 234 | } 235 | 236 | @Override 237 | public void onTextChanged(CharSequence s, int start, int before, int count) { 238 | QuickClearEditText.this.onTextChanged(s, start, before, count); 239 | if (mTextWatcher != null) { 240 | mTextWatcher.onTextChanged(s, start, before, count); 241 | } 242 | if (mAllowQuickClear && mQuickClearBtnWrapper != null) { 243 | if (s != null && s.length() > 0) { 244 | mQuickClearBtnWrapper.post(new Runnable() { 245 | @Override 246 | public void run() { 247 | mQuickClearBtnWrapper.setVisibility(View.VISIBLE); 248 | } 249 | }); 250 | 251 | } else { 252 | mQuickClearBtnWrapper.setVisibility(View.INVISIBLE); 253 | } 254 | } 255 | } 256 | 257 | @Override 258 | public void afterTextChanged(Editable s) { 259 | if (mTextWatcher != null) { 260 | mTextWatcher.afterTextChanged(s); 261 | } 262 | } 263 | } 264 | 265 | private class QuickClearClickListener implements OnClickListener { 266 | 267 | @Override 268 | public void onClick(View v) { 269 | if (mEditText != null) { 270 | mEditText.setText(null); 271 | mEditText.requestFocus(); 272 | } 273 | } 274 | } 275 | 276 | public TextWatcher mTextWatcher; 277 | 278 | public void setOnTextChangeListener(TextWatcher textWatcher) { 279 | mTextWatcher = textWatcher; 280 | } 281 | 282 | } -------------------------------------------------------------------------------- /core/src/main/java/net/angrycode/toolkit/widget/SimpleDrawingView.java: -------------------------------------------------------------------------------- 1 | package net.angrycode.toolkit.widget; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.Path; 9 | import android.support.annotation.Nullable; 10 | import android.util.AttributeSet; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | 14 | public class SimpleDrawingView extends View { 15 | // setup initial color 16 | private final int paintColor = Color.BLACK; 17 | // defines paint and canvas 18 | private Paint drawPaint; 19 | // stores next circle 20 | private Path path = new Path(); 21 | 22 | public SimpleDrawingView(Context context) { 23 | super(context); 24 | } 25 | 26 | public SimpleDrawingView(Context context, AttributeSet attrs) { 27 | super(context, attrs); 28 | setFocusable(true); 29 | setFocusableInTouchMode(true); 30 | setupPaint(); 31 | } 32 | 33 | public SimpleDrawingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | } 36 | @TargetApi(21) 37 | public SimpleDrawingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { 38 | super(context, attrs, defStyleAttr, defStyleRes); 39 | } 40 | 41 | private void setupPaint() { 42 | // Setup paint with color and stroke styles 43 | drawPaint = new Paint(); 44 | drawPaint.setColor(paintColor); 45 | drawPaint.setAntiAlias(true); 46 | drawPaint.setStrokeWidth(5); 47 | drawPaint.setStyle(Paint.Style.STROKE); 48 | drawPaint.setStrokeJoin(Paint.Join.ROUND); 49 | drawPaint.setStrokeCap(Paint.Cap.ROUND); 50 | } 51 | 52 | @Override 53 | protected void onDraw(Canvas canvas) { 54 | canvas.drawPath(path, drawPaint); 55 | } 56 | 57 | @Override 58 | public boolean onTouchEvent(MotionEvent event) { 59 | float pointX = event.getX(); 60 | float pointY = event.getY(); 61 | // Checks for the event that occurs 62 | switch (event.getAction()) { 63 | case MotionEvent.ACTION_DOWN: 64 | path.moveTo(pointX, pointY); 65 | return true; 66 | case MotionEvent.ACTION_MOVE: 67 | path.lineTo(pointX, pointY); 68 | break; 69 | default: 70 | return false; 71 | } 72 | // Force a view to draw again 73 | postInvalidate(); 74 | return true; 75 | } 76 | } -------------------------------------------------------------------------------- /core/src/main/java/net/angrycode/toolkit/widget/SimpleTextWatcher.java: -------------------------------------------------------------------------------- 1 | package net.angrycode.toolkit.widget; 2 | 3 | import android.text.Editable; 4 | import android.text.TextWatcher; 5 | 6 | /** 7 | * Created by wecodexyz on 2017/8/19. 8 | */ 9 | 10 | public class SimpleTextWatcher implements TextWatcher { 11 | public SimpleTextWatcher() { 12 | } 13 | 14 | @Override 15 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 16 | 17 | } 18 | 19 | @Override 20 | public void onTextChanged(CharSequence s, int start, int before, int count) { 21 | 22 | } 23 | 24 | @Override 25 | public void afterTextChanged(Editable s) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/net/angrycode/toolkit/widget/TintableImageView.java: -------------------------------------------------------------------------------- 1 | package net.angrycode.toolkit.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.ColorStateList; 5 | import android.content.res.TypedArray; 6 | import android.util.AttributeSet; 7 | import android.widget.ImageView; 8 | 9 | import net.angrycode.core.R; 10 | 11 | 12 | /** 13 | * https://gist.github.com/tylerchesley/5d15d859be4f3ce31213 14 | */ 15 | public class TintableImageView extends ImageView { 16 | 17 | private ColorStateList tint; 18 | 19 | public TintableImageView(Context context) { 20 | super(context); 21 | } 22 | 23 | public TintableImageView(Context context, AttributeSet attrs) { 24 | super(context, attrs); 25 | init(context, attrs, 0); 26 | } 27 | 28 | public TintableImageView(Context context, AttributeSet attrs, int defStyle) { 29 | super(context, attrs, defStyle); 30 | init(context, attrs, defStyle); 31 | } 32 | 33 | private void init(Context context, AttributeSet attrs, int defStyle) { 34 | TypedArray a = context.obtainStyledAttributes( 35 | attrs, R.styleable.TintableImageView, defStyle, 0); 36 | tint = a.getColorStateList( 37 | R.styleable.TintableImageView_tintColor); 38 | a.recycle(); 39 | } 40 | 41 | @Override 42 | protected void drawableStateChanged() { 43 | super.drawableStateChanged(); 44 | if (tint != null && tint.isStateful()) { 45 | updateTintColor(); 46 | } 47 | } 48 | 49 | public void setColorFilter(ColorStateList tint) { 50 | this.tint = tint; 51 | super.setColorFilter(tint.getColorForState(getDrawableState(), 0)); 52 | } 53 | 54 | private void updateTintColor() { 55 | int color = tint.getColorForState(getDrawableState(), 0); 56 | setColorFilter(color); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/net/angrycode/toolkit/widget/ViewUtils.java: -------------------------------------------------------------------------------- 1 | package net.angrycode.toolkit.widget; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.os.Build; 8 | import android.util.Log; 9 | import android.view.View; 10 | import android.view.inputmethod.InputMethodManager; 11 | import android.webkit.WebView; 12 | import android.widget.TextView; 13 | 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | import static android.view.View.GONE; 17 | import static android.view.View.INVISIBLE; 18 | import static android.view.View.VISIBLE; 19 | 20 | /** 21 | *TextView textView = ViewUtils.{@link #findViewById findViewById}(this, R.id.my_text_view);
//no more casting!TextView textView = ViewUtils.{@link #findViewById findViewById}(parentView, R.id.my_text_view);
//no more casting!String text = ViewUtils.{@link #getText getText}(this, R.id.my_text_view);
ViewUtils.{@link #setText setText}(this, R.id.my_text_vew, "new text");
ViewUtils.{@link #setText setText}(textView, "new text");
ViewUtils.{@link #appendText appendText}(textView, "appended");
ViewUtils.{@link #hideView hideView}(this, R.id.my_text_view);
ViewUtils.{@link #showView showView}(this, R.id.my_text_view);
Bitmap bitmap = ViewUtils.{@link #viewToImage viewToImage}(this, R.id.my_layout);
ViewUtils.{@link #closeKeyboard showKeyboard}(this, R.id.my_text_view);
ViewUtils.{@link #closeKeyboard closeKeyboard}(this, R.id.my_text_view);
View.GONE
.
216 | *
217 | * @param context The current Context or Activity that this method is called from
218 | * @param id R.id.xxxx value for the view to hide"expected textView to throw a ClassCastException" + textView
219 | */
220 | public static void hideView(Activity context, int id) {
221 | if (context != null) {
222 | View view = context.findViewById(id);
223 | if (view != null) {
224 | view.setVisibility(View.GONE);
225 | } else {
226 | Log.e("PercolateAndroidUtils", "View does not exist. Could not hide it.");
227 | }
228 | }
229 | }
230 |
231 | /**
232 | * Sets visibility of the given view to View.VISIBLE
.
233 | *
234 | * @param context The current Context or Activity that this method is called from
235 | * @param id R.id.xxxx value for the view to show
236 | */
237 | public static void showView(Activity context, int id) {
238 | if (context != null) {
239 | View view = context.findViewById(id);
240 | if (view != null) {
241 | view.setVisibility(View.VISIBLE);
242 | } else {
243 | Log.e("PercolateAndroidUtils", "View does not exist. Could not hide it.");
244 | }
245 | }
246 | }
247 |
248 | /**
249 | * Set visibility of given view to be gone or visible
250 | *
251 | * This method has no effect if the view visibility is currently invisible
252 | *
253 | * @param view
254 | * @param gone
255 | * @return view
256 | */
257 | public static
272 | * This method has no effect if the view visibility is currently gone
273 | *
274 | * @param view
275 | * @param invisible
276 | * @return view
277 | */
278 | public static