Loading... # Android项目200项 原文:[https://www.jianshu.com/p/6be91ee932a7](https://www.jianshu.com/p/6be91ee932a7) ## 1.一个APP只需要一个Activity ``` //片段fragment implementation 'me.yokeyword:fragmentation:1.3.6' implementation 'me.yokeyword:fragmentation-swipeback:1.3.6' ``` ## 2.懒人必备查找控件 ``` //ButterKnife(10.0必须适配AndroidX,9.0需要java8,快速生成插件android-butterknife-zelezny) implementation 'com.jakewharton:butterknife:9.0.0' annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0' ``` - 注意:由于butterknife已经基本废弃,所以推荐使用ViewBinding来简化findViewById,如果你想优化你的项目建议看看这篇文章:处理被废弃的ButterKnife和kotlin-android-extensions有妙招 [https://juejin.cn/post/6920515331035561998](https://juejin.cn/post/6920515331035561998) ## 3.方法数超过65k,合并 - a.引入jar包 ``` implementation "com.android.support:multidex:2.0.1" ``` - b.配置合并dex为开启 ``` defaultConfig { multiDexEnabled true } ``` - c.在自己的MyApplication下的方法中初始化 ``` @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } ``` ## 4.新版本的Gradle,需要至少在一个Activity中的 <intent-filter>里面添加: ``` <action android:name="android.intent.action.VIEW" /> ``` ## 5.拉的自己封装的工具类需要去掉,.git的关联 [https://blog.csdn.net/lyj1005353553/article/details/55519487](https://blog.csdn.net/lyj1005353553/article/details/55519487) ## 6.设置去掉所有的页面标题栏 - 在AppTheme中配置 ``` <item name="windowNoTitle">true</item> ``` ## 7.快速构建Builder插件 - innerbuilder插件 [https://plugins.jetbrains.com/plugin/7354-innerbuilder/](https://plugins.jetbrains.com/plugin/7354-innerbuilder/) ## 8.使用Dagger2无法找到对于的Component类, - a.必须在基类的onCreate方法中注入 ``` @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); DaggerCommonComponent.create().inject(this); } ``` - 而不要注入到另一个onCreate中 ``` @Override public void onCreate( @Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); } ``` ## 9.RecyclerView嵌套ScrollView不流畅 - scrollview嵌套viewpager嵌套fragment嵌套recyclerview [https://blog.csdn.net/baidu_31093133/article/details/80777101](https://blog.csdn.net/baidu_31093133/article/details/80777101) ``` recyclerView.setHasFixedSize(true); recyclerView.setNestedScrollingEnabled(false); ``` - 垂直滑动问题: [https://segmentfault.com/a/1190000011553735](https://segmentfault.com/a/1190000011553735) - recyclerview嵌套在NestedScrollView里,一次性加载出全部数据问题 [https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues/1954](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues/1954) - Android SwipeRefreshLayout和RecyclerView嵌套时 下拉刷新冲突的解决办法 [https://blog.csdn.net/peirato_/article/details/54913195](https://blog.csdn.net/peirato_/article/details/54913195) 针对RecyclerView不显示,只需要设置ScrollView的属性 `android:layout_height="match_parent"` `android:fillViewport="true"` 就OK了。 - 监听RecyclerView滚动距离 ``` //RecyclerView滚动监听 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState){ case RecyclerView.SCROLL_STATE_IDLE://现在不是滚动状态 L.e("滚动的距离=="+direction); break; case RecyclerView.SCROLL_STATE_DRAGGING://手指 拖动 break; case RecyclerView.SCROLL_STATE_SETTLING://惯性滚动 break; } } @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); //计算RecyclerView滚动的距离 direction += dy; } }); ``` - RecyclerView解决数据混乱,禁止复用 ``` recyclerView.getRecycledViewPool().setMaxRecycledViews(viewType,0); ``` [https://blog.csdn.net/adojayfan/article/details/87934157](https://blog.csdn.net/adojayfan/article/details/87934157) - RecyclerView抢先获取焦点问题 [https://www.jianshu.com/p/24d0629a8caf](https://www.jianshu.com/p/24d0629a8caf) ## 10.Retrofit网络请求,生成 ``` private static void setRetrofit(String defaultHost) { retrofit = new Retrofit.Builder() .baseUrl(defaultHost) .client(okHttpClient) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JSONObjectConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()).build(); } ``` 其中JSONObjectConverterFactory和GsonConverterFactory不能共存,如果想返回JSONObject对象,去掉 .addConverterFactory(GsonConverterFactory.create())如果想直接生成Object对象,去掉.addConverterFactory(JSONObjectConverterFactory.create()) ## 11.TextView相关属性或常见问题汇总 | 功能,问题 | 代码,链接 | | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 加入删除线 | `tv.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);` | | 设置间距 | 行间距 `lineSpacingExtra`,字间距 `letterSpacing` | | 加粗和取消加粗 | 加粗 `tv.getPaint().setFakeBoldText(true);`取消 `tv.getPaint().setFakeBoldText(false);` | | 设置 `maxLength ,ellipsize=”end"`失效 | 使用 `maxEms`代替 `maxLength` | | 文字内容超出显示省略号 | 超出4行显示省略号[https://blog.csdn.net/gentlemanyc/article/details/49967719?locationNum=2](https://blog.csdn.net/gentlemanyc/article/details/49967719?locationNum=2) | | 设置可垂直滚动 | xml中 `android:scrollbars="vertical"`且代码中 `tv.setMovementMethod(ScrollingMovementMethod.getInstance());` | | 设置字体后,显示不全 | 斜体显示不全,斜体中文显示不全[https://www.jianshu.com/p/4314cc68c1f3](https://www.jianshu.com/p/4314cc68c1f3) | | 弹窗导致TextView停止滚动 | 解决因为弹窗导致TextView停止滚动的问题[https://www.jianshu.com/p/22b4aff0dc8e](https://www.jianshu.com/p/22b4aff0dc8e) | | 多行且需要显示省略号的警告 | logcat 总是报: W/StaticLayout: maxLineHeight should not be -1. maxLines:1 lineCount:1[https://blog.csdn.net/qq_34723470/article/details/82152418](https://blog.csdn.net/qq_34723470/article/details/82152418) | | 加上阴影效果,字体阴影 | Android TextView加上阴影效果[https://blog.csdn.net/hewence1/article/details/39993415](https://blog.csdn.net/hewence1/article/details/39993415) | - ClickableSpan变换颜色且不要下划线: ``` public abstract class BlueClickableSpan extends ClickableSpan { @Override public void updateDrawState(@NonNull TextPaint ds) { ds.setColor(Color.parseColor("#3C78FF")); ds.setUnderlineText(false); } } ``` - 代码中绘制左侧图片 ``` private void call(TextView tv,int icon){ Drawable img = getResources().getDrawable(icon); img.setBounds(0, 0, img.getMinimumWidth(), img.getMinimumHeight()); tv.setCompoundDrawables(img, null, null, null); } ``` - 文字滚动消息: ``` < TextView android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:focusable ="true" android:focusableInTouchMode ="true" android:marqueeRepeatLimit ="marquee_forever" android:ellipsize ="marquee" android:singleLine ="true" android:text ="那么什么是成功的人生,什么样的人又是成功人士呢?像科技界的比尔?盖茨 ,乔布斯 ,商界的巴菲特以及娱乐、体育界的大腕,无疑会被人们视为是成功者。但对于普通民众,如果用比尔?盖茨那样的标准来衡量是否成功似乎有些太苛刻,也不现实。在某种观念中,一个人的成功似乎与财富分不开的。很多国人到美国看到华人最常说的一句话是,你是个成功人士。为什么这么说?因为你能住300平方米、价值百万美元的房子,因为你开的车是奔驰 、宝马等豪华车,因为你有好的工作,年薪至少在10万美元以上。这也许是很多国人看待一个人成功与否的主要标志。但在美国人眼里,成功并不是与拥有众多财富密不可分。在1980年代,多数美国人把拥有更多财富看成人生成功的一个主要标志。而在一项最新的调查中,对于美国人来说,财富不再是成功的最重要组成部分。调查中22个成功人生的潜在组成因素中,“有很多钱”仅排名在第20位。" /> ``` - 线性渐变字体,文字渐变,TextView渐变,参考:修改字体和设置字体渐变色 [https://blog.csdn.net/negineko/article/details/100033250](https://blog.csdn.net/negineko/article/details/100033250) ``` public class GradientColorTextView extends AppCompatTextView { private Rect mTextBound = new Rect(); public GradientColorTextView (Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { int mViewWidth = getMeasuredWidth(); Paint mPaint = getPaint(); String mTipText = getText().toString(); mPaint.getTextBounds(mTipText, 0, mTipText.length(), mTextBound); @SuppressLint("DrawAllocation") LinearGradient mLinearGradient = new LinearGradient(0, 0, mViewWidth, 0, new int[]{0xFFFF0000, 0xFF5400FF}, null, Shader.TileMode.REPEAT); mPaint.setShader(mLinearGradient); canvas.drawText(mTipText, getMeasuredWidth() / 2 - mTextBound.width() / 2, getMeasuredHeight() / 2 + mTextBound.height() / 2, mPaint); } } ``` ## 12.对Retrofit回调进行封装,只输出正确和错误信息 - `OnSuccessAndFailListener` 自定义Retrofit网络回调结果 [https://www.jianshu.com/p/f565a687ba75](https://www.jianshu.com/p/f565a687ba75) ## 13.EditText变搜索按钮,并监听搜索事件 - xml中设置 ``` android:imeOptions="actionSearch" android:singleLine="true" ``` - 代码中 ``` etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH){ String keyWord = v.getText().toString(); if (TextUtils.isEmpty(keyWord)){ ToastUtil.showLong("请输入搜索内容!"); return false; } //开始搜索keyWord相关内容 } return false; } }); ``` ## 14.CoordinatorLayout布局要设置滚动控件的 ``` app:layout_behavior="@string/appbar_scrolling_view_behavior" ``` - 里面的字符串爆红,但还是可以运行,但红色总是不好看的,可能是新版本的sdk引起的,所以需要改为 ``` app:layout_behavior ="android.support.design.widget.AppBarLayout$ScrollingViewBehavior" ``` ## 15.WebView不跳转到外部浏览器 ``` class MyWebViewClient extends WebViewClient{ //不跳转到外部浏览器 public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } } ``` - Android webview loadData 中文乱码 [https://www.jianshu.com/p/85957f003dd4](https://www.jianshu.com/p/85957f003dd4) - webview加载html图片过大左右滑动的解决/webview加载图片自适应大小 [https://blog.csdn.net/smileiam/article/details/72123227](https://blog.csdn.net/smileiam/article/details/72123227) ## 16.WebView出现net::ERR_UNKNOWN_URL_SCHEME错误 [https://www.jianshu.com/p/119823e5cfb5](https://www.jianshu.com/p/119823e5cfb5) ## 17.Glide显示图片为上面圆角,下面直角 - 让Glide输出指定位置的圆角图片 2018年,部分方法为Glide4.0以前的,所以无法使用,但方法值得借鉴 [https://blog.csdn.net/mchenys/article/details/80284132](https://blog.csdn.net/mchenys/article/details/80284132) - Glide 加载部分圆角图片2019年,新的方法,且行为更合理 [https://blog.csdn.net/csclmf/article/details/86544031](https://blog.csdn.net/csclmf/article/details/86544031) - 圆角不圆:有可能是因为图片高度或宽度过大,导致部分圆角不圆 - Glide ViewTarget及SimpleTarget加载问题: [https://www.jianshu.com/p/bf38da6a37ee](https://www.jianshu.com/p/bf38da6a37ee) - Android 使用Glide下载图片的几种方式(老版本) [https://www.jianshu.com/p/a931e2a58c4e](https://www.jianshu.com/p/a931e2a58c4e) - Glide下载图片并保存到本地 [https://www.jianshu.com/p/62c5e73713fb](https://www.jianshu.com/p/62c5e73713fb) ## 18.View转换为Bitmap,view转bitmap ``` private Bitmap loadBitmapFromView(View v) { int w = v.getWidth(); int h = v.getHeight(); Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); Canvas c = new Canvas(bmp); c.drawColor(Color.WHITE); /** 如果不设置canvas画布为白色,则生成透明 */ v.layout(0, 0, w, h); v.draw(c); return bmp; } ``` - 关于View转化成bitmap保存成图片 [https://blog.csdn.net/zhangcanyan/article/details/79579405](https://blog.csdn.net/zhangcanyan/article/details/79579405) - 两个Bitmap合并为一个 [https://www.jianshu.com/p/4f9accc7800f](https://www.jianshu.com/p/4f9accc7800f) - Android:将一个Activity、某块布局转换成图片 [https://www.jianshu.com/p/71b701fd2988](https://www.jianshu.com/p/71b701fd2988) - 注意:view转bitmap时,视图view的位置可能发生改变。这个原因多半时因为: ## 19.使用xml绘制虚线好后,4.0以上需要使用 android:layerType="software"属性,不然虚线会变为实线 [https://blog.csdn.net/Small_Lee/article/details/52153557](https://blog.csdn.net/Small_Lee/article/details/52153557) ## 20.微信分享到朋友圈,调用系统分享,需要对Uri单独处理一下 [https://blog.csdn.net/qq_34900897/article/details/85320646](https://blog.csdn.net/qq_34900897/article/details/85320646) ## 21.support版本冲突的解决办法 [https://blog.csdn.net/yuzhiqiang_1993/article/details/78214812](https://blog.csdn.net/yuzhiqiang_1993/article/details/78214812) ## 22.TabLayout+ViewPager垂直方向联动,用于分类页面 [https://www.jianshu.com/p/9266e58cc4f5](https://www.jianshu.com/p/9266e58cc4f5) ## 23.精确计算RecyclerView滑动高度 - SlideRecyclerView [https://github.com/NathansLiu/SlideRecyclerView](https://github.com/NathansLiu/SlideRecyclerView) ## 24.adb无线调试 [https://jingyan.baidu.com/article/066074d610f4f3c3c21cb0ab.html](https://jingyan.baidu.com/article/066074d610f4f3c3c21cb0ab.html) ## 25.打包名称自定义 [https://www.cnblogs.com/bluestorm/p/6228085.html](https://www.cnblogs.com/bluestorm/p/6228085.html) [https://blog.csdn.net/weixin_33709364/article/details/87160660](https://blog.csdn.net/weixin_33709364/article/details/87160660) 打包方法过时警告:[https://www.cnblogs.com/blogs-of-lxl/p/10306145.html](https://www.cnblogs.com/blogs-of-lxl/p/10306145.html) - 我的通用命名方式:包名最后一部分+版本名称+时间+打包方式 taobao_v1.0_2019-05-20_release - 在android{}里面写入 ``` android.applicationVariants.all { variant -> variant.outputs.all{ outputFileName = "${applicationId.subSequence(applicationId.lastIndexOf(".")+1,applicationId.length())}_v${versionName}_${releaseTime()}_${baseName}.apk" } } ``` - 其中releaseTime()为写在android{}外部的一个方法 ``` def releaseTime() { return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) } ``` ## 26.apk签名打包, - 如何配置签名及生成签名文件: [https://www.cnblogs.com/details-666/p/keystore.html](https://www.cnblogs.com/details-666/p/keystore.html) - 如何判断你的apk是否已经签名: [https://blog.csdn.net/qq_21376985/article/details/53337977](https://blog.csdn.net/qq_21376985/article/details/53337977) ## 27.混淆 [https://blog.csdn.net/qq634416025/article/details/79686051](https://blog.csdn.net/qq634416025/article/details/79686051) ## 28.Error running 'app': Default Activity not found - 由于添加了三方控件导致出现,原因可能时第三方控件版本不对,升级后解决! Default Activity Not Found 问题总结:[https://blog.csdn.net/yforyoung/article/details/84952283](https://blog.csdn.net/yforyoung/article/details/84952283) ## 29.Android:通过Glide保存图片到本地,并同步到相册 ## 30.Android 调用系统分享(不使用第三方),指定QQ、微信等 [https://blog.csdn.net/u010356768/article/details/78246691](https://blog.csdn.net/u010356768/article/details/78246691) [https://www.cnblogs.com/Rainm/p/10547563.html](https://www.cnblogs.com/Rainm/p/10547563.html) - qq空间限制: [https://blog.csdn.net/weixin_41239127/article/details/78743421](https://blog.csdn.net/weixin_41239127/article/details/78743421) - Android系统分享使用Intent分享文本到QQ和文本+图片到微信朋友圈及纯图到微信 [https://www.jianshu.com/p/9522e24713e1](https://www.jianshu.com/p/9522e24713e1) ## 31.安卓从imageview中获得bitmap的方法 [https://blog.csdn.net/yj1499945/article/details/47079621](https://blog.csdn.net/yj1499945/article/details/47079621) ## 32.怎样获取到Android控件的高度,通过监听的方式 [https://www.jianshu.com/p/2c8e5324ec68](https://www.jianshu.com/p/2c8e5324ec68) 你可能也不知道为什么,第一次进入页面,获取控件的高度有值,再次进入获取的高度居然为0,再再再次进入也为0,杀掉应用,进入页面又有高度了,再次进入又为0。因为我需要通过view来获取Bitmap,那么View的宽高值必不可少,所以我通过上面博客的方法去监听控件的高度才拿到值。但为什么只有第一次进入才能拿到宽高值却拜师不得琪姐,请各位大老解答。 ## 33.AccessibilityService获取控件信息getRootInActiveWindow() 经常为null [https://blog.csdn.net/qq_28210079/article/details/80486592](https://blog.csdn.net/qq_28210079/article/details/80486592) ## 33.解决android.permission.WRITE_APN_SETTINGS [https://blog.csdn.net/qq_36437339/article/details/81015715](https://blog.csdn.net/qq_36437339/article/details/81015715) ## 34.新手引导库 - GuideView [https://github.com/binIoter/GuideView](https://github.com/binIoter/GuideView) 在Fragment中由于控件位置绘制流程和生命周期的关系,需要监听控件View宽高,有值后才进行引导层的绘制,同时用Handler进行一定的延迟绘制,保证高亮区域的定位精确度 ## 35.Java中对对象进行排序 [https://blog.csdn.net/qq_37937537/article/details/80445731](https://blog.csdn.net/qq_37937537/article/details/80445731) - 通过Comparable ## 36.监控当前APP是否回到前台 [https://www.jianshu.com/p/101eb42d0fde](https://www.jianshu.com/p/101eb42d0fde) ## 37.使用java8新特性快速的打印输出序列对象 ``` allList.stream().map(User::toString).forEach(L::e); ``` ## 38.Fragment的优化 - 不要再Activity中使用List来保存Fragment [https://blog.csdn.net/qq_30993595/article/details/80736814](https://blog.csdn.net/qq_30993595/article/details/80736814) ## 39.ViewPager2,支持横竖布局,支持0预加载布局 [https://juejin.im/post/5cda3964f265da035d0c9d8f](https://juejin.im/post/5cda3964f265da035d0c9d8f) ## 40.RadioGroup.onCheckedChanged() 会调用多次 [https://blog.csdn.net/qq_32452623/article/details/80474487](https://blog.csdn.net/qq_32452623/article/details/80474487) ## 41.RecycleView notifyItemChanged() 局部刷新报错 - 原因一:[https://blog.csdn.net/qq_29598727/article/details/80515725](https://blog.csdn.net/qq_29598727/article/details/80515725) - 原因二:还有一种原因就是RecycleView对应的mAdapter添加了头部,那么添加了几个头部就需要在刷新的位置加几。 - 我以为我的问题是原因一导致的,没想到使用原因二的解决办法后,去掉了原因一的方法依然没有问题,说明就是原因二导致的。 ## 42.Java多线程 - Java多线程系列目录(共43篇) [https://www.cnblogs.com/skywang12345/p/java_threads_category.html](https://www.cnblogs.com/skywang12345/p/java_threads_category.html) ## 43.ArrayList进行排序 - 24版本以前的老方法: ``` Collections.sort(arrays); ``` - 新方法: ``` arrays.sort(); ``` ## 44.对时间的转换,对比,添加,格式输出 - 推荐使用 [joda-time](https://github.com/JodaOrg/joda-time) ## 45. tools:replace的使用 - `tools:replace=""` 有时候需要替换多个项,使用逗号分割 ``` tools:replace="android:allowBackup,android:appComponentFactory" ``` ## 46. 解决 导入三方时出现: appComponentFactory 错误 [https://blog.csdn.net/qq_34224268/article/details/83861897](https://blog.csdn.net/qq_34224268/article/details/83861897) ## 47. 滚轮时间选择器 - 可以采用 XPopup和WheelPicker [https://github.com/AigeStudio/WheelPicker](https://github.com/AigeStudio/WheelPicker) - 组合的方式来生成一个时间选择器如:[https://www.jianshu.com/p/4a2c853d9276](https://www.jianshu.com/p/4a2c853d9276) ## 48. fragment里coordinatorlayout+viewpager无法正常滑动问题 - fragment里coordinatorlayout+viewpager无法正常滑动问题 - 有人说fragment是无法运行协调者布局的,这是错误的 - 解决方法:在你的viewpager子fragment里面布局最外面套上一层NestedScrollView就可以了 ## 49.查看SHA1命令 keytool -list -v -keystore C:\Users\Desktop\browser\debug.keystore -storepass android - 后面的android为当前密钥的密码 ## 50.RecyclerView横向分页菜单 [https://blog.csdn.net/u014165119/article/details/46834265](https://blog.csdn.net/u014165119/article/details/46834265) ## 51.ArgbEvaluator一个计算颜色渐变值的类 [https://blog.csdn.net/u013581141/article/details/68063469](https://blog.csdn.net/u013581141/article/details/68063469) - 使用事例:自定义CoordinatorLayout.Behavior颜色渐变的TitleBar [https://www.jianshu.com/p/36391b013a30](https://www.jianshu.com/p/36391b013a30) ## 52.交互效果咋了系列: - 自定义ViewGroup第十三式之移花接木 [https://blog.csdn.net/u011387817/article/details/89142467](https://blog.csdn.net/u011387817/article/details/89142467) ## 53.数据绑定DataBind ``` android { … dataBinding { enabled = true } } ``` - 告别findView和ButterKnife [https://www.jianshu.com/p/499c8e2b80c4](https://www.jianshu.com/p/499c8e2b80c4) - Android开发教程 - 使用Data Binding(七)使用BindingAdapter简化图片加载 [https://www.jianshu.com/p/e5b20a258f7d](https://www.jianshu.com/p/e5b20a258f7d) ## 54.错误检查命令 - gradlew processDebugManifest --stacktrace ## 55.new Handler的警告说明和解决办法@SuppressLint(“HandlerLeak”) [https://blog.csdn.net/androidsj/article/details/79865091](https://blog.csdn.net/androidsj/article/details/79865091) ``` private MyHandler myHandler = new MyHandler(_mActivity); private static class MyHandler extends Handler { WeakReference weakReference; public MyHandler(Activity activity) { weakReference = new WeakReference(activity); } @Override public void handleMessage(Message msg) { } } ``` - 另一种写法: ``` private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } }); ``` ## 56.Android 绘制圆发光效果 ## 57.通知无法显示:8.0需要设置渠道和权重 [https://www.jianshu.com/p/f85ef58edf63](https://www.jianshu.com/p/f85ef58edf63) [https://www.jianshu.com/p/d92d7f070709](https://www.jianshu.com/p/d92d7f070709) ## 58.uri指向了外部应用exposed beyond app through RemoteViews.setUri() - 在Application中的onCreate方法中添加如下 ``` StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); builder.detectFileUriExposure(); ``` ## 59.使用Git Submodule管理子模块 ## 60.弹窗需要一个自定义的三角形SanJiaoView : [https://blog.csdn.net/ZhangLei280/article/details/73207669](https://blog.csdn.net/ZhangLei280/article/details/73207669) [https://segmentfault.com/a/1190000003076028](https://segmentfault.com/a/1190000003076028) ## 61.升级AndroidX后出现缺失AppBarLayout$ScrollingViewBehavior [https://www.jianshu.com/p/6b8104787617](https://www.jianshu.com/p/6b8104787617) ## 62.APP启动时间,性能优化,启动页白屏 [https://www.jianshu.com/p/75b0b128c470](https://www.jianshu.com/p/75b0b128c470) ## 63.AndroidStudio安装失败  - 方法一:clean项目 - 方法二:重启大法:重启AndroidStudio,重启手机,重启电脑,重启... - 方法三:检查是否只开启了开发者模式和USB调试,却没有开启了USB安装  ## 64.adb命令 [https://www.jianshu.com/p/56fd03f1aaae](https://www.jianshu.com/p/56fd03f1aaae) ## 65.ConstraintLayout基础 及动态控件(动画效果) [https://www.jianshu.com/p/7888cde8292f](https://www.jianshu.com/p/7888cde8292f) ## 66.获取videoView第一针视频报错: [https://blog.csdn.net/guohesheng/article/details/80236799](https://blog.csdn.net/guohesheng/article/details/80236799) ## 67.Math函数相关计算 ``` //角度换算为对应数值 double skewRot = Math.toRadians(30); ``` ## 68. banner库一张图片无效和第一次进入没有轮播的时候点击第一张无效 一张图片的时候需要设置banner的onClick事件,多张图第一次还未开始轮播点击无效,需要先设置点击事件,再调用banner的start方法 ## 69. <activity-alias> 标签的使用 - <activity-alias> 标签的使用 [https://www.jianshu.com/p/7b52e278508e](https://www.jianshu.com/p/7b52e278508e) ## 70.Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK [https://blog.csdn.net/watermusicyes/article/details/44963773](https://blog.csdn.net/watermusicyes/article/details/44963773) ## 71.程序崩溃日志收集 ``` Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, final Throwable ex) { // Custom code here to handle the error. L.e("发生崩溃=="+thread.getName()+" =="+ex.getMessage()); } }); ``` ## 72.Android Q正式发布。新一代的安卓系统非常注重对用户隐私的保护,它限制APP获取IMEI、DEVICE ID等移动端设备标识码,解决方案: - 简书:[https://www.jianshu.com/p/df3f549ddd35](https://www.jianshu.com/p/df3f549ddd35) 官方:[https://developer.android.google.cn/training/articles/user-data-ids](https://developer.android.google.cn/training/articles/user-data-ids) ## 73.再按一次退出程序 ``` private long firstTime = 0; @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: long secondTime = System.currentTimeMillis(); //如果两次按键时间间隔大于2秒,则不退出 if (secondTime - firstTime > 2000) { Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show(); firstTime = secondTime;//更新firstTime return true; //两次按键小于2秒时,退出应用 } else { finish(); } break; } return super.onKeyUp(keyCode, event); } ``` 如果是Fragment请不要复写onBackPressed()方法,改为复写onBackPressedSupport(): ``` //再按一次退出程序 private long firstTime = 0; @Override public void onBackPressedSupport() { long secondTime = System.currentTimeMillis(); if (getSupportFragmentManager().getBackStackEntryCount() == 1 && secondTime - firstTime > 2000) { Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show(); firstTime = secondTime; } else { super.onBackPressedSupport(); } } ``` ## 74.AndroidX升级日记 [https://www.jianshu.com/p/499e645ad148](https://www.jianshu.com/p/499e645ad148) ## 75.AndroidStudio启动模拟器Android10.0报错 Emulator: Process finished with exit code -1073741819 (0xC0000005) [https://stackoverflow.com/questions/47631771/emulator-process-finished-with-exit-code-1073741819-0xc0000005](https://stackoverflow.com/questions/47631771/emulator-process-finished-with-exit-code-1073741819-0xc0000005) ## 76.简单实现ImageView宽度填满屏幕,高度自适应的两种方式 [https://www.jianshu.com/p/c9424615e99d](https://www.jianshu.com/p/c9424615e99d) ## 77.RecyclerView瀑布流问题 [https://www.jianshu.com/p/4e142909b824](https://www.jianshu.com/p/4e142909b824) [https://blog.csdn.net/tobevan/article/details/78924338](https://blog.csdn.net/tobevan/article/details/78924338) ## 78.android studio关闭字符串资源多语言翻译提示 ``` <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> ``` ## 79.ConstraintLayout 采用代码方式布局用法简介 [http://xgfe.github.io/2017/09/17/ivanchou/layout-with-constraintlayout-by-programming/](http://xgfe.github.io/2017/09/17/ivanchou/layout-with-constraintlayout-by-programming/) ## 80.Glide获得图片资源,并高斯模糊 - 高斯模糊是个耗时过程,需要在子线程进行操作 ``` try { Drawable drawable = Glide.with(mContext) .load(item.getImage_url()) .apply(RequestOptions.bitmapTransform(new BlurTransformation(15, 1))) .submit().get(); layoutRoot.setBackground(drawable); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } ``` - 高斯模糊 ``` public class BlurTransformation extends BitmapTransformation { private static final int VERSION = 1; private static final String ID = "BlurTransformation." + VERSION; private static int MAX_RADIUS = 25; private static int DEFAULT_DOWN_SAMPLING = 1; private int radius; private int sampling; public BlurTransformation() { this(MAX_RADIUS, DEFAULT_DOWN_SAMPLING); } public BlurTransformation(int radius) { this(radius, DEFAULT_DOWN_SAMPLING); } public BlurTransformation(int radius, int sampling) { this.radius = radius; this.sampling = sampling; } @Override protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { int width = toTransform.getWidth(); int height = toTransform.getHeight(); int scaledWidth = width / sampling; int scaledHeight = height / sampling; Bitmap bitmap = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.scale(1 / (float) sampling, 1 / (float) sampling); Paint paint = new Paint(); paint.setFlags(Paint.FILTER_BITMAP_FLAG); canvas.drawBitmap(toTransform, 0, 0, paint); bitmap = FastBlur.blur(bitmap, radius, true); return bitmap; } @Override public String toString() { return "BlurTransformation(radius=" + radius + ", sampling=" + sampling + ")"; } @Override public boolean equals(Object o) { return o instanceof BlurTransformation && ((BlurTransformation) o).radius == radius && ((BlurTransformation) o).sampling == sampling; } @Override public int hashCode() { return ID.hashCode() + radius * 1000 + sampling * 10; } @Override public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { messageDigest.update((ID + radius + sampling).getBytes(CHARSET)); } } ``` - 其中的FastBlur来至库glide-transformations [https://github.com/wasabeef/glide-transformations](https://github.com/wasabeef/glide-transformations) ## 81.Glide获得获取图片主体色系 ``` Glide.with(mContext).load(item.getImage_url()) .listener(GlidePalette.with(item.getImage_url()) .use(GlidePalette.Profile.VIBRANT) .intoBackground(layoutRoot, GlidePalette.Swatch.RGB) .crossfade(true) ).into(imageView); ``` ## 82.Android获取内网IP - 方法一: ``` package com.rongyan.clienttest; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.regex.Matcher; import java.util.regex.Pattern; public class NetWorkUtil { //匹配C类地址的IP public static final String regexCIp = "^192\\.168\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$"; //匹配A类地址 public static final String regexAIp = "^10\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$"; //匹配B类地址 public static final String regexBIp = "^172\\.(1[6-9]|2\\d|3[0-1])\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)\\.(\\d{1}|[1-9]\\d|1\\d{2}|2[0-4]\\d|25\\d)$"; public static String getHostIp() { String hostIp; Pattern ip = Pattern.compile("(" + regexAIp + ")|" + "(" + regexBIp + ")|" + "(" + regexCIp + ")"); Enumeration<NetworkInterface> networkInterfaces = null; try { networkInterfaces = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e) { e.printStackTrace(); } InetAddress address; while (networkInterfaces.hasMoreElements()) { NetworkInterface networkInterface = networkInterfaces.nextElement(); Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses(); while (inetAddresses.hasMoreElements()) { address = inetAddresses.nextElement(); String hostAddress = address.getHostAddress(); Matcher matcher = ip.matcher(hostAddress); if (matcher.matches()) { hostIp = hostAddress; return hostIp; } } } return null; } } ``` - 方法二: ``` public String getWifiIp() { //获取wifi服务 WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); //判断wifi是否开启 if (!wifiManager.isWifiEnabled()) { wifiManager.setWifiEnabled(true); } WifiInfo wifiInfo = wifiManager.getConnectionInfo(); int ipAddress = wifiInfo.getIpAddress(); return intToIp(ipAddress); } //获取Wifi ip 地址 private String intToIp(int i) { return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + (i >> 24 & 0xFF); } ``` - 参考:[https://www.cnblogs.com/jxust-jiege666/p/8168149.html](https://www.cnblogs.com/jxust-jiege666/p/8168149.html) ## 83.Android获取外网IP - 参考:[https://www.jianshu.com/p/1e3eaf887191](https://www.jianshu.com/p/1e3eaf887191) - 方法一,通过访问第三方接口地址来获取 ``` import org.json.JSONException; import org.json.JSONObject; /** * 获取外网IP地址 * @return */ public void GetNetIp() { new Thread(){ @Override public void run() { String line = ""; URL infoUrl = null; InputStream inStream = null; try { infoUrl = new URL("http://pv.sohu.com/cityjson?ie=utf-8"); URLConnection connection = infoUrl.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection) connection; int responseCode = httpConnection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { inStream = httpConnection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8")); StringBuilder strber = new StringBuilder(); while ((line = reader.readLine()) != null) strber.append(line + "\n"); inStream.close(); // 从反馈的结果中提取出IP地址 int start = strber.indexOf("{"); int end = strber.indexOf("}"); String json = strber.substring(start, end + 1); if (json != null) { JSONObject jsonObject = null; try { jsonObject = new JSONObject(json); } catch (JSONException e) { e.printStackTrace(); } line = jsonObject.optString("cip"); } L.e("line=="+line); Message msg = new Message(); msg.what = 1; msg.obj = line; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } ``` ## 84.颜色均匀渐变的动画 ``` ValueAnimator animator = ValueAnimator.ofInt(0xffffff00,0xff0000ff); animator.setEvaluator(new ArgbEvaluator()); animator.setDuration(3000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int curValue = (int)animation.getAnimatedValue(); tv.setBackgroundColor(curValue); } }); animator.start(); ``` ## 85.Android的cpu架构7种 - 参考:[https://blog.csdn.net/u012400885/article/details/52923765](https://blog.csdn.net/u012400885/article/details/52923765) [https://www.jianshu.com/p/f29ad4beef59](https://www.jianshu.com/p/f29ad4beef59) ## 86.'gradle' 不是内部或外部命令,也不是可运行的程序 或批处理文件 [https://blog.csdn.net/u014743890/article/details/84316176](https://blog.csdn.net/u014743890/article/details/84316176) ## 87.使用ImmersionBar时,加载出现Dialog的显示会导致导航栏出现不消失: ## 88.Dagger2-Android不支持泛型Activity的注入 [https://blog.csdn.net/ybf326/article/details/82931587](https://blog.csdn.net/ybf326/article/details/82931587) ## 89.删除git配置  ## 90.把本地项目复制到github  ## 91.AndroidStudio设置github账号  ## 92.邀请码识别来源 [https://www.openinstall.io/](https://www.openinstall.io/) ## 93.Android 自动抓取网站图标实现分享样式的定制 [https://blog.csdn.net/dnsliu/article/details/57122535](https://blog.csdn.net/dnsliu/article/details/57122535) ## 94.Fragment实现懒加载 [https://blog.csdn.net/vic6329063/article/details/82838430](https://blog.csdn.net/vic6329063/article/details/82838430) ## 95.版权登记 [http://www.ccopyright.com/index.php?optionid=1216](http://www.ccopyright.com/index.php?optionid=1216) ## 96.获取随机颜色值 分别取rgb的随机值(0~256),然后加起来就是一个随机颜色值,通过Color.parseColor()转为color值即可使用: ``` public static String getRandColor() { String R, G, B; Random random = new Random(); R = Integer.toHexString(random.nextInt(256)).toUpperCase(); G = Integer.toHexString(random.nextInt(256)).toUpperCase(); B = Integer.toHexString(random.nextInt(256)).toUpperCase(); R = R.length() == 1 ? "0" + R : R; G = G.length() == 1 ? "0" + G : G; B = B.length() == 1 ? "0" + B : B; return "#" + R + G + B; } ``` ## 97.Glide设置圆角图片后设置ImageVIew的scanType="centerCrop"无效解决办法 [https://www.jianshu.com/p/95d3f64a48dc](https://www.jianshu.com/p/95d3f64a48dc) ``` RequestOptions options = new RequestOptions() .transform(new CenterCrop(),new RoundedCorners(18)) .placeholder(R.drawable.no_banner) .error(R.drawable.no_banner); Glide.with(context).load(path) .apply(options) .thumbnail(0.1f) .into(imageView); ``` ## 98.TabLayout修改字体大小,字体颜色;去除点击效果 [https://www.jianshu.com/p/c5c42d6fec60](https://www.jianshu.com/p/c5c42d6fec60) ## 99.悬浮球: - 适合单页添加一个悬浮球:无需权限的悬浮球,快收到你碗里去吧! [https://www.jianshu.com/p/a64423b50065](https://www.jianshu.com/p/a64423b50065) - 一个极好的全局悬浮窗框架:EasyFloat ## 100.Android视频播放,选择,压缩,上传 [https://www.jianshu.com/p/78b7176c041e](https://www.jianshu.com/p/78b7176c041e) ## 101.循环枚举获得枚举值 ``` public static <T extends Enum<T> & BaseEnum> T getEnumType(String enumCode, Class<T> enumClass) { T enumType = null; EnumSet<T> enumSet = EnumSet.allOf(enumClass); //获取枚举类型 for (T enumItem : enumSet) { //循环枚举 if(enumItem.getEnumCode().equals(enumCode)) { enumType = enumItem; break; } } return enumType; } ``` ## 102.权限申请 - AndPermission [https://github.com/yanzhenjie/AndPermission](https://github.com/yanzhenjie/AndPermission) - 文档:[https://yanzhenjie.com/AndPermission/cn/](https://yanzhenjie.com/AndPermission/cn/) - 不再维护,改用 - PermissionsDispatcher [https://github.com/permissions-dispatcher/PermissionsDispatcher](https://github.com/permissions-dispatcher/PermissionsDispatcher) ``` AndPermission.with(FortuneFragment.this) .runtime() .permission(Permission.Group.STORAGE) .onGranted(permissions -> { ToastUtil.showShort("get permiss"); }) .onDenied(new Action<List<String>>() { @Override public void onAction(List<String> permissions) { ToastUtil.showShort("你拒绝了获取此权限!"); // 这些权限被用户总是拒绝。 if (AndPermission.hasAlwaysDeniedPermission(FortuneFragment.this, permissions)) { new AlertDialog.Builder(FortuneFragment.this.getActivity()) .setTitle("权限申请") .setMessage("需要此权限才能使用此功能,去设置?") .setPositiveButton("去设置", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AppUtils.goIntentSetting(FortuneFragment.this.getActivity()); } }) .setNegativeButton("取消",null) .show(); } } }) .start(); ``` ## 103.获取栈顶的Activity - android获取当前栈顶的activity [https://blog.csdn.net/y2653904/article/details/85157253](https://blog.csdn.net/y2653904/article/details/85157253) ## 104.关于Android弹出软键盘“顶起”View的问题 Activity -> android:windowSoftInputMode="adjustPan|stateHidden" [https://blog.csdn.net/q4878802/article/details/94382815](https://blog.csdn.net/q4878802/article/details/94382815) ``` public void setHintKeyboardView(View view) { view.setOnTouchListener((v, event) -> { hintKeyboard(TrendInfoActivity.this); return false; }); if (view instanceof ViewGroup){ ViewGroup viewGroup = (ViewGroup) view; for (int i = 0; i < viewGroup.getChildCount(); i++) { setHintKeyboardView(viewGroup.getChildAt(i)); } } } public static void hintKeyboard(Activity activity) { InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm.isActive() && activity.getCurrentFocus() != null) { if (activity.getCurrentFocus().getWindowToken() != null) { imm.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } } ``` - 要顶起的控件 ``` setHintKeyboardView(db.layoutComment); ``` - 注意:主要布局xml中要设置:android:fitsSystemWindows="true"才生效 ## 105.DataBind中神奇的“最”字 - 出现异常: Caused by: org.apache.xerces.impl.io.MalformedByteSequenceException: Invalid byte 3 of 3-byte UTF-8 sequence. - 原因是在xml中使用了“最”字和对象的结合 ``` <TextView android:id="@+id/textView18" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@{`最大特征:`+data.attr.zdtd}" android:textColor="@color/textDarkGray" android:textSize="14sp" app:layout_constraintStart_toStartOf="@+id/textView17" app:layout_constraintTop_toBottomOf="@+id/textView17" tools:text="最大特征:渴望" /> ``` - 估计是DataBind的一个bug,只有在代码中写这个“最”字了。 ``` db.textView18.setText("最大特征:"+data.getAttr().getZdtd()); ``` - 看来不止这一个字,再次发现:邀字 ## 106.可以调整间隙的星星控件 - SimpleRatingBar - 如果使用DataBind出错,需要下载下来,写入设置星星个数的setter方法。 ## 107.背景高斯模糊 ``` private Bitmap applyBlur() { Bitmap bitmap = ScreenUtils.snapShotWithStatusBar(this.getActivity()); return blur(bitmap); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private Bitmap blur(Bitmap bkg) { long startMs = System.currentTimeMillis(); float radius = 1; bkg = small(bkg); Bitmap bitmap = bkg.copy(bkg.getConfig(), true); final RenderScript rs = RenderScript.create(this.getContext()); final Allocation input = Allocation.createFromBitmap(rs, bkg, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); final Allocation output = Allocation.createTyped(rs, input.getType()); final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); script.setRadius(radius); script.setInput(input); script.forEach(output); output.copyTo(bitmap); bitmap = big(bitmap); MainActivity.ivCover.setBackground(new BitmapDrawable(getResources(), bitmap)); rs.destroy(); Log.d("zhangle","blur take away:" + (System.currentTimeMillis() - startMs )+ "ms"); return bitmap; } private static Bitmap big(Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postScale(4f,4f); //长和宽放大缩小的比例 Bitmap resizeBmp = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true); return resizeBmp; } private static Bitmap small(Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postScale(0.25f,0.25f); //长和宽放大缩小的比例 Bitmap resizeBmp = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true); return resizeBmp; } ``` ## 108.java Enum 枚举帮助类,根据值或者名称获取枚举类型对象 [https://blog.csdn.net/busbanana/article/details/72954676](https://blog.csdn.net/busbanana/article/details/72954676) ## 109'Android打开系统拍照&相册获取头像 - 参考Android打开系统拍照&相册获取头像 [https://www.jianshu.com/p/e29e24b88440](https://www.jianshu.com/p/e29e24b88440) - 使用PictureSelector获取单张压缩图片: ``` private static void getPic(Activity activity) { PictureSelector.create(activity) .openGallery(PictureMimeType.ofImage()) .loadImageEngine(GlideEngine.createGlideEngine()) .isWeChatStyle(true) .selectionMode(PictureConfig.SINGLE) .isWithVideoImage(true) .previewImage(true) .isCamera(true) .compress(true) .compressSavePath(activity.getExternalCacheDir().getPath()) .forResult(result -> { LocalMedia localMedia = result.get(0); String compressPath = localMedia.getCompressPath(); }); } ``` 使用上面的PictureSelector在低版本的手机或个别机型上拍照会出现崩溃,我使用时它的版本是v2.5.9,不知道修复没有。因此我们最好还是使用系统原生的: ``` public static final int REQUEST_CAMERA = 909; private String compressPath;//拍照后的图片地址 public void tackPhoto() { compressPath = getFileName(); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Uri uriForFile = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", new File(compressPath)); intent.putExtra(MediaStore.EXTRA_OUTPUT, uriForFile); } else { intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(compressPath))); } startActivityForResult(intent, REQUEST_CAMERA); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == REQUEST_CAMERA) { L.e("图片地址==" + compressPath + " 图片大小==" + FormatUtils.formatSize(FileUtils.getFileSize(new File(compressPath)))); if (TextUtils.isEmpty(compressPath)) return; Luban.with(this) .load(compressPath) .ignoreBy(100) .setTargetDir(getExternalCacheDir().getPath())//压缩后的图片保存到应用缓存目录 .setCompressListener(new OnCompressListener() { @Override public void onStart() {} @Override public void onSuccess(File file) { compressPath = file.getPath(); L.e("压缩后图片地址==" + file.getPath() + " 图片大小==" + FormatUtils.formatSize(FileUtils.getFileSize(file))); signPop.setIvTackPic(compressPath); } @Override public void onError(Throwable e) {} }) .launch(); } } public String getFileName() { String saveDir = getExternalCacheDir().getPath(); File dir = new File(saveDir); if (!dir.exists()) { dir.mkdir(); // 创建文件夹 } //用日期作为文件名,确保唯一性 Date date = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); String fileName = saveDir + "/" + formatter.format(date) + ".jpg"; return fileName; } ``` - 如果想用系统直接打开前置摄像头: ``` intent.putExtra("android.intent.extras.CAMERA_FACING", 1); // 调用前置摄像头 ``` ## 110. FileProvider相关 - Android之FileProvider :通过FileProvider来获取content uri [https://blog.csdn.net/yegshun/article/details/81478619](https://blog.csdn.net/yegshun/article/details/81478619) - android 7.0+ FileProvider 访问隐私文件 相册、相机、安装应用的适配 [https://www.cnblogs.com/bimingcong/p/10328429.html](https://www.cnblogs.com/bimingcong/p/10328429.html) ## 111. view 可见性 监听探究 [https://www.jianshu.com/p/a5cc954b997c](https://www.jianshu.com/p/a5cc954b997c) ## 112. [android checkbox 未选中状态 已选中状态 替换成自己的图片] - 首先创建check_box_selector.xml ``` <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/cb_pressed" android:state_checked="true"></item> <item android:drawable="@drawable/cb_normal" android:state_checked="false"></item> </selector> ``` - 然后对CheckBox设置android:button="@drawable/check_box_selector" - 如果想然选中状态在右侧,可以设置 `android:layoutDirection="rtl"` 进行反向绘制 ## 113. 获取今日,明日,本周,本月,今年时间 [https://www.cnblogs.com/shuilangyizu/p/6902643.html](https://www.cnblogs.com/shuilangyizu/p/6902643.html) ``` Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00")); int firstDayOfWeek = calendar.getFirstDayOfWeek(); List<String> times =new ArrayList<>(); for (int i = 0; i < 7; i++) { calendar.set(Calendar.DAY_OF_WEEK, firstDayOfWeek + i); // 获取星期的显示名称,例如:周一、星期一、Monday等等 String format = new SimpleDateFormat("MM月dd日").format(calendar.getTime()); times.add(format); } String time6 = times.get(6); time6 = time6.substring(time6.indexOf("月")+1); calendar.add(Calendar.DAY_OF_MONTH, -1); String jr = new SimpleDateFormat("MM月dd日").format(calendar.getTime()); calendar.add(Calendar.DAY_OF_MONTH, +1); String mr = new SimpleDateFormat("MM月dd日").format(calendar.getTime()); dayName[0] = "今日运势("+jr+")"; dayName[1] = "明日运势("+mr+")"; dayName[2] = "本周运势("+times.get(0)+"-"+time6+")"; dayName[3] = "本月运势("+(calendar.get(Calendar.MONTH) + 1)+"月)"; dayName[4] = "今年运势("+calendar.get(Calendar.YEAR)+"年)"; dayNameShow = dayName[0]; ``` ## 114. Android定位相关 - Android系统GPS定位实现 [https://blog.csdn.net/ryshuang/article/details/90265265](https://blog.csdn.net/ryshuang/article/details/90265265) - Andriod 手机定位 解决location为null的问题 [https://blog.csdn.net/liufangbaishi2014/article/details/46899305](https://blog.csdn.net/liufangbaishi2014/article/details/46899305) - Android地图开发中的地理编码与地理反编码 [https://blog.csdn.net/syy6/article/details/5714631](https://blog.csdn.net/syy6/article/details/5714631) - 经纬度查询:[https://www.juhe.cn/cellmap/lat](https://www.juhe.cn/cellmap/lat) Android之GPS定位类 LocationManager、LocationListener、GpsStatus.Listener、Location详解 [https://blog.csdn.net/w690333243/article/details/77658303](https://blog.csdn.net/w690333243/article/details/77658303) - 总结,使用: ``` (方式一) locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 3000, 0, locationListener); ``` - 比 ``` (方式二) locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 0, locationListener); ``` - 好用得多! 使用网络方式定位比gps定位更高概率拿到Location对象,从而获取经纬度。但要注意使用网络LocationManager.NETWORK_PROVIDER的方式,依然要开启gps和网络,否则依然无法获取Location对象。而(方式二)我只有一次获取成功了,之后的监听再也没拿到过Location对象。 ## 115. 热修复 [https://www.jianshu.com/p/af8c47fabb12](https://www.jianshu.com/p/af8c47fabb12) ## 116.Android中给View设置阴影的三种方式 [https://blog.csdn.net/wang29169/article/details/84206379](https://blog.csdn.net/wang29169/article/details/84206379) - 关于CardView参考:使用CardView实现卡片式设计 [https://www.jianshu.com/p/31f163f5c9d9](https://www.jianshu.com/p/31f163f5c9d9) - 或者第三方的控件 shadow-layout [https://github.com/dmytrodanylyk/shadow-layout](https://github.com/dmytrodanylyk/shadow-layout) ## 117.Android去掉阻尼效果 - 只需要在控件中添加如下属性即可:android:overScrollMode="never" ## 118.Android版本升级坑 [https://www.jianshu.com/p/123c12218b7a](https://www.jianshu.com/p/123c12218b7a) ## 119.startActivityForResult的用法,以及intent传递图片 - startActivityForResult的用法 [https://blog.csdn.net/bangxianzhou5100/article/details/101077196](https://blog.csdn.net/bangxianzhou5100/article/details/101077196) - fragment中的startActivityForResult无效: - 谈谈Fragment中的onActivityResult [https://blog.csdn.net/boise/article/details/78910032](https://blog.csdn.net/boise/article/details/78910032) ## 120.第三方地图接入相关 - 高德地图接入笔记 [https://www.jianshu.com/p/77c305d64deb](https://www.jianshu.com/p/77c305d64deb) ## 121.34个省份,可以用死数据 ``` private String[] provinceList =new String[]{"北京市", "天津市", "河北省", "山西省", "内蒙古自治区", "辽宁省", "吉林省","黑龙江省", "上海市", "江苏省", "浙江省", "安徽省", "福建省", "江西省", "山东省","河南省", "湖北省", "湖南省", "广东省", "广西壮族自治区", "海南省", "重庆市","四川省", "贵州省", "云南省", "西藏自治区", "陕西省", "甘肃省", "青海省", "宁夏回族自治区", "新疆维吾尔自治区", "台灣", "香港特别行政区", "澳门特别行政区"}; ``` ## 122.Android数据库 - 注释类型数据库:greenDAO - GreenDAO 完美解决数据库升级数据丢失问题 [https://blog.csdn.net/qq_35956194/article/details/79167897](https://blog.csdn.net/qq_35956194/article/details/79167897) - 关系型数据库 LitePal - 数据库调试 Glance - 数据库可视化调试工具Glance [https://mp.weixin.qq.com/s/OX1-BRA2Qy8QRboYFicrwg](https://mp.weixin.qq.com/s/OX1-BRA2Qy8QRboYFicrwg) ## 123.3.6.1AndroidStudio开启离线编译 [https://blog.csdn.net/c603751448/article/details/104801723](https://blog.csdn.net/c603751448/article/details/104801723) ## 124.实现选一个视频或图片进行发布动态,朋友圈 - 使用PictureSelector实现 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.4.6' ``` private List<LocalMedia> datas = new ArrayList<>();//选中的数据项 private void getPic() { PictureParameterStyle pictureParameterStyle = new PictureParameterStyle(); pictureParameterStyle.pictureStatusBarColor = 0xFF393a3e; pictureParameterStyle.pictureTitleBarBackgroundColor = 0xFF393a3e; PictureSelector.create(this) .openGallery(PictureMimeType.ofAll()) .loadImageEngine(GlideEngine.createGlideEngine()) .isWeChatStyle(true) .isWithVideoImage(false) .maxSelectNum(5) .maxVideoSelectNum(1) .selectionMode(PictureConfig.MULTIPLE) .previewImage(true) .isCamera(true) .compress(true) .compressSavePath(this.getExternalCacheDir().getPath()) .setPictureStyle(pictureParameterStyle) .forResult(result -> { datas.clear(); db.layoutVideoImg.removeAllViews(); for (int i = 0; i < result.size(); i++) { LocalMedia localMedia = result.get(i); db.layoutVideoImg.addView(createVideoView(localMedia)); datas.add(localMedia); } if (datas.size()<5)db.layoutVideoImg.addView(createVideoView(null)); }); } ``` ## 125.Android 圆形进度条 [https://www.jianshu.com/p/6e4641ff74ba](https://www.jianshu.com/p/6e4641ff74ba) ## 126.Android开发之SVG的使用 [https://www.jianshu.com/p/845ac47cb1f1](https://www.jianshu.com/p/845ac47cb1f1) ## 127.Android移除含有动画的View - 今天遇到removeAllViews无效的问题,原来需要结束里面子控件的动画后再移除: ``` layoutParent.getChildAt(i).clearAnimation(); //然后 layoutParent.removeAllViews(); ``` ## 128.Android Studio项目中文件错乱问题 - Android Studio项目中文件错乱问题 [https://blog.csdn.net/weixin_39397471/article/details/103196046](https://blog.csdn.net/weixin_39397471/article/details/103196046) - AndroidStudio 代码报错(引入红色),但可正常运行解决方法(4种方式) [https://blog.csdn.net/hx7013/article/details/79971980](https://blog.csdn.net/hx7013/article/details/79971980) ## 129.Android代码中设置颜色选择器 ``` btn.setTextColor(getResources().getColorStateList(R.color.tab_black_brown_selector)); ``` ## 130.【Android】WebView的input上传照片的兼容问题 [https://juejin.im/post/5a322cdef265da43176a2913](https://juejin.im/post/5a322cdef265da43176a2913) ## 131.系统选择器SwitchCompat - support.v7.widget.SwitchCompat(带文字的开关) [https://blog.csdn.net/qq_23089525/article/details/51742836](https://blog.csdn.net/qq_23089525/article/details/51742836) - Android SwitchCompat开关按钮自定义颜色和样式 [https://blog.csdn.net/andguohaolei/article/details/72954010](https://blog.csdn.net/andguohaolei/article/details/72954010) ## 132.Retrofit学习之文件和参数上传,上传头像 [https://www.jianshu.com/p/74b7da380855](https://www.jianshu.com/p/74b7da380855) ## 133.adb被占用 - 找出占用这个端口的应用,一般是第三方的关联手机的进程,直接杀掉,如:LdsMobileLink.exe ``` netstat -aon|findstr "5037" ``` ## 134.使用Retrofit多图,多参数,上传 - 接口定义,3个参数,一个数组的map ``` /** * 发动态 images 数组 可选 [] 图片,文件对象数组 无 video 字符串 可选 视频,文件对象 无 content 字符串 必须 文字内容 无 servant_id 字符串 必须 服务者用户ID 无 is_anonymous 字符串 必须 是否匿名发布 1否2是 */ @Multipart @POST("user/blogu/create") Observable<BaseResponse<BaseErrResponse>> blogCreate(@Part("content") RequestBody content, @Part("servant_id") RequestBody servant_id, @Part("is_anonymous") RequestBody is_anonymous, @PartMap Map<String, RequestBody> map); ``` - 上传5张图片,或者1个视频 ``` /** * images 数组 可选 [] 图片,文件对象数组 无 * video 字符串 可选 1个视频,文件对象 无 * content 字符串 必须 文字内容 无 * servant_id 字符串 必须 服务者用户ID 无 * is_anonymous 字符串 必须 是否匿名发布 1否2是 */ private void submitContent() { if (!hasText) { ToastUtil.showLong("请输入内容!"); return; } if (isVideo && !isCompressedVideo) { ToastUtil.showLong("视频压缩中,请等待..."); return; } String content = db.etContent.getText().toString(); Map<String, RequestBody> map = new HashMap<>(); if (!haveVideo) {//图片 for (int i = 0; i < datas.size(); i++) { LocalMedia localMedia = datas.get(i); String compressPath = localMedia.getCompressPath(); File file = new File(compressPath); RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); map.put("images[]\"; filename=\"" + file.getName(), fileBody); } } else {//视频 File file = new File(videoUploadPath); RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file); map.put("video\"; filename=\"" + file.getName(), fileBody); } RequestBody contentBody = RequestBody.create(MediaType.parse("text/plain"), content); RequestBody servantBody = RequestBody.create(MediaType.parse("text/plain"), servant_id > 0 ? servant_id + "" : ""); RequestBody isAnonymousBody = RequestBody.create(MediaType.parse("text/plain"), isAnonymous ? "2" : "1"); dataProvider.blog.blogCreate(contentBody, servantBody, isAnonymousBody, map) .subscribe(new OnSuccessAndFailListener<BaseResponse<BaseErrResponse>>(dialog) { @Override protected void onSuccess(BaseResponse<BaseErrResponse> baseResponse) { ToastUtil.showLong(baseResponse.getData().getMessage()); finish(); } }); } ``` ## 135.安卓10(Android10/API29)保存图片到相册DCIM/Camera - 安卓10(Android10/API29)保存图片到相册DCIM/Camera ## 136.Android动态的设置5图或1视频,并播放或浏览图片 [https://www.jianshu.com/p/e55baa9ef04e](https://www.jianshu.com/p/e55baa9ef04e) ## 137.Android用原生切换按钮SwitchCompat,改为仿IOS按钮 ``` <androidx.appcompat.widget.SwitchCompat android:textSize="14sp" android:checked="true" android:textColor="@color/tv_gray" app:track="@drawable/switch_track" android:thumb="@drawable/switch_thum" app:switchPadding="16dp" android:padding="16dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:layoutDirection="rtl" android:text="匿名发布,猜猜我是谁" /> ``` - switch_track.xml ``` <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape> <corners android:radius="30dp"/> <solid android:color="#E9E6EA"/> <size android:width="53dp" android:height="20dp"/> </shape> </item> <item android:state_checked="false"> <shape> <corners android:radius="30dp"/> <solid android:color="#ff6d00"/> <size android:width="53dp" android:height="20dp"/> </shape> </item> </selector> ``` - switch_thum.xml ``` <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape android:shape="oval"> <size android:width="24dp" android:height="24dp"/> <solid android:color="#ffffff"/> <stroke android:width="1dp" android:color="#E9E6EA"/> </shape> </item> <item android:state_checked="false"> <shape android:shape="oval"> <size android:width="24dp" android:height="24dp"/> <solid android:color="#ffffff"/> <stroke android:width="1dp" android:color="#ff6d00"/> </shape> </item> </selector> ```  - 注意:如果要设置开关的文字 ``` android:textOff="关" android:textOn="开" app:showText="true" ``` ## 138.JAVA 根据时间戳判断日期是否为刚刚,今天、昨天 ``` public static void commentTime(TextView tvTime ,int oldTime) { int nowTime = (int) (System.currentTimeMillis() / 1000); String timeStr="刚刚"; //小于5分钟就是刚刚发送的 if (nowTime-oldTime<60*5){ tvTime.setText(timeStr); return; } long day = (nowTime - oldTime) / (24 * 60 * 60); long time1000 = Long.parseLong(String.valueOf(oldTime)) * 1000; SimpleDateFormat format; if (day < 1) { //今天 format = new SimpleDateFormat("HH:mm", Locale.CHINA); timeStr = "今天 " +format.format(time1000); } else if (day == 1) { //昨天 format = new SimpleDateFormat("HH:mm", Locale.CHINA); timeStr = "昨天 " + format.format(time1000); } else { format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA); timeStr = format.format(time1000); } tvTime.setText(timeStr); } ``` ## 139.贝塞尔曲线 - 必须要理解掌握的贝塞尔曲线(原创)[https://www.jianshu.com/p/0c9b4b681724](https://www.jianshu.com/p/0c9b4b681724) ## 140.APP上架需要准备的材料清单(上架规范和流程) [https://blog.csdn.net/qq_36256748/article/details/89511470](https://blog.csdn.net/qq_36256748/article/details/89511470) ## 141.Glide实现,居中裁剪,后高斯模糊,再圆角显示。 ``` public static void blurUrl(ImageView imageView, String url) { RequestOptions options = new RequestOptions() .transform(new MultiTransformation<Bitmap>( new CenterCrop(), new BlurTransformation( 25, 1 ), new RoundedCorners(DpPxUtils.dp2px(6)) //设置图片圆角角度 )) .placeholder(R.mipmap.ic_site_big) .error(R.mipmap.ic_site_big); Glide.with(imageView) .load(url) .apply(options) .into(imageView); } ``` ## 142.强制竖屏 ``` @SuppressLint("SourceLockedOrientationActivity") @Override protected void onCreate(@Nullable Bundle savedInstanceState) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); super.onCreate(savedInstanceState); } ``` ## 143.屏幕适配,字体适配,图标适配 - 屏幕适配:[https://www.jianshu.com/p/1302ad5a4b04](https://www.jianshu.com/p/1302ad5a4b04) - 今日头条适配方案AndroidAutoSize:[https://github.com/JessYanCoding/AndroidAutoSize](https://github.com/JessYanCoding/AndroidAutoSize) - 字体大小适配:[https://www.jianshu.com/p/2fdc97ae74a8](https://www.jianshu.com/p/2fdc97ae74a8) - 图标适配:[https://blog.csdn.net/guolin_blog/article/details/79417483](https://blog.csdn.net/guolin_blog/article/details/79417483) ## 144.获取签名工具 [https://open.weixin.qq.com/cgi-bin/readtemplate?t=resource/app_download_android_tmpl&lang=zh_CN](https://open.weixin.qq.com/cgi-bin/readtemplate?t=resource/app_download_android_tmpl&lang=zh_CN) ## 145.Android Glide4.0+图片加载进度监听 - Android Glide4.0+图片加载进度监听:[https://www.jianshu.com/p/17d9de68bab3](https://www.jianshu.com/p/17d9de68bab3) ## 146.监听RecyclerView滚动到最后一项,RecyclerView加载更多 ``` //加载更多 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); // 当不滚动时 if (newState == RecyclerView.SCROLL_STATE_IDLE) { //获取最后一个完全显示的ItemPosition int lastVisibleItem = manager.findLastCompletelyVisibleItemPosition(); int totalItemCount = manager.getItemCount(); // 判断是否滚动到底部,并且是向右滚动 if (lastVisibleItem == (totalItemCount - 1)) { //加载更多功能的代码 } } } }); ``` ## 147.Android RecyclerView使用 及 滑动时加载图片优化方案 - Android RecyclerView使用 及 滑动时加载图片优化方案 [https://blog.csdn.net/weixin_34199405/article/details/86255485](https://blog.csdn.net/weixin_34199405/article/details/86255485) - 优化大量图片加载recyclerview列表的滑动效果 [https://blog.csdn.net/stimgo/article/details/86607278](https://blog.csdn.net/stimgo/article/details/86607278) ## 148.其实很多图片不是必须的,用xml的bitmap来创建不同颜色的图片 [https://www.jianshu.com/p/249b10f5c4b7](https://www.jianshu.com/p/249b10f5c4b7) ## 149.Android Studio 安装APK在真机安装时报 Installation failed due to: 'null' 解决 [https://www.jianshu.com/p/31edef558c99](https://www.jianshu.com/p/31edef558c99) - 除了上面说的,我还遇到一种情况就是使用了RenderScript,去掉RenderScript并删除对应的rs文件即可。 ## 150.android之Animation自定义动画轨迹 CSDN:[https://blog.csdn.net/kaikevin01/article/details/78871197](https://blog.csdn.net/kaikevin01/article/details/78871197) - 简书:[https://www.jianshu.com/p/f64c3cd25f67](https://www.jianshu.com/p/f64c3cd25f67) ## 151.获取图片File的宽高,图片宽高 ``` //获取图片的宽高 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true;//仅做解码处理,不加载到内存 BitmapFactory.decodeFile(file.getAbsolutePath(), options);//解析文件 int width = options.outWidth; int height = options.outHeight; ``` ## 152.雪花算法生成id ``` public class SnowFlakeWorkId { //因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。 //机器ID 2进制5位 32位减掉1位 31个 private long workerId; //机房ID 2进制5位 32位减掉1位 31个 private long datacenterId; //代表一毫秒内生成的多个id的最新序号 12位 4096 -1 = 4095 个 private long sequence; //设置一个时间初始值 2^41 - 1 差不多可以用69年 private long twepoch = 1585644268888L; //5位的机器id private long workerIdBits = 5L; //5位的机房id private long datacenterIdBits = 5L; //每毫秒内产生的id数 2 的 12次方 private long sequenceBits = 12L; // 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内 private long maxWorkerId = -1L ^ (-1L << workerIdBits); // 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内 private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private long workerIdShift = sequenceBits; private long datacenterIdShift = sequenceBits + workerIdBits; private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private long sequenceMask = -1L ^ (-1L << sequenceBits); //记录产生时间毫秒数,判断是否是同1毫秒 private long lastTimestamp = -1L; public long getWorkerId() { return workerId; } public long getDatacenterId() { return datacenterId; } public long getTimestamp() { return System.currentTimeMillis(); } public SnowFlakeWorkId() { this.workerId = 0L; this.datacenterId = 0L; } public SnowFlakeWorkId(long workerId, long datacenterId, long sequence) { // 检查机房id和机器id是否超过31 不能小于0 if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException( String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException( String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; this.sequence = sequence; } // 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id public synchronized long nextId() { // 这儿就是获取当前时间戳,单位是毫秒 long timestamp = timeGen(); if (timestamp < lastTimestamp) { L.e("clock is moving backwards. Rejecting requests until %d." + lastTimestamp); throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } // 下面是说假设在同一个毫秒内,又发送了一个请求生成一个id // 这个时候就得把seqence序号给递增1,最多就是4096 if (lastTimestamp == timestamp) { // 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来, //这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围 sequence = (sequence + 1) & sequenceMask; //当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } // 这儿记录一下最近一次生成id的时间戳,单位是毫秒 lastTimestamp = timestamp; // 这儿就是最核心的二进制位运算操作,生成一个64bit的id // 先将当前时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后12 bit // 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型 return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } /** * 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID * * @param lastTimestamp * @return */ private long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } //获取当前时间戳 private long timeGen() { return System.currentTimeMillis(); } } ``` ## 153.悬浮TabLayout ``` <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:id="@+id/layout_top" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:gravity="center_vertical" android:orientation="horizontal" android:paddingLeft="8dp" android:paddingTop="8dp" android:paddingBottom="8dp" android:text="首页" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <TextView android:id="@+id/tv_location" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:layout_toStartOf="@+id/tv_msg" android:drawableLeft="@mipmap/ic_location_drakgray" android:drawableRight="@mipmap/ic_arrow_down_black" android:drawablePadding="4dp" android:ellipsize="end" android:gravity="center_vertical" android:lines="1" android:maxLength="3" android:onClick="@{clicklistener}" android:padding="8dp" android:src="@mipmap/ic_default_head_portrait_gray" android:text="当前位置" android:textColor="@color/tv_black" android:textSize="14sp" /> <TextView android:id="@+id/tv_search" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@drawable/round66_light_gray_bg" android:drawableLeft="@mipmap/ic_search_gray" android:drawablePadding="8dp" android:onClick="@{clicklistener}" android:padding="8dp" android:singleLine="true" android:text="快速搜寻用户、ID、分类" android:textColor="@color/tv_white_gray" android:textSize="12sp" /> </LinearLayout> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.youth.banner.Banner android:layout_width="match_parent" android:layout_height="160dp" android:background="@mipmap/ic_site_long" app:layout_scrollFlags="scroll" /> <com.google.android.material.tabs.TabLayout android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="scrollable"> <com.google.android.material.tabs.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tab1" /> <com.google.android.material.tabs.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tab2" /> <com.google.android.material.tabs.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tab3" /> </com.google.android.material.tabs.TabLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout> </LinearLayout> ``` ## 154.自定义水波纹 - Android 自定义view实现水波纹效果 [https://www.cnblogs.com/itcqx/p/5557273.html](https://www.cnblogs.com/itcqx/p/5557273.html) - Android自定义View-水波纹progressbar [https://www.jianshu.com/p/574eba73e4ec](https://www.jianshu.com/p/574eba73e4ec) ## 155.自定义水波纹中心扩散 - RippleView博客 [http://blog.csdn.net/zhuwentao2150/article/details/79452735](http://blog.csdn.net/zhuwentao2150/article/details/79452735) [https://github.com/zhuwentao2150/RippleView](https://github.com/zhuwentao2150/RippleView) - RippleView - 会闪烁添加圆的代码需要改为: ``` // 里面添加圆 if (mRipples.size() > 0 && mRipples.size() < mWidth/(2* DpPxUtils.dp2px(mDensity))) { // 控制第二个圆出来的间距 if (mRipples.get(mRipples.size() - 1).width > DensityUtil.dip2px(mContext, mDensity)) { mRipples.add(new Circle(0, 255)); } } ``` ## 156.自定义进度条 ``` <ProgressBar style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal" android:max="100" android:progress="30" android:id="@+id/progress_voice_time" android:progressDrawable="@drawable/progressbar_voice" android:layout_width="120dp" android:layout_height="5dp"/> ``` - 然后: ``` <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:gravity="center_vertical|fill_horizontal"> <shape android:shape="rectangle"> <!--android:tint="?attr/colorControlNormal">--> <size android:height="4dp" /> <solid android:color="#26FFFFFF" /> <corners android:radius="2dp" /> </shape> </item> <item android:id="@android:id/secondaryProgress" android:gravity="center_vertical|fill_horizontal"> <scale android:scaleWidth="100%"> <shape android:shape="rectangle"> <!--android:tint="?attr/colorControlActivated">--> <size android:height="4dp" /> <solid android:color="#FFFFFF" /> <corners android:radius="2dp" /> </shape> </scale> </item> <item android:id="@android:id/progress" android:gravity="center_vertical|fill_horizontal"> <scale android:scaleWidth="100%"> <shape android:shape="rectangle"> <!--android:tint="?attr/colorControlActivated">--> <size android:height="4dp" /> <solid android:color="#FFFFFF" /> <corners android:radius="2dp" /> </shape> </scale> </item> </layer-list> ``` ## 157.ProgressDialog弹框进度的使用 ``` ProgressDialog dialog; Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message msg) { int progress = msg.what; if (progress==0){ dialog = new ProgressDialog(BillMainActivity.this); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//转盘 dialog.setProgress(progress); dialog.show(); progress++; mHandler.sendEmptyMessageDelayed(progress,1000); }else if (progress>0&&progress<=20){ dialog.setProgress(progress); progress++; mHandler.sendEmptyMessageDelayed(progress,1000); }else { dialog.dismiss(); } return false; } }); ``` ## 158.Android自动适配 - 今日头条适配方案AndroidAutoSize ## 159.Android8.1系统崩溃,无法启动java.lang.ClassNotFoundException: com.android.id.impl.IdProviderImpl - 原因:使用了穿山甲的广告sdk,但由于使用了模拟器测试,判断为海外服务器导致初始化崩溃 - errCode==40026 errMsg==使用海外ip请求国内服务器导致,请确认使用的是国内ip请求广告。 - 解决:对穿山甲广告的初始化进行异常捕获 ## git merge后,后悔了如何回退 ## 160.Error inflating class android.webkit.WebView,只在9.0有错 [https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview](https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview) [https://www.cnblogs.com/princesong/p/7414880.html](https://www.cnblogs.com/princesong/p/7414880.html) ## 161.RecyclerView更全解析之 - 仿支付宝侧滑删除和拖动排序 - RecyclerView更全解析之 - 仿支付宝侧滑删除和拖动排序 [https://www.jianshu.com/p/9952477c8105](https://www.jianshu.com/p/9952477c8105) - Android侧滑-RecyclerView实现简单高效的侧滑菜单 [https://www.jianshu.com/p/26541fdc7d7f](https://www.jianshu.com/p/26541fdc7d7f) ## 162.神奇的bug 当外层Frament中的控件id和其里面嵌套的Fragment控件id相同,且动态的设置了他们的布局LayoutParams,且他们的布局父类不同:如外层是LinearLayout.LayoutParams,里层是ConstraintLayout.LayoutParams时,会报布局转换样式控制转换异常且他不会明说是那个Fragment的错。这个时候最好就要查查你自己的控件id是否写了相同的。 ## 163.Android解决RecyclerView中的item显示不全方案 [https://segmentfault.com/a/1190000019841905?utm_source=tag-newest](https://segmentfault.com/a/1190000019841905?utm_source=tag-newest) ## 164.Android解决EditText 获取焦点 并自动弹出输入法 ``` db.etCode.setFocusable(true); db.etCode.setFocusableInTouchMode(true); db.etCode.setInputType(EditorInfo.TYPE_CLASS_NUMBER); db.etCode.requestFocus(); new Handler().postDelayed(() -> { InputMethodManager inputManager =(InputMethodManager) db.etCode.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInput(db.etCode, 0); },300); ``` ## 165.Android监听键盘的显示与隐藏 - 首先定义一个监听器 ``` public class SoftKeyBoardListener { private View rootView;//activity的根视图 int rootViewVisibleHeight;//纪录根视图的显示高度 private OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener; public SoftKeyBoardListener(Activity activity) { //获取activity的根视图 rootView = activity.getWindow().getDecorView(); Rect r = new Rect(); rootView.getWindowVisibleDisplayFrame(r); rootViewVisibleHeight = r.height(); //监听视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变 rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //获取当前根视图在屏幕上显示的大小 Rect r = new Rect(); rootView.getWindowVisibleDisplayFrame(r); int visibleHeight = r.height(); System.out.println(""+visibleHeight); if (rootViewVisibleHeight == 0) { rootViewVisibleHeight = visibleHeight; return; } //根视图显示高度没有变化,可以看作软键盘显示/隐藏状态没有改变 if (rootViewVisibleHeight == visibleHeight) { return; } //根视图显示高度变小超过200,可以看作软键盘显示了 if (rootViewVisibleHeight - visibleHeight > 200) { if (onSoftKeyBoardChangeListener != null) { onSoftKeyBoardChangeListener.keyBoardShow(rootViewVisibleHeight - visibleHeight); } rootViewVisibleHeight = visibleHeight; return; } //根视图显示高度变大超过200,可以看作软键盘隐藏了 if (visibleHeight - rootViewVisibleHeight > 200) { if (onSoftKeyBoardChangeListener != null) { onSoftKeyBoardChangeListener.keyBoardHide(visibleHeight - rootViewVisibleHeight); } rootViewVisibleHeight = visibleHeight; return; } } }); } private void setOnSoftKeyBoardChangeListener(OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) { this.onSoftKeyBoardChangeListener = onSoftKeyBoardChangeListener; } public interface OnSoftKeyBoardChangeListener { void keyBoardShow(int height); void keyBoardHide(int height); } public static void setListener(Activity activity, OnSoftKeyBoardChangeListener onSoftKeyBoardChangeListener) { SoftKeyBoardListener softKeyBoardListener = new SoftKeyBoardListener(activity); softKeyBoardListener.setOnSoftKeyBoardChangeListener(onSoftKeyBoardChangeListener); } } ``` - 然后使用: ``` SoftKeyBoardListener.setListener(this, new SoftKeyBoardListener.OnSoftKeyBoardChangeListener() { @Override public void keyBoardShow(int height) { L.e("键盘显示 高度" + height); } @Override public void keyBoardHide(int height) { L.e("键盘隐藏 高度" + height); } }); ``` - 参考:[https://www.zhihu.com/question/44269936](https://www.zhihu.com/question/44269936) ## 166.Android js交互 与 Html启动App [https://www.jianshu.com/p/447659afb619](https://www.jianshu.com/p/447659afb619) ## 167.Android项目加载后图标不显示解决方案 [https://blog.csdn.net/iteye_568/article/details/82565572](https://blog.csdn.net/iteye_568/article/details/82565572) ## 168.仿探探卡片滑动选择 - 仿探探卡片滑动选择 [https://www.jianshu.com/p/158fd88b769c](https://www.jianshu.com/p/158fd88b769c) - 玩转仿探探卡片式滑动效果 [https://www.jianshu.com/p/b873b717d1ea](https://www.jianshu.com/p/b873b717d1ea) ## 169.监听View加载完成 ``` view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //加载完成后,做该做的事 } }); ``` ## 170.您的应用签名算法采用“SHA256withRSA”,在部分4.2以下安卓版本的手机上不能安装的解决方案 [https://blog.csdn.net/wonghoman/article/details/52923640?utm_source=blogxgwz6](https://blog.csdn.net/wonghoman/article/details/52923640?utm_source=blogxgwz6) ## 171.Glide加载图片会闪烁,我是两个综合起来使用不再闪烁 - Android使用Glide加载图片刷新时会闪烁 [https://www.jianshu.com/p/d220dfa9ff1a](https://www.jianshu.com/p/d220dfa9ff1a) - 解决Glide加载图片闪烁的问题(感觉加载了两遍 !!!) [https://www.pianshen.com/article/5131541975/](https://www.pianshen.com/article/5131541975/) - RecyclerView用notifyDataSetChanged()刷新时候的图片闪烁 [https://blog.csdn.net/zhoulesin_dev/article/details/80183715](https://blog.csdn.net/zhoulesin_dev/article/details/80183715) ## 172.Android 区间选择控件收集 [https://blog.csdn.net/FlyPig_Vip/article/details/104553111](https://blog.csdn.net/FlyPig_Vip/article/details/104553111) ## 173.image not loaded错误解决方法 - 图片资源包含了.jpg的图片,直接把jpg改未png即可 ## 174.AndroidStudio所有的xml错乱,与实际内容不服, 代码错乱、文件错乱等 缓存清除解决方案 [https://blog.csdn.net/qq_33456552/article/details/88980646](https://blog.csdn.net/qq_33456552/article/details/88980646) ## 175.微信支付宝自动续费 [https://www.jianshu.com/p/5a3a07045706](https://www.jianshu.com/p/5a3a07045706) ## 176.这么用GridLayoutManager,你可能还真没尝试过 [https://www.jianshu.com/p/60aa2fc17870](https://www.jianshu.com/p/60aa2fc17870) ## 177.Android日历控件相关 - Android精美日历控件CalendarView自定义使用完全解析 [https://blog.csdn.net/huanghaibin_dev/article/details/79040147](https://blog.csdn.net/huanghaibin_dev/article/details/79040147) ## 178.Android拨打电话的两种实现方法 [https://blog.csdn.net/s13383754499/article/details/80609278](https://blog.csdn.net/s13383754499/article/details/80609278) ## 179.监听AppBarLayout滑动距离比例,变色 ``` public class AppBarUtils { public static void setColorChange(AppBarLayout appBar,View viewTarget,String startColor,String endColor){ appBar.getChildAt(0).getViewTreeObserver().addOnGlobalLayoutListener(() -> { //获取AppBarLayout最大滑动距离 final int scrollRange = appBar.getTotalScrollRange(); appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { //获得滑动具体,计算比例 int scrollDistance = Math.abs(verticalOffset); float scrollPercentage = (float) scrollDistance/scrollRange; L.e("scrollPercentage=="+scrollPercentage); ArgbEvaluator argbEvaluator = new ArgbEvaluator();//渐变色计算类 int currentLastColor = (int) (argbEvaluator.evaluate(scrollPercentage, Color.parseColor(startColor), Color.parseColor(endColor))); viewTarget.setBackgroundColor(currentLastColor); }); }); } public static void setColorGrayWhite(AppBarLayout appBar,View viewTarget){ setColorChange(appBar,viewTarget,"#f5f5f5","#ffffff"); } } ``` - 同时,如果需要appbar自动折叠和展开 ``` //自动展开: mAppBarLayout.setExpanded(true); //自动折叠: mAppBarLayout.setExpanded(false); ``` ## 180.监听应用回到后台和前台: - 在MyApplication中使用如下方法: ``` private int liveActivityCount;//当前应用活动的页面个数 private void isBackEvent() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} @Override public void onActivityStarted(@NonNull Activity activity) { liveActivityCount++; L.e("应用已回到前台"); } @Override public void onActivityResumed(@NonNull Activity activity) {} @Override public void onActivityPaused(@NonNull Activity activity) {} @Override public void onActivityStopped(@NonNull Activity activity) { liveActivityCount--; if (liveActivityCount==0) L.e("应用已回到后台"); } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} @Override public void onActivityDestroyed(@NonNull Activity activity) {} }); } ``` ## 181.这一次,彻底了解 Gradle 吧! - 这一次,彻底了解 Gradle 吧! [https://mp.weixin.qq.com/s/UlnHuM2-Dqad3MXE77xJyg](https://mp.weixin.qq.com/s/UlnHuM2-Dqad3MXE77xJyg) - Gradle 爬坑指南 -- 导论 [https://juejin.im/post/6882178101191639053](https://juejin.im/post/6882178101191639053) ## 182.Android Jetpack - 还不知道Android Jetpack是什么?你就out了 [https://zhuanlan.zhihu.com/p/92603960](https://zhuanlan.zhihu.com/p/92603960) - 学习Android Jetpack? 实战和教程这里全都有! [https://juejin.cn/post/6844903889574051848](https://juejin.cn/post/6844903889574051848) ## 183.github无法查看图片 - 修改hosts映射 ``` 140.82.113.3 github.com 140.82.114.20 gist.github.com 151.101.184.133 assets-cdn.github.com 151.101.184.133 raw.githubusercontent.com 151.101.184.133 gist.githubusercontent.com 151.101.184.133 cloud.githubusercontent.com 151.101.184.133 camo.githubusercontent.com 151.101.184.133 avatars0.githubusercontent.com 199.232.68.133 avatars0.githubusercontent.com 199.232.28.133 avatars1.githubusercontent.com 151.101.184.133 avatars1.githubusercontent.com 151.101.184.133 avatars2.githubusercontent.com 199.232.28.133 avatars2.githubusercontent.com 151.101.184.133 avatars3.githubusercontent.com 199.232.68.133 avatars3.githubusercontent.com 151.101.184.133 avatars4.githubusercontent.com 199.232.68.133 avatars4.githubusercontent.com 151.101.184.133 avatars5.githubusercontent.com 199.232.68.133 avatars5.githubusercontent.com 151.101.184.133 avatars6.githubusercontent.com 199.232.68.133 avatars6.githubusercontent.com 151.101.184.133 avatars7.githubusercontent.com 199.232.68.133 avatars7.githubusercontent.com 151.101.184.133 avatars8.githubusercontent.com 199.232.68.133 avatars8.githubusercontent.com ``` ## 184.EditText光标移动到文本框末尾 ``` et.setSelection(et.getText().length()); ``` ## 185.排除aar/jar中冗余或者冲突包、类 - 例如和com.google.android:support-v4冲突 [https://www.jianshu.com/p/9d639c082cfc?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation](https://www.jianshu.com/p/9d639c082cfc?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) ``` implementation('org.eclipse.paho:org.eclipse.paho.android.service:1.0.2') { exclude(group: 'com.google.android', module: 'support-v4') } ``` - AndroidStudio-依赖jar包版本不一致导致冲突 [https://www.jianshu.com/p/7d30def081e9](https://www.jianshu.com/p/7d30def081e9) ## 186.内存泄漏可视化检查 - leakcanary-Github [https://github.com/square/leakcanary](https://github.com/square/leakcanary) - leakcanary文档 [https://square.github.io/leakcanary/](https://square.github.io/leakcanary/) ## 187.AndroidStudio上登陆github账号异常 invalid authentication data.404 Not Found -Not Found,但确定了我的账号密码正确,最好的方法就是用Token来登录,参考:关于Android Studio绑定github登陆不上的问题 [https://blog.csdn.net/jideqiang123/article/details/110739331](https://blog.csdn.net/jideqiang123/article/details/110739331) ## 188. 设置Activity为singleTask之后,如何在getIntent().getStringExtra()获取数据 ## 189.无限循环卡片?我选择自定义LayoutManager - 无限循环卡片?我选择自定义LayoutManager [https://www.cnblogs.com/yaya25001/p/5644325.html](https://www.cnblogs.com/yaya25001/p/5644325.html) [https://mp.weixin.qq.com/s/DpV6iD4rBSgQQHsPNDJe6w](https://mp.weixin.qq.com/s/DpV6iD4rBSgQQHsPNDJe6w) ## 190.Android官方架构组件Navigation:大巧不工的Fragment管理框架 [https://juejin.cn/post/6844903773366648840](https://juejin.cn/post/6844903773366648840) ## 191. 解决升级到 Android Studio 4.1 后一些插件(GsonFormat、LayoutCreator ...)无法使用 ## 192. JetPark组件 - “终于懂了“系列:Jetpack AAC完整解析(一)Lifecycle 完全掌握! [https://juejin.cn/post/6893870636733890574#heading-4](https://juejin.cn/post/6893870636733890574#heading-4) [https://nowtime.cc/software/1167.html](https://nowtime.cc/software/1167.html) Lifecycle与Http融合- [Retrofit结合Lifecycle, 将Http生命周期管理到极致]wulai(https://www.jianshu.com/p/07fe489a53f2#comments) ## 193.git忽略文件 [https://github.com/github/gitignore/blob/master/Android.gitignore](https://github.com/github/gitignore/blob/master/Android.gitignore) ## 194. 不会Kotlin,你好意思说你是Android开发者 - Kotlin文档,Google官方文档,中文博客,练习网站 [https://developer.android.google.cn/kotlin/first](https://developer.android.google.cn/kotlin/first) [http://www.kotlincn.net/docs/reference/](http://www.kotlincn.net/docs/reference/) ## 195.Gradle系列之Groovy - Gradle系列之Groovy基础篇 [https://juejin.cn/post/6844903876177428494](https://juejin.cn/post/6844903876177428494) ## 196.Dagger替代者Koin,前提你用的Kotlin开发 - Koin--适用于Kotlin的超好用依赖注入框架,Dagger替代者 [https://www.jianshu.com/p/bccb93a78cee?utm_campaign=shakespeare](https://www.jianshu.com/p/bccb93a78cee?utm_campaign=shakespeare) ## 197.数据库 - LitePal使用-对象操作好用 [https://blog.csdn.net/guolin_blog/article/details/82714414](https://blog.csdn.net/guolin_blog/article/details/82714414) - Android架构组件JetPack之Room(三) [https://www.jianshu.com/p/d1b37b1afb5c](https://www.jianshu.com/p/d1b37b1afb5c) - GreenDao最全面的使用详解 [https://blog.csdn.net/qq_44375666/article/details/108387207](https://blog.csdn.net/qq_44375666/article/details/108387207) ## 198.数据存储MMKV - SharedPreferences替换:MMKV集成与原理 [https://juejin.cn/post/6930168094983716877/](https://juejin.cn/post/6930168094983716877/) ## 199.Android相关工具镜像下载地址 - AndroidDevTools [https://www.androiddevtools.cn/index.html](https://www.androiddevtools.cn/index.html) ## 200.大前端 | 名称 | 语言 | 团队简介 | | --------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | flutter | Dart | 自己的API渲染机制 `107k` | | 微信小程序 | javascropt,wxml | 样式weui-wxss `13.2k`,界面weui `25.2k`,组件wepy `20.8k` [https://github.com/Tencent/weui-wxss](https://github.com/Tencent/weui-wxss) | | uni-app | vue.js | 跨10端,`27.2k` | | taro | JavaScript,TypeScript | `27.5k` | | jetpack Compose | kotlin | google推出的布局和逻辑统一一种语言写法的高效原生安卓app,有希望跨端.相关文章:相似度99%?Jetpack Compose 与 React Hooks API对比[https://juejin.cn/post/6930436484822794247](https://juejin.cn/post/6930436484822794247) | | 其他 | 前端语言 | 大团队 | - 总结: 不知不觉,终于完成了200项的收集,说实话,最先这篇文章,不能叫文章,只能叫收藏列表吧。基本就是我平时遇到的问题和觉得好用的工具的收藏记录。慢慢的居然记录了200项了,如果再次遇到相同的问题,打开这篇文章搜索一下就能快速定位错误。 但发现了这种记录有一些问题:1杂乱无章,无分类 2.内容重复(删除了一些重复的,但有的依然重复)3.过时(有的控件已经过时,不推荐使用了)4.格式混乱(有的用的表格,有的用的排序,有的用的代码块)5.链接失效或不必要(有的链接已经失效了404,有的链接就是简单几行代码,没必要跳转) 时代在变迁,代码在变幻,2019年IO大会确定了Kotlin作为Android开发第一语言,但用了这么久的java来开发Android,确实还是很排斥的。但当我买了一本书,其中通过实例穿插讲解,我才知道了Kotlin原来如此优秀,原来它也发展了很多年,是最近才火的,以后的发展将会更好。于是我开始反思,决定改变自己。 改变:首先写个项目模板,用Kotlin写了TempAndroid,然后再写一篇专门记录Kotlin相关问题的收藏列表Kotlin学习笔记 [https://github.com/xiaoshubin/TempAndroid](https://github.com/xiaoshubin/TempAndroid) [https://www.jianshu.com/p/b7be40fab8de](https://www.jianshu.com/p/b7be40fab8de) END 最后修改:2026 年 07 月 06 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏 下一篇 发表评论 取消回复 使用cookie技术保留您的个人信息以便您下次快速评论,继续评论表示您已同意该条款 评论 * 私密评论 名称 * 🎲 邮箱 * 地址 发表评论 提交中...