?
感謝開源,感謝大神,才讓我們這些菜鳥成長!
附上云閱開源項目地址:點我吧。
?
1.輪播圖的實現。
現在的APP基本都會實現這個功能吧,然后一直都找不到好的第三方庫,能夠滿足各種需求。然而碰到了這個開源庫...
- gradle配置: implementation 'com.youth.banner:banner:1.4.9'
- github地址: https://github.com/youth5201314/banner
- 參考文章: Android-圖片輪播(banner)
?
?
2.MVVM-DataBinding架構開發。
它本質上就是MVC、MVP的改進版。實現數據和視圖相互綁定。MVVM就是講其中的View的狀態和行為抽象化,讓我們將視圖UI和業務邏輯分開。
- gradle配置:? ? (在module的build.gradle中的android模塊中加入如下代碼塊)
dataBinding {enabled =
true} - 官方地址: https://developer.android.google.cn/topic/libraries/data-binding/index.html
- 中文翻譯地址:? ? ??https://blog.csdn.net/jjwwmlp456/article/details/54915981
- 參考文章:? ? ? ?https://www.jianshu.com/p/ba4982be30f8
?
?
3.解決Bug:Android點擊應用圖標后會重新進入啟動頁。
- 問題描述:發現應用打包安裝后按home鍵切換到后臺后,點擊應用圖標又重新打開了一個,并沒有回到原來的界面。
- 解決方案與參考文章:https://blog.csdn.net/jianiuqi/article/details/54091181
?
?
4.第三方庫Glide的簡單使用。
implementation
'com.github.bumptech.glide:glide:4.7.1' - Glide參考文章:https://mrfu.me/2016/02/27/Glide_Getting_Started/
- Glide的github地址:https://github.com/bumptech/glide
- 處理圖片效果的Glide庫,如果選擇這個第三方庫,則無需添加glide,它里面自帶就有glide
implementation
'jp.wasabeef:glide-transformations:2.0.1 - 處理圖片效果的github地址:https://github.com/wasabeef/picasso-transformations
- 圖片處理參考文章:https://www.jianshu.com/p/976c86fa72bc
?
5.分包策略,解決65535方法限制的問題。
implementation
'com.android.support:multidex:1.0.3' - 參考文章:https://blog.csdn.net/djy1992/article/details/51162013
- 官網文檔:https://developer.android.com/tools/building/multidex.html#about
?
?
6.避免在1s中多次點擊按鈕。
方法:自己新建一個抽象類,集成OnClickListener,重寫里面的onClick函數。
public abstract class PerfectClickListener
implements OnClickListener {public static final int MIN_CLICK_DELAY_TIME = 1000
;private long lastClickTime = 0
;private int id = -1
;@Overridepublic void onClick(View v) {long currentTime =
Calendar.getInstance().getTimeInMillis();int mId =
v.getId();if (id !=
mId) {id =
mId;lastClickTime =
currentTime;onNoDoubleClick(v);return;}if (currentTime - lastClickTime >
MIN_CLICK_DELAY_TIME) {lastClickTime =
currentTime;onNoDoubleClick(v);}}protected abstract void onNoDoubleClick(View v);
} View Code 使用場景:在啟動頁中,有一個跳轉按鈕,避免用戶多次點擊啟動多個主頁。
?
?
7.如何使用android.support.v7.widget.Toolbar
implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
implementation "com.android.support:design:$rootProject.supportLibraryVersion"
implementation "com.android.support:support-v4:$rootProject.supportLibraryVersion"
implementation "com.android.support:cardview-v7:$rootProject.supportLibraryVersion"
這里使用了rootProject來間接設置,注意這個rootProject要在項目的build.gradle中具體配置。具體百度吧。
<
android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorTheme"
app:contentInsetStart="0.0dp"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ToolbarStyle">
這里使用了自定義主題,在style文件中自己創建一個。
- 對于popupTheme具體含義參考這篇文章:https://blog.csdn.net/lovexieyuan520/article/details/48931185
- 具體使用方法參考這篇文章:https://blog.csdn.net/javazejian/article/details/50451853
?
?
?
8.設置主題點擊波紋效果。
- 首先得引入谷歌的材料設計庫,上面第7點中有相關引入方式。
- 然后再布局文件中:android:background="?attr/...."? ?(這里省略號寫一些波紋樣式即可)
- 參考文章:https://yq.aliyun.com/articles/12407
?
?
9.左右滑動ViewPager時,沒獲取焦點沒響應。
原來僅僅是在定義Viewpager的時候,添加如下代碼即可。
android:descendantFocusability="blocksDescendants"
- 參考文章:https://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html
- 參數意義:
beforeDescendants:viewgroup會優先其子類控件而獲取到焦點afterDescendants:viewgroup只有當其子類控件不需要獲取焦點時才獲取焦點blocksDescendants:viewgroup會覆蓋子類控件而直接獲得焦點 ?
?
10.設置狀態欄顏色以及其他方法。
- 首先自定義一個視圖叫做StatusBarView。
public class StatusBarView
extends View {public StatusBarView(Context context, AttributeSet attrs) {super(context, attrs);}public StatusBarView(Context context) {super(context);}
} public class StatusBarUtil {public static final int DEFAULT_STATUS_BAR_ALPHA = 112
;/*** 設置狀態欄顏色** @param activity 需要設置的 activity* @param color 狀態欄顏色值*/public static void setColor(Activity activity, @ColorInt
int color) {setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA);}/*** 設置狀態欄顏色** @param activity 需要設置的activity* @param color 狀態欄顏色值* @param statusBarAlpha 狀態欄透明度*/public static void setColor(Activity activity, @ColorInt
int color,
int statusBarAlpha) {if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));} else if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);ViewGroup decorView =
(ViewGroup) activity.getWindow().getDecorView();int count =
decorView.getChildCount();if (count > 0 && decorView.getChildAt(count - 1)
instanceof StatusBarView) {decorView.getChildAt(count - 1
).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));} else {StatusBarView statusView =
createStatusBarView(activity, color, statusBarAlpha);decorView.addView(statusView);}setRootView(activity);}}/*** 設置狀態欄純色 不加半透明效果** @param activity 需要設置的 activity* @param color 狀態欄顏色值*/public static void setColorNoTranslucent(Activity activity, @ColorInt
int color) {setColor(activity, color, 0
);}/*** 設置狀態欄顏色(5.0以下無半透明效果,不建議使用)** @param activity 需要設置的 activity* @param color 狀態欄顏色值*/@Deprecatedpublic static void setColorDiff(Activity activity, @ColorInt
int color) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 生成一個狀態欄大小的矩形ViewGroup decorView =
(ViewGroup) activity.getWindow().getDecorView();int count =
decorView.getChildCount();if (count > 0 && decorView.getChildAt(count - 1)
instanceof StatusBarView) {decorView.getChildAt(count - 1
).setBackgroundColor(color);} else {StatusBarView statusView =
createStatusBarView(activity, color);decorView.addView(statusView);}setRootView(activity);}/*** 使狀態欄半透明* <p>* 適用于圖片作為背景的界面,此時需要圖片填充到狀態欄** @param activity 需要設置的activity*/public static void setTranslucent(Activity activity) {setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA);}/*** 使狀態欄半透明* <p>* 適用于圖片作為背景的界面,此時需要圖片填充到狀態欄** @param activity 需要設置的activity* @param statusBarAlpha 狀態欄透明度*/public static void setTranslucent(Activity activity,
int statusBarAlpha) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}setTransparent(activity);addTranslucentView(activity, statusBarAlpha);}/*** 針對根布局是 CoordinatorLayout, 使狀態欄半透明* <p>* 適用于圖片作為背景的界面,此時需要圖片填充到狀態欄** @param activity 需要設置的activity* @param statusBarAlpha 狀態欄透明度*/public static void setTranslucentForCoordinatorLayout(Activity activity,
int statusBarAlpha) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}transparentStatusBar(activity);addTranslucentView(activity, statusBarAlpha);}/*** 設置狀態欄全透明** @param activity 需要設置的activity*/public static void setTransparent(Activity activity) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}transparentStatusBar(activity);setRootView(activity);}/*** 使狀態欄透明(5.0以上半透明效果,不建議使用)* <p>* 適用于圖片作為背景的界面,此時需要圖片填充到狀態欄** @param activity 需要設置的activity*/@Deprecatedpublic static void setTranslucentDiff(Activity activity) {if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT) {// 設置狀態欄透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);setRootView(activity);}}/*** 為DrawerLayout 布局設置狀態欄變色** @param activity 需要設置的activity* @param drawerLayout DrawerLayout* @param color 狀態欄顏色值*/public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt
int color) {setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA);}/*** 為DrawerLayout 布局設置狀態欄顏色,純色** @param activity 需要設置的activity* @param drawerLayout DrawerLayout* @param color 狀態欄顏色值*/public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt
int color) {setColorForDrawerLayout(activity, drawerLayout, color, 0
);}/*** 為DrawerLayout 布局設置狀態欄變色** @param activity 需要設置的activity* @param drawerLayout DrawerLayout* @param color 狀態欄顏色值* @param statusBarAlpha 狀態欄透明度*/public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt
int color,int statusBarAlpha) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().setStatusBarColor(Color.TRANSPARENT);} else {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}// 生成一個狀態欄大小的矩形// 添加 statusBarView 到布局中ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0
);if (contentLayout.getChildCount() > 0 && contentLayout.getChildAt(0)
instanceof StatusBarView) {contentLayout.getChildAt(0
).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));} else {StatusBarView statusBarView =
createStatusBarView(activity, color);contentLayout.addView(statusBarView, 0
);}// 內容布局不是 LinearLayout 時,設置padding topif (!(contentLayout
instanceof LinearLayout) && contentLayout.getChildAt(1) !=
null) {contentLayout.getChildAt(1
).setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) +
contentLayout.getPaddingTop(),contentLayout.getPaddingRight(), contentLayout.getPaddingBottom());}// 設置屬性ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1
);drawerLayout.setFitsSystemWindows(false);contentLayout.setFitsSystemWindows(false);contentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);addTranslucentView(activity, statusBarAlpha);}/*** 為DrawerLayout 布局設置狀態欄變色(5.0以下無半透明效果,不建議使用)** @param activity 需要設置的activity* @param drawerLayout DrawerLayout* @param color 狀態欄顏色值*/@Deprecatedpublic static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt
int color) {if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 生成一個狀態欄大小的矩形ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0
);if (contentLayout.getChildCount() > 0 && contentLayout.getChildAt(0)
instanceof StatusBarView) {contentLayout.getChildAt(0
).setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA));} else {// 添加 statusBarView 到布局中StatusBarView statusBarView =
createStatusBarView(activity, color);contentLayout.addView(statusBarView, 0
);}// 內容布局不是 LinearLayout 時,設置padding topif (!(contentLayout
instanceof LinearLayout) && contentLayout.getChildAt(1) !=
null) {contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0
);}// 設置屬性ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1
);drawerLayout.setFitsSystemWindows(false);contentLayout.setFitsSystemWindows(false);contentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);}}/*** 為 DrawerLayout 布局設置狀態欄透明** @param activity 需要設置的activity* @param drawerLayout DrawerLayout*/public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA);}/*** 為 DrawerLayout 布局設置狀態欄透明** @param activity 需要設置的activity* @param drawerLayout DrawerLayout*/public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout,
int statusBarAlpha) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}setTransparentForDrawerLayout(activity, drawerLayout);addTranslucentView(activity, statusBarAlpha);}/*** 為 DrawerLayout 布局設置狀態欄透明** @param activity 需要設置的activity* @param drawerLayout DrawerLayout*/public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().setStatusBarColor(Color.TRANSPARENT);} else {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0
);// 內容布局不是 LinearLayout 時,設置padding topif (!(contentLayout
instanceof LinearLayout) && contentLayout.getChildAt(1) !=
null) {contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0
);}// 設置屬性ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1
);drawerLayout.setFitsSystemWindows(false);contentLayout.setFitsSystemWindows(false);contentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);}/*** 為 DrawerLayout 布局設置狀態欄透明(5.0以上半透明效果,不建議使用)** @param activity 需要設置的activity* @param drawerLayout DrawerLayout*/@Deprecatedpublic static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) {if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT) {// 設置狀態欄透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 設置內容布局屬性ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0
);contentLayout.setFitsSystemWindows(true);contentLayout.setClipToPadding(true);// 設置抽屜布局屬性ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1
);vg.setFitsSystemWindows(false);// 設置 DrawerLayout 屬性drawerLayout.setFitsSystemWindows(
false);}}/*** 為頭部是 ImageView 的界面設置狀態欄全透明** @param activity 需要設置的activity* @param needOffsetView 需要向下偏移的 View*/public static void setTransparentForImageView(Activity activity, View needOffsetView) {setTranslucentForImageView(activity, 0
, needOffsetView);}/*** 為頭部是 ImageView 的界面設置狀態欄透明(使用默認透明度)** @param activity 需要設置的activity* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageView(Activity activity, View needOffsetView) {setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);}/*** 為頭部是 ImageView 的界面設置狀態欄透明** @param activity 需要設置的activity* @param statusBarAlpha 狀態欄透明度* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageView(Activity activity,
int statusBarAlpha, View needOffsetView) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().setStatusBarColor(Color.TRANSPARENT);activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);if (activity
instanceof TabActivity){activity.getWindow()//兼容TabActivity
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}} else {activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}addTranslucentView(activity, statusBarAlpha);if (needOffsetView !=
null) {ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();if (layoutParams !=
null) {layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0
);}}}public static void setMargin(Activity activity, View needOffsetView) {if (needOffsetView !=
null) {ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();if (layoutParams !=
null) {layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0
);}}}/*** 為 fragment 頭部是 ImageView 的設置狀態欄透明** @param activity fragment 對應的 activity* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) {setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);}/*** 為 fragment 頭部是 ImageView 的設置狀態欄透明** @param activity fragment 對應的 activity* @param needOffsetView 需要向下偏移的 View*/public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) {setTranslucentForImageViewInFragment(activity, 0
, needOffsetView);}/*** 為 fragment 頭部是 ImageView 的設置狀態欄透明** @param activity fragment 對應的 activity* @param statusBarAlpha 狀態欄透明度* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageViewInFragment(Activity activity,
int statusBarAlpha, View needOffsetView) {setTranslucentForImageView(activity, statusBarAlpha, needOffsetView);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT <
Build.VERSION_CODES.LOLLIPOP) {clearPreviousSetting(activity);}}@TargetApi(Build.VERSION_CODES.KITKAT)private static void clearPreviousSetting(Activity activity) {ViewGroup decorView =
(ViewGroup) activity.getWindow().getDecorView();int count =
decorView.getChildCount();if (count > 0 && decorView.getChildAt(count - 1)
instanceof StatusBarView) {decorView.removeViewAt(count - 1
);ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0
);rootView.setPadding(0, 0, 0, 0
);}}/*** 添加半透明矩形條** @param activity 需要設置的 activity* @param statusBarAlpha 透明值*/private static void addTranslucentView(Activity activity,
int statusBarAlpha) {ViewGroup contentView =
(ViewGroup) activity.findViewById(android.R.id.content);if (contentView.getChildCount() > 1
) {contentView.getChildAt(1).setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0
));} else {contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha));}}/*** 生成一個和狀態欄大小相同的彩色矩形條** @param activity 需要設置的 activity* @param color 狀態欄顏色值* @return 狀態欄矩形條*/private static StatusBarView createStatusBarView(Activity activity, @ColorInt
int color) {// 繪制一個和狀態欄一樣高的矩形StatusBarView statusBarView =
new StatusBarView(activity);LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(color);return statusBarView;}/*** 生成一個和狀態欄大小相同的半透明矩形條** @param activity 需要設置的activity* @param color 狀態欄顏色值* @param alpha 透明值* @return 狀態欄矩形條*/private static StatusBarView createStatusBarView(Activity activity, @ColorInt
int color,
int alpha) {// 繪制一個和狀態欄一樣高的矩形StatusBarView statusBarView =
new StatusBarView(activity);LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));return statusBarView;}/*** 設置根布局參數*/private static void setRootView(Activity activity) {ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0
);rootView.setFitsSystemWindows(true);rootView.setClipToPadding(true);}/*** 使狀態欄透明*/@TargetApi(Build.VERSION_CODES.KITKAT)private static void transparentStatusBar(Activity activity) {if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);activity.getWindow().setStatusBarColor(Color.TRANSPARENT);} else {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}}/*** 創建半透明矩形 View** @param alpha 透明值* @return 半透明 View*/private static StatusBarView createTranslucentStatusBarView(Activity activity,
int alpha) {// 繪制一個和狀態欄一樣高的矩形StatusBarView statusBarView =
new StatusBarView(activity);LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0
));return statusBarView;}/*** 獲取狀態欄高度** @param context context* @return 狀態欄高度*/public static int getStatusBarHeight(Context context) {// 獲得狀態欄高度int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"
);return context.getResources().getDimensionPixelSize(resourceId);}/*** 計算狀態欄顏色** @param color color值* @param alpha alpha值* @return 最終的狀態欄顏色*/private static int calculateStatusColor(@ColorInt
int color,
int alpha) {float a = 1 - alpha /
255f;int red = color >> 16 & 0xff
;int green = color >> 8 & 0xff
;int blue = color & 0xff
;red = (
int) (red * a + 0.5
);green = (
int) (green * a + 0.5
);blue = (
int) (blue * a + 0.5
);return 0xff << 24 | red << 16 | green << 8 |
blue;}} View Code - 簡單用法:根據自己需求在需要的地方,StatusBarUtil.set相關的方法即可。
?
?
11.常見的item點擊的效果樣式文件。
長按或者點擊一個TextView之后,背景顏色更改,一般都是初始為白色,長按后顯示灰色。顏色值也是很講究的。
有點類似微信頁面每一個item點擊的效果,一直調不出那樣的效果。主要是找不到合適的顏色。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_enabled="false"><shape><solid android:color="#F1F3F4" /></shape></item><item android:state_pressed="true"><shape><solid android:color="#d9d9d9" /></shape></item><item android:state_pressed="false"><shape><solid android:color="#F1F3F4" /></shape></item></selector>
?
?
?12.用SharePerferences文件來記錄全局配置。
可以寫一個通用類,用來封裝不同數據類型寫入文件,從文件中獲取數據的方法。
public class SPUtils {private static final String CONFIG = "config"
;/*** 獲取SharedPreferences實例對象** @param fileName*/private static SharedPreferences getSharedPreference(String fileName) {return MyApplication.getInstance().getSharedPreferences(fileName, Context.MODE_PRIVATE);}/*** 保存一個String類型的值!*/public static void putString(String key, String value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putString(key, value).apply();}/*** 獲取String的value*/public static String getString(String key, String defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getString(key, defValue);}/*** 保存一個Boolean類型的值!*/public static void putBoolean(String key, Boolean value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putBoolean(key, value).apply();}/*** 獲取boolean的value*/public static boolean getBoolean(String key, Boolean defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getBoolean(key, defValue);}/*** 保存一個int類型的值!*/public static void putInt(String key,
int value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putInt(key, value).apply();}/*** 獲取int的value*/public static int getInt(String key,
int defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getInt(key, defValue);}/*** 保存一個float類型的值!*/public static void putFloat(String fileName, String key,
float value) {SharedPreferences.Editor editor =
getSharedPreference(fileName).edit();editor.putFloat(key, value).apply();}/*** 獲取float的value*/public static float getFloat(String key, Float defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getFloat(key, defValue);}/*** 保存一個long類型的值!*/public static void putLong(String key,
long value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putLong(key, value).apply();}/*** 獲取long的value*/public static long getLong(String key,
long defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getLong(key, defValue);}/*** 取出List<String>** @param key List<String> 對應的key* @return List<String>*/public static List<String>
getStrListValue(String key) {List<String> strList =
new ArrayList<String>
();int size = getInt(key + "size", 0
);//Log.d("sp", "" + size);for (
int i = 0; i < size; i++
) {strList.add(getString(key + i,
null));}return strList;}/*** 存儲List<String>* @param key List<String>對應的key* @param strList 對應需要存儲的List<String>*/public static void putStrListValue(String key, List<String>
strList) {if (
null ==
strList) {return;}// 保存之前先清理已經存在的數據,保證數據的唯一性
removeStrList(key);int size =
strList.size();putInt(key + "size"
, size);for (
int i = 0; i < size; i++
) {putString(key +
i, strList.get(i));}}/*** 清空List<String>所有數據** @param key List<String>對應的key*/public static void removeStrList(String key) {int size = getInt(key + "size", 0
);if (0 ==
size) {return;}remove(key + "size"
);for (
int i = 0; i < size; i++
) {remove(key +
i);}}/*** 清空對應key數據*/public static void remove(String key) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.remove(key).apply();}} View Code ? 另外,可以根據自己的需求,靈活地在文件中增加一些具體的方法。比如存儲APP的夜間模式或者日間模式,是否登錄等。
?
?
13.利用Glide畫圓角圖。
- 首先在module的build.gradle中添加依賴,前面第4點有講過。
- 然后再自定義一個轉換器,類似于下面這樣。
public class GlideCircleTransform
extends BitmapTransformation {public GlideCircleTransform(Context context) {super(context);}@Overrideprotected Bitmap transform(BitmapPool pool, Bitmap toTransform,
int outWidth,
int outHeight) {return circleCrop(pool, toTransform);}private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {if (source ==
null)
return null;int size =
Math.min(source.getWidth(), source.getHeight());int x = (source.getWidth() - size) / 2
;int y = (source.getHeight() - size) / 2
;// TODO this could be acquired from the pool tooBitmap squared =
Bitmap.createBitmap(source, x, y, size, size);Bitmap result =
pool.get(size, size, Bitmap.Config.ARGB_8888);if (result ==
null) {result =
Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);}Canvas canvas =
new Canvas(result);Paint paint =
new Paint();paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));paint.setAntiAlias(true);float r = size /
2f;canvas.drawCircle(r, r, r, paint);return result;}@Overridepublic String getId() {return getClass().getName();}
} View Code - 然后就是Glide調用這個轉換器了,類似于下面這樣。
public static void displayCircle(ImageView imageView, String imageUrl) {Glide.with(imageView.getContext()).load(imageUrl).crossFade(500
).error(R.drawable.ic_avatar_default).transform(new GlideCircleTransform(imageView.getContext())).into(imageView);} ?
?
?
14.學會添加library。
這里添加library不是在build.gradle中添加依賴,也不是在工程中添加libs文件。而是導入一個Module。
其實是一個主項目需要實現某些功能,然后將這部分功能劃分開來,最后將這部分功能集成到主項目中,也是是模塊的劃分,所以這里稱為module。
這樣的好處是:可以再主module中任何地方引用子module的圖片資源,代碼資源等等。
現在有一個地方不是特別理解,就是如何將多個library統一放在一個文件夾下面,這樣方便管理,不然都不知道哪個是主項目了。
添加單個library參考文章:https://blog.csdn.net/u014772414/article/details/51194952
?
?
?
15.學會定義BaseFragment。
主要抓住一下要點吧。
- 如果網絡異常,則頁面應該怎么顯示。
- 如果正在加載中,則頁面應該怎么顯示。
- 如何動態添加自己想要的布局。
? 所以在布局方面我們可以這樣布局。
<
RelativeLayoutandroid:id=
"@+id/container"android:layout_width=
"match_parent"android:layout_height=
"match_parent"><!--加載失敗--><
LinearLayoutandroid:id=
"@+id/ll_error_refresh"android:layout_width=
"match_parent"android:layout_height=
"match_parent"android:gravity=
"center"android:orientation=
"vertical"android:visibility=
"gone"><
ImageViewandroid:id=
"@+id/img_err"android:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:src=
"@drawable/load_err" /><
TextViewandroid:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:layout_marginTop=
"15dp"android:text=
"加載失敗,點擊重試"android:textSize=
"14sp" /></LinearLayout><!--加載中..--><
LinearLayoutandroid:id=
"@+id/ll_progress_bar"android:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:layout_centerHorizontal=
"true"android:layout_marginTop=
"80dp"android:gravity=
"center_vertical"><
ImageViewandroid:id=
"@+id/img_progress"android:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:src=
"@drawable/yun_anim" /><
TextViewandroid:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:layout_marginLeft=
"10dp"android:text=
"努力加載中..."android:textColor=
"@color/colorTabText"android:textSize=
"14sp" /></LinearLayout></RelativeLayout>
View Code 然后再BaseFragment中,這樣決定怎么隱藏怎么顯示。
public abstract class BaseFragment<SV
extends ViewDataBinding>
extends Fragment {protected SV bindingView;
//布局viewprotected boolean mIsVisible=
false;
//fragment是否顯示了private LinearLayout mLlProgressBar;
//加載中private LinearLayout mRefresh;
//加載失敗protected RelativeLayout mContainer;
//內容布局private AnimationDrawable mAnimationDrawable;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View ll = inflater.inflate(R.layout.fragment_base,
null);bindingView =
DataBindingUtil.inflate(getActivity().getLayoutInflater(),setContent(), null,
false);RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);bindingView.getRoot().setLayoutParams(params);mContainer =
ll.findViewById(R.id.container);mContainer.addView(bindingView.getRoot());//動態替換成自己想要的布局return ll;}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);mLlProgressBar =
getView(R.id.ll_progress_bar);ImageView img =
getView(R.id.img_progress);// 加載動畫mAnimationDrawable =
(AnimationDrawable) img.getDrawable();// 默認進入頁面就開啟動畫if (!
mAnimationDrawable.isRunning()) {mAnimationDrawable.start();}mRefresh =
getView(R.id.ll_error_refresh);// 點擊加載失敗布局mRefresh.setOnClickListener(
new PerfectClickListener() {@Overrideprotected void onNoDoubleClick(View v) {showLoading();onRefresh();}});bindingView.getRoot().setVisibility(View.GONE);}protected <T
extends View> T getView(
int id) {return (T) getView().findViewById(id);}/*** 加載失敗后點擊后的操作*/protected void onRefresh() {}/*** 顯示加載中狀態*/protected void showLoading() {if (mLlProgressBar.getVisibility() !=
View.VISIBLE) {mLlProgressBar.setVisibility(View.VISIBLE);}// 開始動畫if (!
mAnimationDrawable.isRunning()) {mAnimationDrawable.start();}if (bindingView.getRoot().getVisibility() !=
View.GONE) {bindingView.getRoot().setVisibility(View.GONE);}if (mRefresh.getVisibility() !=
View.GONE) {mRefresh.setVisibility(View.GONE);}}/*** 加載完成的狀態*/protected void showContentView() {if (mLlProgressBar.getVisibility() !=
View.GONE) {mLlProgressBar.setVisibility(View.GONE);}// 停止動畫if (mAnimationDrawable.isRunning()) {mAnimationDrawable.stop();}if (mRefresh.getVisibility() !=
View.GONE) {mRefresh.setVisibility(View.GONE);}if (bindingView.getRoot().getVisibility() !=
View.VISIBLE) {bindingView.getRoot().setVisibility(View.VISIBLE);}}/*** 加載失敗點擊重新加載的狀態*/protected void showError() {if (mLlProgressBar.getVisibility() !=
View.GONE) {mLlProgressBar.setVisibility(View.GONE);}// 停止動畫if (mAnimationDrawable.isRunning()) {mAnimationDrawable.stop();}if (mRefresh.getVisibility() !=
View.VISIBLE) {mRefresh.setVisibility(View.VISIBLE);}if (bindingView.getRoot().getVisibility() !=
View.GONE) {bindingView.getRoot().setVisibility(View.GONE);}}/*** 布局*/public abstract int setContent();/*** 在這里實現Fragment數據的緩加載.*/@Overridepublic void setUserVisibleHint(
boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);if (getUserVisibleHint()) {mIsVisible =
true;loadData();} else {mIsVisible =
false;}}/*** 顯示時加載數據,需要這樣的使用* 注意聲明 isPrepared,先初始化* 生命周期會先執行 setUserVisibleHint 再執行onActivityCreated* 在 onActivityCreated 之后第一次顯示加載數據,只加載一次*/protected void loadData() {}@Overridepublic void onDestroy() {super.onDestroy();}
} View Code ?
?
?
16.ViewModel使用方法。
- 作用:為Activity或Fragment管理,請求數據,但是具體的邏輯不在ViewModel中寫。
- 參考文章:https://blog.csdn.net/qq_24442769/article/details/79426609
- build.gradle配置:
implementation 'android.arch.lifecycle:extensions:1.1.0'
implementation 'android.arch.lifecycle:reactivestreams:1.1.0'
?
?
?
17.RxJava+RxAndroid的學習。
作用:RxJava是一個Android中的響應式實現,RxAndroid將異步UI事件封裝起來。
參考文章:https://www.cnblogs.com/zhaoyanjun/p/5175502.html
github地址:https://github.com/ReactiveX/RxJava? 和? https://github.com/ReactiveX/RxAndroid
?
?
?
18.首頁功能完成。
只要研究好一個頁面,基本上其他頁面都不在話下了。
因為這個頁面,進行了網絡請求,視圖綁定,BaseFragment定義,接口API請求等,基本上能做的事情,這個頁面都會做。
除此之外,還有一些通用的工具包,首頁Bean類型的定義,首頁的布局文件,抽屜的布局,只不過沒有處理點擊事件。
預覽頁面如下:
然后僅僅完成首頁之后,我進行打包,方便以后使用。
百度云鏈接:https://pan.baidu.com/s/1VCiuVy21RLMOHflN9MP19A? ? ??
密碼:hmci
之后的過程應該就是舉一反三了!
這里我總結一下過程:(想到哪寫到哪)
1.首先定義了一個抽象類BaseFragment,讓這個每日推薦的Fragment來繼承它。不單單繼承,還要求傳入一個泛型類,這個泛型類繼承ViewDataBinding。
這里就是為了方便處理數據的,因為用到MVVM模式,所有聲明了layout的視圖都生成一個視圖綁定類,進而可以獲取到數據。個人覺得就是為了簡化
從視圖中findViewById的過程,其實在用的時候,還是有一次賦值的過程,不過就不用以前那么麻煩地看視圖id了。
言歸正傳,這個BaseFragment同樣繼承了Fragment,注意是v4的Fragment,這里是一坑。
然后這個BaseFragment主要干什么?
布局很簡單,最外層LinearLayout,里面一個RelativeLayout,再里面就是兩個LinearLayout,分別是正在加載和加載失敗的圖片及文字的顯示。
所以啊,布局中的RelativeLayout其實就是顯示內容的,要么這個內容為正在加載,要么為加載失敗,要么就是內容了,就這三種情況。
值得一提的是,使用MVVM模式是如何將內容添加到這個RelativeLayout中呢?
這里其實是用了DataBindingUtil.inflate方法,動態生成一個泛型類(繼承于ViewDataBinding),然后用RelativeLayout.addView方法添加即可。
回想一下Fragment的生命周期~
首先onAttach->onCreateView->onCreate->onActivityCreate->onStart->onResume,到這里視圖才能真正展示。。。
所以在onActivityCreate中可以做什么呢?
答案當然就是:開啟動畫啦。然后就是設置失敗刷新的點擊事件。反正這里就是需要干嘛就干嘛。
然后還有一些方法,比如顯示加載中狀態,加載完成狀態,加載失敗狀態,主要處理視圖的隱藏,這里還是簡單。主要方便繼承者調用嘛。
重要點1:然后就是實現Fragment數據的緩加載,其實就是判斷fragment是否可見,可見才進行請求。
重要點2:然后需要添加一條消息,就是你數據在一個子線程里請求請求請求...完成之后,要通過一條消息發送給主線程,主線程來處理數據。
? ?注意在onDestroy中,將這條消息取消掉,否則內存泄漏就慘咯,然后自己也寫一個移除方法。
?
2.好了,現在有BaseFragment了。那么現在還需要對當前頁面單獨設置一個接口,用來實現僅僅這個頁面才會做的一些方法。
對于主頁來說,我需要顯示輪播圖,顯示主頁列表,顯示主頁錯誤頁面,顯示旋轉動畫,取主頁緩存。
?
3.然后就是EverydayFragment的具體實現了,原來在這里面才真正開啟動畫的,所以這樣的話會更加靈活處理各種頁面動畫。
原來這里不是用的BaseFragment中的動畫,他是自己單獨寫的一個動畫。所以在它自己的布局中有一個獨特的動畫圖。
這里的需要定義一個類似于之前MVP模式中的P(presenter),這里因為用到MVVM模式,所以效果是一樣的,不過這里名字為viewModel了。
?
4.來看一下這個處理器吧。(我習慣稱處理網絡請求的東西叫做處理器,不過這里進行了兩次封裝,還有一個我就叫具體網絡請求器吧)
這里還包裝了一層,將真正的網絡請求封裝了。
首先看一下第一層處理器。
在構造器里面建立了一個具體網絡請求器的實例。這個可以叫做創建型模式吧。
簡單理一下網絡數據加載過程:
在處理器中調用了網絡請求器的同名的方法,用了一個接口作為回調3種情況,1個是成功,1個是加載失敗,1個是添加消息給basefragment。
如果加載成功,則顯示數據,然后將緩存清理,重新添加最新的緩存。如果沒有數據列表,則從緩存中拿,緩存也沒有,則重新請求。
如果加載失敗,就嘗試從緩存中讀取,如果緩存有數據,就顯示列表數據。如果緩存也沒數據,則顯示BaseFragment中定義的錯誤界面。
然后看一些這個具體網絡請求器干了些什么?
首先是具體請求輪播圖。調用的過程真的太講究了,得好好學學別人的封裝過程。
首先是定義了一個同名的請求方法后,里面傳入了一個接口,主要是處理具體的回調。
網絡封裝過程:
首先HttpClient是一個網絡請求類,這是一個接口。因為每個接口會有一個BaseUrl。
這個HttpClient里面有一個Builder類,里面處理各種請求,返回的都是HttpClient對象。
為什么會返回HttpClient對象呢?因為這里還用了一個BuildFactory類,來封裝Retrofit請求,設置一些請求參數等。
最后還是要回到HttpClient,然后這里面還有請求的具體參數寫的API請求,返回Observable<T>類型數據。
5.獲取到網絡數據后該怎么顯示呢?
答案就是適配器了。
在哪里設置適配器?
這個就得看著自己了,在繼承BaseFragment的類中設置。在這個繼承BaseFragment中有很多實例,如綁定Header布局對象,綁定Footer布局對象,
當前Fragment的適配器,當前Fragment的ViewModel。
這個Header布局對象就是當前Fragment頁面最上方的布局,一個banner,一個4個圖標入口。使用RecycleView.addHeaderView方法即可。
如果RecyclerView滑動不流暢,需設置recylerView.setNestedScrollingEnabled(false);
注意在onPause中停止全部圖片請求,在onResume繼續圖片請求。
這個適配器就是處理數據顯示的一個關鍵類了。
首先他繼承了BaseRecyclerViewAdapter,無賴,只能硬著頭皮先看BaseRecyclerViewAdapter了。
這個BaseRecyclerViewAdapter也是繼承了RecyclerView.Adapter<BaseRecyclerViewHolder>,無賴,還得先了解BaseRecyclerViewHolder啦
BaseRecyclerViewHolder主要就是一個視圖持有者,竟然定義了兩個泛型,T是我們的數據類型,D是一個視圖綁定類。
因為繼承了RecyclerView.ViewHolder,那么它的作用也就是容納視圖的作用了。所以這個BaseRecyclerVIewHolder的作用就是執行
ViewDataBinding的一個executePendingBindings方法而已。
BaseRecyclerViewAdapter所以就該實現父類中定義的抽象方法onBindViewHolder了。其他就是一些數據的增加刪除獲取了。
EverydayAdapter正是繼承了BaseRecyclerViewAdapter了,然后具體實現了兩個必須實現的方法,getItemViewType,onCreateViewHolder了
?
?
19.對于適配器中的getItemViewType的理解。
現在才真正理解適配器中的函數。
其實里面的position,并不是一開始就加載完。而是加載到手機屏幕高度,就是說position只會填充完當前手機屏幕。
然后滑動手機屏幕后,這個position才會做相應的改變。
?
?
20.學會了自定義處理WebView的活動(通用)。
現在的APP基本都會用到webView,特別是類似于微信那種自帶進度條的WebView很常見。
所以現在新建了一個專門處理webView的活動,可能只是這個項目通用吧,不同項目根據自己需求酌情修改即可。
這個網頁可以處理很多東西,撥打電話,發送短信,上傳圖片,播放視頻,循環顯示網頁標題,進度條,可以說比較通用吧。
參考文章:https://github.com/youlookwhat/WebViewStudy
21.以后再補充。
轉載于:https://www.cnblogs.com/Jason-Jan/p/8886805.html
總結
以上是生活随笔為你收集整理的从Github开源项目《云阅》所学到的知识的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。