感謝開源,感謝大神,才讓咱們這些菜鳥成長!html
附上雲閱開源項目地址:點我吧。java
如今的APP基本都會實現這個功能吧,而後一直都找不到好的第三方庫,可以知足各類需求。然而碰到了這個開源庫...react
它本質上就是MVC、MVP的改進版。實現數據和視圖相互綁定。MVVM就是講其中的View的狀態和行爲抽象化,讓咱們將視圖UI和業務邏輯分開。android
dataBinding { enabled = true }
implementation 'com.github.bumptech.glide:glide:4.7.1'
implementation 'jp.wasabeef:glide-transformations:2.0.1
implementation 'com.android.support:multidex:1.0.3'
方法:本身新建一個抽象類,集成OnClickListener,重寫裏面的onClick函數。git
public abstract class PerfectClickListener implements OnClickListener { public static final int MIN_CLICK_DELAY_TIME = 1000; private long lastClickTime = 0; private int id = -1; @Override public 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); }
使用場景:在啓動頁中,有一個跳轉按鈕,避免用戶屢次點擊啓動多個主頁。github
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中具體配置。具體百度吧。web
<android.support.v7.widget.Toolbar android: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文件中本身建立一個。canvas
原來僅僅是在定義Viewpager的時候,添加以下代碼便可。緩存
android:descendantFocusability="blocksDescendants"
beforeDescendants:viewgroup會優先其子類控件而獲取到焦點
afterDescendants:viewgroup只有當其子類控件不須要獲取焦點時才獲取焦點
blocksDescendants:viewgroup會覆蓋子類控件而直接得到焦點
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 狀態欄顏色值 */ @Deprecated public 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 */ @Deprecated public 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 top if (!(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 狀態欄顏色值 */ @Deprecated public 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 top if (!(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 top if (!(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 */ @Deprecated public 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; } }
長按或者點擊一個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>
能夠寫一個通用類,用來封裝不一樣數據類型寫入文件,從文件中獲取數據的方法。
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(); } }
另外,能夠根據本身的需求,靈活地在文件中增長一些具體的方法。好比存儲APP的夜間模式或者日間模式,是否登陸等。
public class GlideCircleTransform extends BitmapTransformation { public GlideCircleTransform(Context context) { super(context); } @Override protected 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 too Bitmap 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; } @Override public String getId() { return getClass().getName(); } }
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); }
這裏添加library不是在build.gradle中添加依賴,也不是在工程中添加libs文件。而是導入一個Module。
實際上是一個主項目須要實現某些功能,而後將這部分功能劃分開來,最後將這部分功能集成到主項目中,也是是模塊的劃分,因此這裏稱爲module。
這樣的好處是:能夠再主module中任何地方引用子module的圖片資源,代碼資源等等。
如今有一個地方不是特別理解,就是如何將多個library統一放在一個文件夾下面,這樣方便管理,否則都不知道哪一個是主項目了。
添加單個library參考文章:https://blog.csdn.net/u014772414/article/details/51194952
主要抓住一下要點吧。
因此在佈局方面咱們能夠這樣佈局。
<RelativeLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> <!--加載失敗--> <LinearLayout android:id="@+id/ll_error_refresh" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:visibility="gone"> <ImageView android:id="@+id/img_err" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/load_err" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:text="加載失敗,點擊重試" android:textSize="14sp" /> </LinearLayout> <!--加載中..--> <LinearLayout android: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"> <ImageView android:id="@+id/img_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/yun_anim" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="努力加載中..." android:textColor="@color/colorTabText" android:textSize="14sp" /> </LinearLayout> </RelativeLayout>
而後再BaseFragment中,這樣決定怎麼隱藏怎麼顯示。
public abstract class BaseFragment<SV extends ViewDataBinding> extends Fragment { protected SV bindingView;//佈局view protected boolean mIsVisible=false;//fragment是否顯示了 private LinearLayout mLlProgressBar;//加載中 private LinearLayout mRefresh;//加載失敗 protected RelativeLayout mContainer;//內容佈局 private AnimationDrawable mAnimationDrawable; @Nullable @Override public 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; } @Override public 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() { @Override protected 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數據的緩加載. */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { mIsVisible = true; loadData(); } else { mIsVisible = false; } } /** * 顯示時加載數據,須要這樣的使用 * 注意聲明 isPrepared,先初始化 * 生命週期會先執行 setUserVisibleHint 再執行onActivityCreated * 在 onActivityCreated 以後第一次顯示加載數據,只加載一次 */ protected void loadData() { } @Override public void onDestroy() { super.onDestroy(); } }
implementation 'android.arch.lifecycle:extensions:1.1.0'
implementation 'android.arch.lifecycle:reactivestreams:1.1.0'
做用:RxJava是一個Android中的響應式實現,RxAndroid將異步UI事件封裝起來。
參考文章:http://www.javashuo.com/article/p-mgjetrgr-dy.html
github地址:https://github.com/ReactiveX/RxJava 和 https://github.com/ReactiveX/RxAndroid
只要研究好一個頁面,基本上其餘頁面都不在話下了。
由於這個頁面,進行了網絡請求,視圖綁定,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了
如今才真正理解適配器中的函數。
其實裏面的position,並非一開始就加載完。而是加載到手機屏幕高度,就是說position只會填充完當前手機屏幕。
而後滑動手機屏幕後,這個position纔會作相應的改變。
如今的APP基本都會用到webView,特別是相似於微信那種自帶進度條的WebView很常見。
因此如今新建了一個專門處理webView的活動,可能只是這個項目通用吧,不一樣項目根據本身需求酌情修改便可。
這個網頁能夠處理不少東西,撥打電話,發送短信,上傳圖片,播放視頻,循環顯示網頁標題,進度條,能夠說比較通用吧。
參考文章:https://github.com/youlookwhat/WebViewStudy