Android開發(fā)小技巧

1.Touch處理

MotionEventCompat.getActionMasked(ev)
等價于
event.getAction() & ACTION_MASK

    /**
     * Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK}
     * portion.
     */
    public static int getActionMasked(MotionEvent event) {
        return event.getAction() & ACTION_MASK;
    }

2.枚舉

enum

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY 
}
public enum Day {
    MONDAY(1, "星期一", "星期一各種不在狀態(tài)"),
    TUESDAY(2, "星期二", "星期二依舊犯困"),
    WEDNESDAY(3, "星期三", "星期三感覺半周終于過去了"),
    THURSDAY(4, "星期四", "星期四期待這星期五"),
    FRIDAY(5, "星期五", "星期五感覺還不錯"),
    SATURDAY(6, "星期六", "星期六感覺非常好"),
    SUNDAY(7, "星期日", "星期日感覺周末還沒過夠。撩嚼。停士。");

    Day(int index, String name, String value) {
        this.index = index;
        this.name = name;
        this.value = value;
    }

    private int index;
    private String name;
    private String value;

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

java enum枚舉類的用法以及高級玩法


ENUM中的每一個值都是一個Object,它的每個聲明都會占用運行時的部分內(nèi)存以便能夠引用到這個Object完丽。因此ENUM的值會比對應(yīng)的Integer和String所占用的內(nèi)存多恋技。過度在Android開發(fā)中使用ENUM將會增大DEX大小,并會增大運行時的內(nèi)存分配大小舰涌。

Android中的注解方式

添加支持注解的依賴到你的項目中猖任,需要在build.gradle文件中的依賴塊中添加:
dependencies { compile 'com.android.support:support-annotations:24.2.0' }

IntDefStringDef是兩個神奇的注解常量,可以用來替代Enum的使用瓷耙。這些注解能夠幫助我們在編譯時對變量賦值進(jìn)行檢查朱躺。


 public static final int SUNDAY= 0;
 public static final int MONDAY= 1;
 public static final int TUESDAY= 2;
 public static final int WEDNESDAY= 3;
     ...

@IntDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY})
@Retention(RetentionPolicy.SOURCE)
public @interface Day{}

// 使用
@Day int today;
// Constants
public static final String WINTER = "Winter";
public static final String SPRING = "Spring";
public static final String SUMMER = "Summer";
public static final String FALL = "Fall";

// Declare the @ StringDef for these constants:
@ StringDef ({WINTER, SPRING, SUMMER, FALL})
@Retention(RetentionPolicy.SOURCE)
public @interface Season {}


// 在使用的時候,例如我們有一個變量名稱為:
int color;
// 與此同時有一個函數(shù):
void setColor(@Color int COLOR){
color = COLOR;
}
//在調(diào)用此函數(shù)的時候搁痛,參數(shù)名稱如果不是IntDef中的變量名稱的時候长搀,例如
//setColor(2),Android Studio中就會提示錯誤(雖然編譯仍然會通過)鸡典。

Android 性能:避免在Android上使用ENUM
Android開發(fā)中用于替代Enum的@IntDef的使用

3.getResources().getDrawable() 過時的解決方法

  1. 當(dāng)你這個Drawable受當(dāng)前Activity主題的影響時
ContextCompat.getDrawable(getActivity(), R.drawable.name);
  1. 當(dāng)你這個Drawable不受主題影響時
ResourcesCompat.getDrawable(getResources(), R.drawable.name, null);
  1. 當(dāng)你這個Drawable想使用另外一個主題樣式時
ResourcesCompat.getDrawable(getResources(), R.drawable.name, anotherTheme);

4.填加分割線

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@drawable/setting_divider"
    android:showDividers="middle"
    android:background="@drawable/area_dialog_bg"
    android:gravity="center">

5.獲取屏幕寬高

1. 通過WindowManager獲取
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
heigth = dm.heightPixels;
width = dm.widthPixels;

2. 通過Resources獲取
DisplayMetrics dm = getResources().getDisplayMetrics();
heigth = dm.heightPixels;
width = dm.widthPixels;

3. 獲取屏幕的默認(rèn)分辨率
Display display = getWindowManager().getDefaultDisplay();
heigth = display.getWidth();
width = display.getHeight();

1源请、3都是使用getWindowManager()得到的,但這個是建立在類Activity上的彻况,如果自己的類沒有繼承這個谁尸,則取不到數(shù)據(jù),故個人認(rèn)為通過Resources獲取最好。

在非Activity類中纽甘,拿到WindowManager

((WindowManager) MyApplicationContext.context.getSystemService(Context.WINDOW_SERVICE))
  .getDefaultDisplay()
  .getMetrics(dm);

6.分享(不調(diào)用第三方SDK)

Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "來自WanAndroid" + detailLink);
startActivity(Intent.createChooser(shareIntent, "分享"));

7.RecyclerView點擊條目自動滑動到中央

public class CenterLayoutManager extends LinearLayoutManager {

    public CenterLayoutManager(Context context) {
        super(context);
    }

    public CenterLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public CenterLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private static class CenterSmoothScroller extends LinearSmoothScroller {

        CenterSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }
    }
}

其中calculateDtToFit的參數(shù)良蛮,viewStart viewEnd表示被點擊的條目需要移動的Start/End,viewEnd - viewStart = View的寬度悍赢;boxStart boxEnd表示當(dāng)前RecyclerView的Start/End决瞳,boxEnd - boxStart = RecyclerView的寬度(無Padding)货徙。

public int calculateDyToMakeVisible(View view, int snapPreference) {
    final RecyclerView.LayoutManager layoutManager = getLayoutManager();
    if (layoutManager == null || !layoutManager.canScrollVertically()) {
        return 0;
    }
    final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
                view.getLayoutParams();
    final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
    final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
    final int start = layoutManager.getPaddingTop();
    final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
        return calculateDtToFit(top, bottom, start, end, snapPreference);
}

8.自定義View的動畫還可以這么搞

        mTabSelector = new Runnable() {
            public void run() {
                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
                smoothScrollTo(scrollPos, 0);
                mTabSelector = null;
            }
        };
        post(mTabSelector);

9.RecyclerView滾動到當(dāng)前界面第一個

scrollToPosition 會把不在屏幕的 Item 移動到屏幕上,原來在上方的 Item 移動到 可見 Item 的第一項皮胡,在下方的移動到屏幕可見 Item 的最后一項痴颊。已經(jīng)顯示的 Item 不會移動。

scrollToPositionWithOffset會把 Item 移動到可見 Item 的第一項屡贺,即使它已經(jīng)在可見 Item 之中蠢棱。另外它還有 offset 參數(shù),表示 Item 移動到第一項后跟 RecyclerView 上邊界或下邊界之間的距離(默認(rèn)是 0)

10.MAC快捷鍵

大小寫轉(zhuǎn)換 Cmd + Shift + U Ctrl + Shift + U
注釋代碼(//) Cmd + / Ctrl + /
注釋代碼(/**/) Cmd + Option + / Ctrl + Alt + /
格式化代碼 Cmd + Option + L Ctrl + Alt + L
清除無效包引用 Option + Control + O Alt + Ctrl + O
查找 Cmd + F Ctrl + F
查找+替換 Cmd + R Ctrl + R
上下移動代碼 Option + Shift + Up/Down Alt + Shift + Up/Down
刪除行 Cmd + Delete Ctrl + Y
擴大縮小選中范圍 Option + Up/Down Ctrl + W/Ctrl + Shift + W
快捷生成結(jié)構(gòu)體 Cmd + Option + T Ctrl + Alt + T
快捷覆寫方法 Ctrl + O Ctrl + O
快捷定位到行首/尾 Cmd + Left/Right Ctrl + Left/Right
折疊展開代碼塊 Cmd + Plus,Minus Ctrl + Plus/Minus
折疊展開全部代碼塊 Cmd + Shift + Plus,Minus Ctrl + Shift + Plus,Minus
文件方法結(jié)構(gòu) Cmd + F12 Ctrl + F12
查找調(diào)用的位置 Ctrl + Option + H Ctrl + Alt + H
大小寫轉(zhuǎn)換 Cmd + Shift + U Ctrl + Shift + U

11.CoordinatorLayout相關(guān)注意事項

  1. CoordinatorLayout繼承自viewgroup,但是使用類似于framLayout,有層次結(jié)構(gòu),后面的布局會覆蓋在前面的布局之上,但跟behavior屬性也有很大關(guān)系,的app:layout_behavior屬性,只有CoordinatorLayout的直接子布局才能響應(yīng),所以不要做徒勞無功的事

  2. CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout結(jié)合起來才能產(chǎn)生這么神奇的效果,不要想當(dāng)然的光拿著CoordinatorLayout就要玩出來(剛接觸的時候我也有這種想法),累死你

  3. AppBarLayout:繼承自lineLayout,使用時其他屬性隨lineLayou,已經(jīng)響應(yīng)了CoordinatorLayout的layout_behavior屬性,所以他能搞出那么多特效來

  4. AppBarLayout的直接子控件可以設(shè)置的屬性:layout_scrollFlags

1.scroll|exitUntilCollapsed如果AppBarLayout的直接子控件設(shè)置該屬性,該子控件可以滾動,向上滾動NestedScrollView出父布局(一般為CoordinatorLayout)時,會折疊到頂端,向下滾動時NestedScrollView必須滾動到最上面的時候才能拉出該布局
2.scroll|enterAlways:只要向下滾動該布局就會顯示出來,只要向上滑動該布局就會向上收縮
3.scroll|enterAlwaysCollapsed:向下滾動NestedScrollView到最底端時該布局才會顯示出來
4.如果不設(shè)置改屬性,則改布局不能滑動

  1. CollapsingToolbarLayout,字面意思是折疊的toolbar,它確實是起到折疊作用的,可以把自己的自布局折疊 繼承自framLayout,所以它的直接子類可以設(shè)置layout_gravity來控制顯示的位置,它的直接子布局可以使用的屬性:app:layout_collapseMode(折疊模式):可取的值如下:

1.pin:在滑動過程中,此自布局會固定在它所在的位置不動,直到CollapsingToolbarLayout全部折疊或者全部展開
2.parallax:視察效果,在滑動過程中,不管上滑還是下滑都會有視察效果,不知道什么事視察效果自己看gif圖(layout_collapseParallaxMultiplier視差因子 0~1之間取值,當(dāng)設(shè)置了parallax時可以配合這個屬性使用,調(diào)節(jié)自己想要的視差效果)
3.不設(shè)置:跟隨NestedScrollView的滑動一起滑動,NestedScrollView滑動多少距離他就會跟著走多少距離

  1. toobar最好是放在CollapsingToolbarLayout,也不是沒有其他用法,但是在這套系統(tǒng)中一般只能放在CollapsingToolbarLayout里面,才能達(dá)到好的效果,這里toolbar同時設(shè)置layout_gravity和app:layout_collapseMode時有一些比較復(fù)雜的情況.不一一作介紹,因為一般我們只會把toolbar放在最上面(不用設(shè)置layout_gravity屬性,默認(rèn)的),并且設(shè)置app:layout_collapseMode為pin,讓他固定在最頂端,有興趣的自己試試其他情況,

  2. 告你一個驚天大冪冪:只要CollapsingToolbarLayout里面包含有toolbar那么CollapsingToolbarLayout的折疊后高度就是toolbar的高度,相當(dāng)于CollapsingToolbarLayout設(shè)置了minHeight屬性,

  3. 再告訴你一個驚天大咪咪:CollapsingToolbarLayout折疊到最頂端時,它就是老大,會處于最上層,包括toolbar在內(nèi),所有的布局都會被他蓋住,顯示不出來.

  4. CollapsingToolbarLayout 自己的屬性 說明,不是直接子布局可用的,是自己可以用的屬性
    contentScrim折疊后的顏色也是展開時的漸變顏色,效果超贊.
    title標(biāo)題,如果設(shè)置在折疊時會動畫的顯示到折疊后的部分上面,拉開時會展開,很好玩的
    expandedTitleMargin當(dāng)title文字展開時文字的margin,當(dāng)然還有marginTop等屬性,腦補吧
    app:collapsedTitleTextAppearance=”@style/Text”折疊時title的樣式里面可以定義字體大小顏色等
    app:collapsedTitleTextAppearance=”@style/Text1”折疊時title的樣式里面可以定義字體大小顏色等
    當(dāng)然還有一些,自己試試吧,現(xiàn)在的這些已經(jīng)完全夠用了

  5. 還有最后一個問題:怎么實現(xiàn)固定表頭,這個也簡單,寫一個布局放在CollapsingToolbarLayout之后,AppBarLayout之內(nèi)即可,xml文件中自己找找看吧.你要是問如果放在CollapsingToolbarLayout之前,AppBarLayout之內(nèi)會怎么樣?這樣折疊布局就不起作用了.不會折疊了.
    https://blog.csdn.net/qq_31340657/article/details/51918773

12. startActivities

    fun multiIntent(view : View) {
        val intent_1   = Intent(this,IntentActivityA::class.java)
        val intent_2   = Intent(this,IntentActivityB::class.java)
        val intent_3   = Intent(this,IntentActivityC::class.java)
        
        startActivities(arrayOf(intent_1,intent_2,intent_3))
    }
image
 IntentActivity C onCreate
 IntentActivity B onCreate
 IntentActivity C onDestroy
 IntentActivity A onCreate
 IntentActivity B onDestroy
 IntentActivity A onDestroy

注意不是全部創(chuàng)建壓入棧里的甩栈。相鄰原則I殉丁!谤职!

13.代碼中更改margin

FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) tv.getLayoutParams();
layoutParams.setMargins(0, 0, DisplayUtils.dip2px(Main2Activity.this,12), DisplayUtils.dip2px(Main2Activity.this,12));
tv.setLayoutParams(layoutParams);

注意:getLayoutParams()獲取到的是tv的父布局的類型!R谙省T黍凇!蒿柳!如果它的父布局是FrameLayout而你轉(zhuǎn)成LinearLayout.LayoutParams饶套。程序會報錯!

14.Gson中的TypeToken使用

List<AdBean> result = new Gson().fromJson(cacheContent, new TypeToken<List<AdBean>>() { }.getType());

使用它可以將String轉(zhuǎn)換成List數(shù)組結(jié)構(gòu)垒探。

15. SparseArray和SparseArrayCompat

SparseArray以鍵值對的形式保存數(shù)據(jù)妓蛮,key是int類型,并且是唯一的不允許重復(fù)的key圾叼,而value可以是任何object蛤克。

優(yōu)點
相比HashMap更加節(jié)省內(nèi)存空間,數(shù)據(jù)存儲只依賴key和value2個數(shù)組夷蚊,數(shù)組空間是可復(fù)用的构挤,數(shù)據(jù)的存儲密度較高。
因為key數(shù)組是有序的惕鼓,通過key獲取value相對高效筋现。

缺點:
key數(shù)組是保持有序的,在插入和查找時箱歧,通過二分法確定位置key的下標(biāo)矾飞,比較耗時;
插入刪除等操作可能會移動數(shù)組數(shù)據(jù)呀邢。

綜合來說洒沦,SparseArray適用于小數(shù)據(jù)量的鍵值對場景。數(shù)據(jù)量達(dá)到幾百時驼鹅,效率優(yōu)勢和HashMap相比已不明顯微谓。

SparseArray只能在API19以上的系統(tǒng)里面 才有這個類森篷,也就是Android4.4以上。所以Android為我們提供了一個兼容的類SparseArrayCompat豺型,使用這個可以兼容更低的版本仲智。

    private static final int BASE_ITEM_TYPE_HEADER = 100000;
    
    private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();

    public void addHeaderView(View view) {
        mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
    }

    @Override
    public int getItemViewType(int position) {
        if (isHeaderViewPos(position)) {
            return mHeaderViews.keyAt(position);
        } else if (isFooterViewPos(position)) {
            return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
        }
        return mInnerAdapter.getItemViewType(position - getHeadersCount());
    }

16.隱藏狀態(tài)欄

 <style name="AppLaunch" parent="@android:style/Theme.Light.NoTitleBar.Fullscreen">
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowBackground">@drawable/app_window_bg</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>

設(shè)置全屏?xí)詣与[藏狀態(tài)欄!R霭薄钓辆!但是不會隱藏標(biāo)題欄,所以想要全屏需要windowNoTitle肴焊、windowFullscreen一起設(shè)置前联!

17.List<T>中的T可以為接口

效果同List<?extends MyInterface>

18.從View中獲取Context的正確姿勢

在繼承自AppCompatActivity時娶眷,Android底層會將我們應(yīng)用的控件轉(zhuǎn)為v7包中對應(yīng)的控件似嗤,Context就被替換成了TintContextWrapper(它繼承自ContextWrapper)。
或者
View 可能繼承自 AppCompat 系的 View (比如 AppCompatTextView, AppCompatImageView)届宠,此時的context存儲于TintContextWrapper的getBaseContext中烁落,所以需要以下處理:

        Context context = v.getContext();
        if (context instanceof TintContextWrapper) {
            context = ((TintContextWrapper) context).getBaseContext();
        }
        if (context instanceof Activity) {
            ((Activity) context).finish();
        }

或者

   public static Activity getActivityFromView(View view) {
        Context context = view.getContext();
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity) context;
            }
            context = ((ContextWrapper) context).getBaseContext();
        }
        return null;
    }

Android 從 View 中獲取 Activity 時遇到 TintContextWrapper cannot be cast to 的問題

19.文件讀寫操作時,先進(jìn)行判斷

   public static String getCachePath(Context context) {
        if (Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())) {
            // 有sd卡
            try {
                return context.getExternalCacheDir().getAbsolutePath();
            } catch (Exception e) {
                e.printStackTrace();
                return context.getCacheDir().getAbsolutePath();
            }
        } else {
            // 無sd卡
            return context.getCacheDir().getAbsolutePath();
        }
    }

20.點擊通知的跳轉(zhuǎn)邏輯

startActivities(new Intent[]{mainIntent, targetIntent});

這樣的話豌注,先跳轉(zhuǎn)目標(biāo)頁伤塌,點擊返回跳轉(zhuǎn)到主頁面。

21.RecyclerView的小技巧

使內(nèi)容等間距排列轧铁,spanCount是個數(shù)每聪,spacing是兩個Item的間距。

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view); // item position
        int column = position % spanCount; // item column
        outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
        outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
        if (position >= spanCount) {
            outRect.top = spacing; // item top
        }
    }

22.判斷intent是否能跳轉(zhuǎn)/是否有效

    public static boolean checkIntent(Intent intent) {
        return checkIntent(intent, PackageManager.GET_INTENT_FILTERS);
    }


    public static boolean checkIntent(Intent intent, int flags) {
        PackageManager pm = RootInit.getInstance().getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(intent, flags);
        final int size = (activities == null) ? 0 : activities.size();
        return size > 0;
    }

23.WebView配置

private void initWebViewSetting() {
        WebSettings ws = getSettings();
        try {
            ws.setJavaScriptEnabled(true);
            // Logger
        } catch (Exception e) {
            e.printStackTrace();
        }
        ws.setSupportZoom(false);
        String appCacheDir = Paths.getInternalCachePath(getContext());
        ws.setDomStorageEnabled(true);
        ws.setAppCacheMaxSize(1024 * 1024 * 4);//設(shè)置緩沖
        ws.setAppCachePath(appCacheDir);
        ws.setAllowFileAccess(true);
        ws.setAppCacheEnabled(true);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ws.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }
        ws.setJavaScriptCanOpenWindowsAutomatically(true);

        ws.setDefaultTextEncodingName("utf-8");
        ws.setUseWideViewPort(true);
        ws.setLoadWithOverviewMode(true);
        ws.setBuiltInZoomControls(false);
        // webView.setBackgroundColor(0);
        ws.setCacheMode(WebSettings.LOAD_NO_CACHE);
        removeJavascriptInterface("searchBoxJavaBridge_");
        removeJavascriptInterface("accessibility");
        removeJavascriptInterface("accessibilityTraversal");
        ws.setAllowFileAccess(true);// 設(shè)置允許訪問文件數(shù)據(jù)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            ws.setAllowUniversalAccessFromFileURLs(false);
            ws.setAllowFileAccessFromFileURLs(false);
        }
        setWebChromeClient(new BpWebChromeClient());
        setWebViewClient(new BpWebViewClient());
        setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadStart(String url, String userAgent, String contentDisposition,
                                        String mimetype, long contentLength) {
                if (null != url) {
                    Uri uri = Uri.parse(url);
                    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                    if (ToolBox.checkIntent(intent)) {
                        try {
                            getActivity().startActivity(intent);
                        } catch (Throwable e) {
                            YLog.e(e);
                        }
                    }
                }
            }
        });
    }

24. PopupWindow 沉浸式

pop.setClippingEnabled(false)

25. Dialog 沉浸式

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
    dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
    dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}

26.android studio預(yù)覽可視化

 if (thisView.isInEditMode()) {//這段代碼在運行時不會執(zhí)行齿风,只會在Studio編輯預(yù)覽時運行药薯,不用在意性能問題
            final int d = SmartUtil.dp2px(5);
            final Context context = thisView.getContext();

            Paint paint = new Paint();
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(0xcccccccc);
            paint.setStrokeWidth(SmartUtil.dp2px(1));
            paint.setPathEffect(new DashPathEffect(new float[]{d, d, d, d}, 1));
            canvas.drawRect(d, d, thisView.getWidth() - d, thisView.getBottom() - d, paint);

            TextView textView = new TextView(context);
            textView.setText(context.getString(R.string.srl_component_falsify, getClass().getSimpleName(), SmartUtil.px2dp(thisView.getHeight())));
            textView.setTextColor(0xcccccccc);
            textView.setGravity(Gravity.CENTER);
            //noinspection UnnecessaryLocalVariable
            View view = textView;
            view.measure(makeMeasureSpec(thisView.getWidth(), EXACTLY), makeMeasureSpec(thisView.getHeight(), EXACTLY));
            view.layout(0, 0, thisView.getWidth(), thisView.getHeight());
            view.draw(canvas);
        }

27.descendantFocusability子布局獲取焦點

beforeDescendants:viewgroup會優(yōu)先其子類控件而獲取到焦點
afterDescendants:viewgroup只有當(dāng)其子類控件不需要獲取焦點時才獲取焦點
blocksDescendants:viewgroup會覆蓋子類控件而直接獲得焦點

注意:它只在RelativeLayout中設(shè)置生效!>劝摺果善!

28.注解生命周期

  1. RetentionPolicy.SOURCE源碼注解,編譯成.class文件后注解就不存在系谐,用來提示開發(fā)者
  2. RetentionPolicy.CLASS CLASS匯編注解巾陕,編譯成.class文件后注解也還存在,用于自動生成代碼
  3. RetentionPolicy.RUNTIME 運行時動態(tài)注解纪他,生命周期一直程序運行時都存在鄙煤,常用于自動注入

29.繪制完加載布局

        final View layout = findViewById(Window.ID_ANDROID_CONTENT);
        ViewTreeObserver vto = layout.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                } else {
                    layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }
                initUIandEvent();
            }
        });

30.光暈效果(蒙層)

BlurMaskFilter

/**
 * This takes a mask, and blurs its edge by the specified radius. Whether or
 * or not to include the original mask, and whether the blur goes outside,
 * inside, or straddles, the original mask's border, is controlled by the
 * Blur enum.
 */
 /* 翻譯成大白話的意思就是BlurMaskFilter可以在原本的View上添加一層指定模糊半徑的蒙層,具體模糊的方式茶袒,由Blur枚舉類型控制 */
// 光暈的paint
private val outPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        // 光暈的顏色
        color = Color.parseColor("#e6e8db")
        // 使用BlurMaskFilter制作陰影效果
        maskFilter = BlurMaskFilter(shadowRadius.toFloat(), BlurMaskFilter.Blur.SOLID)
    }

31.RecyclerView融邊效果

android:fadingEdgeLength="30dp"
android:requiresFadingEdge="horizontal"

32.DialogFragment全屏顯示

        final Window window = getDialog().getWindow();
        window.setBackgroundDrawableResource(R.color.transparent);
        window.getDecorView().setPadding(0, 0, 0, 0);
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;

注意:第二行和第三行必須設(shè)置才能全屏梯刚,親測!

33. 自定義控件調(diào)整位置

offsetTopAndBottom薪寓、offsetLeftAndRight

43.鍵盤彈出背景不動

手機屏幕的高為1920px亡资,那么整個Activity的布局高度也為1920px澜共。當(dāng)設(shè)置該屬性后點擊界面中的EditText,此時彈出軟鍵盤其高度為800px锥腻。為了完整地顯示此軟鍵盤搔体,系統(tǒng)會調(diào)整Activity布局的高度為1920px-800px=1120px置森。

當(dāng) windowSoftInputMode 被設(shè)置為 adjustResize 時候,當(dāng)布局調(diào)整的時候被調(diào)整的布局均會重繪制,并走了onMeasure绳泉,onSizeChanged蓄氧,onLayout 弧满。
當(dāng) windowSoftInputMode 被設(shè)置為 adjustPan 時候俊庇,當(dāng)布局調(diào)整的時候被調(diào)整的布局均會重繪制,并走了onMeasure, onLayout 甲葬。

這里只需要注意 兩者都走了 onMeasure 方法廊勃,至于 adjustPan 沒走 onSizeChanged。

所以只需重寫onMeasure方法经窖,即可實現(xiàn)供搀。

44.PopupWindow透傳致其它View響應(yīng)點擊事件

解決:只需要讓PopupWindow持有焦點即可。

this.setFocusable(true);

//最后一個參數(shù)為true
new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);

45. dialog相關(guān)背景設(shè)置

1.設(shè)置透明度(Dialog自身的透明度)

WindowManager.LayoutParams lp=dialog.getWindow().getAttributes();
lp.alpha=1.0f;
dialog.getWindow().setAttributes(lp);

alpha在0.0f到1.0f之間钠至。1.0完全不透明,0.0f完全透明

2.設(shè)置黑暗度(Dialog自身的黑暗度)

dialog.setContentView(R.layout.dialog);
WindowManager.LayoutParams lp=dialog.getWindow().getAttributes();
lp.dimAmount=1.0f;
dialog.getWindow().setAttributes(lp);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

dimAmount在0.0f和1.0f之間胎源,0.0f完全不暗棉钧,1.0f全暗

3.設(shè)置Dialog底背景模糊和黑暗度

WindowManager.LayoutParams.FLAG_BLUR_BEHIND(設(shè)置模糊)

WindowManager.LayoutParams.FLAG_DIM_BEHIND(設(shè)置暗淡)

4.清除Dialog底背景模糊和黑暗度

getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND | WindowManager.LayoutParams.FLAG_DIM_BEHIND)

46.防止多點觸控

android:splitMotionEvents=false
全局禁用:

<style name="MyStyle">
      <item name="android:windowEnableSplitTouch">false</item>
      <item name="android:splitMotionEvents>false</item>
</style>

47.DialogFragment 在5.0手機上適配的各種問題

如果出現(xiàn)彈窗在5.0手機上位置不對,設(shè)置Gravity不好使涕蚤,大小尺寸不對宪卿,你可能需要如下設(shè)置來解決
1、布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/add_friend_popup_bg">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="0.5dp"
            android:layout_marginBottom="0.5dp"
            android:fadeScrollbars="true" />
    </FrameLayout>
</FrameLayout>

你需要在內(nèi)容外部在再套一層万栅,這樣彈窗大小的問題就能解決佑钾!

2、設(shè)置主題

   @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 必須設(shè)置烦粒,不然會出現(xiàn)5.0及以下手機不適配
        setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogStyle);
    }
    <style name="DialogStyle" parent="@android:style/Theme.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:backgroundDimEnabled">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>

5.0及以下手機dialogFragment 有默認(rèn)主題休溶,顯示位置不對一般是這個導(dǎo)致的!

48.虛擬導(dǎo)航鍵顯示判斷

    public static boolean isNavBarVisible(Context context) {
        ViewGroup rootLinearLayout = findRootLinearLayout(context);
        int navigationBarHeight = 0;
        if (rootLinearLayout != null) {
            ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) rootLinearLayout.getLayoutParams();
            navigationBarHeight = layoutParams.bottomMargin;
        }
        return navigationBarHeight != 0;
    }

    private static ViewGroup findRootLinearLayout(Context context) {
        Activity activity = ActivityUtils.getActivityByContext(context);
        if (activity != null) {
            Window window = activity.getWindow();
            if (window != null) {
                ViewGroup decorView = (ViewGroup) window.getDecorView();
                if (decorView != null) {
                    View contentView = activity.findViewById(android.R.id.content);
                    if (contentView != null) {
                        ViewGroup parent = (ViewGroup) contentView.getParent();
                        while (parent != decorView) {
                            if (parent == null) {
                                return null;
                            }
                            if (parent instanceof LinearLayout && !(parent instanceof FitWindowsLinearLayout)) {
                                return parent;
                            }
                            parent = (ViewGroup) parent.getParent();
                        }
                    }
                }
            }
        }
        return null;
    }

51.recyclerView禁用多指

android:splitmotionevents=”false”
或者
recyclerView.setMotionEventSplittingEnabled(false);

52.recyclerView滑動到指定位置,并指定位置在頂部

1.第一種方法
此方法能實現(xiàn)指定位置位于屏幕頂部,但是不具有平滑滾動視覺效果:

 if (position != -1) {
                    mRecycleview.scrollToPosition(position);
                    LinearLayoutManager mLayoutManager =
                            (LinearLayoutManager) mRecycleview.getLayoutManager();
                    mLayoutManager.scrollToPositionWithOffset(position, 0);
                }

2.第二種方法
此方法能實現(xiàn)指定位置位于屏幕頂部,具有平滑滾動視覺效果:

首先獲取第一個可見位置和最后一個可見位置,分三種情況:
1.如果如果跳轉(zhuǎn)位置在第一個可見位置之前扰她,就smoothScrollToPosition()可以直接跳轉(zhuǎn);
2.如果跳轉(zhuǎn)位置在第一個可見項之后兽掰,最后一個可見項之前smoothScrollToPosition()不會滾動,此時調(diào)用smoothScrollBy來滑動到指定位置;
3.如果要跳轉(zhuǎn)的位置在最后可見項之后徒役,則先調(diào)用smoothScrollToPosition()將要跳轉(zhuǎn)的位置滾動到可見位置,在addOnScrollListener()里通過onScrollStateChanged控制,調(diào)用smoothMoveToPosition孽尽,再次執(zhí)行判斷;

 //目標(biāo)項是否在最后一個可見項之后
    private boolean mShouldScroll;
    //記錄目標(biāo)項位置
    private int mToPosition;
    /**
     * 滑動到指定位置
     */
    private void smoothMoveToPosition(RecyclerView mRecyclerView, final int position) {
        // 第一個可見位置
        int firstItem = mRecyclerView.getChildLayoutPosition(mRecyclerView.getChildAt(0));
        // 最后一個可見位置
        int lastItem = mRecyclerView.getChildLayoutPosition(mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1));
        if (position < firstItem) {
            // 第一種可能:跳轉(zhuǎn)位置在第一個可見位置之前
            mRecyclerView.smoothScrollToPosition(position);
        } else if (position <= lastItem) {
            // 第二種可能:跳轉(zhuǎn)位置在第一個可見位置之后
            int movePosition = position - firstItem;
            if (movePosition >= 0 && movePosition < mRecyclerView.getChildCount()) {
                int top = mRecyclerView.getChildAt(movePosition).getTop();
                mRecyclerView.smoothScrollBy(0, top);
            }
        } else {
            // 第三種可能:跳轉(zhuǎn)位置在最后可見項之后
            mRecyclerView.smoothScrollToPosition(position);
            mToPosition = position;
            mShouldScroll = true;
        }
    }

 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (mShouldScroll&& RecyclerView.SCROLL_STATE_IDLE == newState) {
                    mShouldScroll = false;
                    smoothMoveToPosition(irc, mToPosition);
                }
            }
        });
if (position != -1) {
                    smoothMoveToPosition(irc,position);
                }else {
                    smoothMoveToPosition(irc,position+1);
                }

53.RecyclerVIew內(nèi)的Item布局超出來了?

事情是這樣的忧勿,項目升級androidx后發(fā)現(xiàn)杉女,RecyclerVIew內(nèi)的Item布局超出來瞻讽,即外層限制不住它了,相關(guān)代碼:

 GridLayoutManager.LayoutParams params = (GridLayoutManager.LayoutParams) rvVoiceRoom.getLayoutParams();
            //超過一行需要動態(tài)修改左邊和底部的距離
            params.setMargins(DisplayUtil.dip2px(getContext(), 14), 0, 0, 0);
            rvVoiceRoom.setLayoutParams(params);
            GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount, GridLayoutManager.HORIZONTAL, false);
            rvVoiceRoom.setLayoutManager(gridLayoutManager);

明明限制了左邊距是14dp熏挎,為什么顯示效果是直接加在第一個item里了呢速勇,無法限制整個滑動的區(qū)域。
查看布局文件發(fā)現(xiàn)婆瓜,這個RecyclerView沒有父布局快集。

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rv_voice_room"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:overScrollMode="never"/>

由此可以想到,應(yīng)該是androidx修改了RecyclerView的源碼廉白,當(dāng)RecyclerView沒有父布局時个初,對他的layoutParam(GridLayoutManager.LayoutParams)進(jìn)行修改,相當(dāng)于在加在第一個item(最后一個 item里)猴蹂。
故院溺,修復(fù)這個問題也很簡單,套一層父布局即可解決磅轻,布局修改如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_voice_room"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:overScrollMode="never" />

</FrameLayout>

別忘了修改params的類型

FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) rvVoiceRoom.getLayoutParams();

效果和分析的一樣珍逸,確實是這個原因。有時間還是比較下源碼聋溜,MarkW簧拧!撮躁!todo

54.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末漱病,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子把曼,更是在濱河造成了極大的恐慌杨帽,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嗤军,死亡現(xiàn)場離奇詭異注盈,居然都是意外死亡,警方通過查閱死者的電腦和手機叙赚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門老客,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人震叮,你說我怎么就攤上這事沿量。” “怎么了冤荆?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵朴则,是天一觀的道長。 經(jīng)常有香客問我,道長乌妒,這世上最難降的妖魔是什么汹想? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮撤蚊,結(jié)果婚禮上古掏,老公的妹妹穿的比我還像新娘。我一直安慰自己侦啸,他們只是感情好槽唾,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著光涂,像睡著了一般庞萍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忘闻,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天钝计,我揣著相機與錄音,去河邊找鬼齐佳。 笑死私恬,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的炼吴。 我是一名探鬼主播本鸣,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼硅蹦!你這毒婦竟也來了荣德?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤提针,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后曹傀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辐脖,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年皆愉,在試婚紗的時候發(fā)現(xiàn)自己被綠了嗜价。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡幕庐,死狀恐怖久锥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情异剥,我是刑警寧澤瑟由,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站冤寿,受9級特大地震影響歹苦,放射性物質(zhì)發(fā)生泄漏青伤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一殴瘦、第九天 我趴在偏房一處隱蔽的房頂上張望狠角。 院中可真熱鬧,春花似錦蚪腋、人聲如沸丰歌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽立帖。三九已至,卻和暖如春神得,著一層夾襖步出監(jiān)牢的瞬間厘惦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工哩簿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宵蕉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓节榜,卻偏偏與公主長得像羡玛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宗苍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容