感谢开源,感谢大神,才让我们这些菜鸟成长!

附上云阅开源项目地址:点我吧。

1.轮播图的实现。

  现在的APP基本都会实现这个功能吧,然后一直都找不到好的第三方库,能够满足各种需求。然而碰到了这个开源库...

2.MVVM-DataBinding架构开发。

  它本质上就是MVC、MVP的改进版。实现数据和视图相互绑定。MVVM就是讲其中的View的状态和行为抽象化,让我们将视图UI和业务逻辑分开。

  • gradle配置:    (在module的build.gradle中的android模块中加入如下代码块) 
 dataBinding {
enabled = true
}

3.解决Bug:Android点击应用图标后会重新进入启动页。

4.第三方库Glide的简单使用。

  • gradle配置:
implementation 'com.github.bumptech.glide:glide:4.7.1'
implementation 'jp.wasabeef:glide-transformations:2.0.1

  

5.分包策略,解决65535方法限制的问题。

  • gradle配置:
 implementation 'com.android.support:multidex:1.0.3'

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; @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);
}

  使用场景:在启动页中,有一个跳转按钮,避免用户多次点击启动多个主页。

7.如何使用android.support.v7.widget.Toolbar

  • 首先一定要在build.gradle中添加依赖。
 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.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文件中自己创建一个。

8.设置主题点击波纹效果。

  • 首先得引入谷歌的材料设计库,上面第7点中有相关引入方式。
  • 然后再布局文件中:android:background="?attr/...."   (这里省略号写一些波纹样式即可)
  • 参考文章:https://yq.aliyun.com/articles/12407

9.左右滑动ViewPager时,没获取焦点没响应。

  原来仅仅是在定义Viewpager的时候,添加如下代码即可。

android:descendantFocusability="blocksDescendants"
      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 状态栏颜色值
*/
@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;
} }
  • 简单用法:根据自己需求在需要的地方,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();
} }

  另外,可以根据自己的需求,灵活地在文件中增加一些具体的方法。比如存储APP的夜间模式或者日间模式,是否登录等。

13.利用Glide画圆角图。  

  • 首先在module的build.gradle中添加依赖,前面第4点有讲过。
  • 然后再自定义一个转换器,类似于下面这样。
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();
}
}
  • 然后就是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。

  主要抓住一下要点吧。

  • 如果网络异常,则页面应该怎么显示。
  • 如果正在加载中,则页面应该怎么显示。
  • 如何动态添加自己想要的布局。

  所以在布局方面我们可以这样布局。

        <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();
}
}

16.ViewModel使用方法。

   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.以后再补充。

从Github开源项目《云阅》所学到的知识的更多相关文章

  1. iOS:开发常用GitHub开源项目(持续更新)

    IOS开发常用GitHub开源项目(持续更新) 数据类 开源库 作者 简介 AFNetworking Mattt 网络请求库 ASIHTTPRequest pokeb 网络请求库 Alamofire ...

  2. 【Github开源项目体验】- ZFile 基于 Java 的在线网盘

    [Github开源项目体验]- ZFile 基于 Java 的在线网盘 在线云盘.网盘.OneDrive.云存储.私有云.对象存储.h5ai.上传.下载 date: 2022-08-02 addres ...

  3. 2015-2016最火的Android开源项目--github开源项目集锦(不看你就out了)

    标签: Android开发开源项目最火Android项目github 2015-2016最火的Android开源项目 本文整理与集结了近期github上使用最广泛最火热与最流行的开源项目,想要充电与提 ...

  4. 如何参与一个 GitHub 开源项目?

    最近一年开源项目特别的热,很多技术大会或论坛都以开源项目作为主题进行探讨,可见这是一种趋势.而Github作为开源项目的著名托管地,可谓无 人不知,越来越多的个人和公司纷纷加入到Github的大家族里 ...

  5. Android笔记——导入Github开源项目CircleRefreshLayout

    百度n久都找不到android studio导入第三方类库的正确方法,纠结睡不着 ,最后终于蒙到了方法,原来想太多了  ---------------------------------------- ...

  6. 如何参与一个GitHub开源项目

    Github作为开源项目的著名托管地,可谓无人不知,越来越多的个人和公司纷纷加入到Github的大家族里来,为开源尽一份绵薄之力.对于个人来讲,你把自己的项目托管到Github上并不表示你参与了Git ...

  7. 开源项目福利-github开源项目免费使用Azure PipeLine

    微软收购Github后,很多人猜想微软可能会砍掉VSTS,然而事实VSTS并没有砍掉,关于Azure Devops的详细信息可以查看 这篇博客,如果想查看原文也可以从链接里提供的原始地址里查看. 今天 ...

  8. 基于RBAC模型的权限系统设计(Github开源项目)

    RBAC(基于角色的访问控制):英文名称Rose base Access Controller.本博客介绍这种模型的权限系统设计.取消了用户和权限的直接关联,改为通过用户关联角色.角色关联权限的方法来 ...

  9. Android Hawk数据库 github开源项目

    Android Hawk数据库 github开源项目 Hawk 是一个很便捷的数据库  . 操作数据库仅仅需一行代码 , 能存不论什么数据类型 . github 地址: https://github. ...

  10. [Android开源项目] GitHub开源项目总结 (转)

    [Android开源项目] GitHub开源项目总结 GitHub开源项目android-styled-dialogs http://neast.cn/forum.php?mod=viewthread ...

随机推荐

  1. 你是怎么调试 JavaScript 程序

    你是怎么调试 JavaScript 程序的?最原始的方法是用 alert() 在页面上打印内容,稍微改进一点的方法是用 console.log() 在 JavaScript 控制台上输出内容.嗯~,用 ...

  2. “三八节”如何做好EDM邮件营销

    阳春三月,乍暖还寒,万物复苏,一年一度的三八节也马上来临了,各路商家都开足马力,掀起了一股美丽的旋风.如今酒香也怕巷子深,要想取得良好的营销效果,就得早早动手,赚足眼球,才会换来节日当天的丰厚回馈.U ...

  3. MySQL数据库----------小知识点

    **********************补2016年5月23日的博客************************* MySQL数据库 常用数据类型: int                整数 ...

  4. C#使用DotNetZip对zip压缩包进行添加删除操作

    参考:http://stackoverflow.com/questions/9855155/how-can-i-delete-a-directory-in-a-zip-file-using-net D ...

  5. HDU 1281 棋盘游戏 【二分图最大匹配】

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1281 题意概括: 有N*M大的棋盘,要在里面放尽量多的“车”,求最多能放的车的个数,和为了放最多的车有多 ...

  6. 1.springboot:入门项目

    简介: Springboot 来简化spring应用开发,约定大于配置,去繁从简,just run 就可以创建一个独立的,产品应用 背景: J2EE笨重开发,繁多的配置,低下的开发效率,复杂的部署流程 ...

  7. C#通过指针读取文件

    // readfile.cs // 编译时使用:/unsafe // 参数:readfile.txt // C#通过指针读取文件.使用该程序读并显示文本文件. using System; using ...

  8. Unity中自定义扩展方法

    问题背景 在使用unity开发过程中,通常会遇到一种情况,比如说给物体重新赋值坐标的问题, Transfrom tran: ,pos_y=,pos_z=; tran.position=new Vect ...

  9. UITableView控件didSelectRow和didDeselectRow方法注意事项

    UITableView控件didSelectRow和didDeselectRow方法注意事项 1. 因Xcode强大的自动补全功能,在使用UITableView如下两个方法时,务必特别小心,避免出错: ...

  10. Gradle Goodness: Running Java Applications from External Dependency

    With Gradle we can execute Java applications using the JavaExec task or the javaexec() method. If we ...