Android项目200项

原文链接链接: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有妙招

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

6.设置去掉所有的页面标题栏

在AppTheme中配置

 <item name="windowNoTitle">true</item>

7.快速构建Builder插件

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

recyclerView.setHasFixedSize(true);
recyclerView.setNestedScrollingEnabled(false);

垂直滑动问题:
https://segmentfault.com/a/1190000011553735
recyclerview嵌套在NestedScrollView里,一次性加载出全部数据问题
https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues/1954
Android SwipeRefreshLayout和RecyclerView嵌套时 下拉刷新冲突的解决办法
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

RecyclerView抢先获取焦点问题

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行显示省略号
设置可垂直滚动 xml中android:scrollbars="vertical"且代码中tv.setMovementMethod(ScrollingMovementMethod.getInstance());
设置字体后,显示不全 斜体显示不全斜体中文显示不全
弹窗导致TextView停止滚动 解决因为弹窗导致TextView停止滚动的问题
多行且需要显示省略号的警告 logcat 总是报: W/StaticLayout: maxLineHeight should not be -1. maxLines:1 lineCount:1
加上阴影效果,字体阴影 Android TextView加上阴影效果
  • 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位。" />
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网络回调结果

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

webview加载html图片过大左右滑动的解决/webview加载图片自适应大小

16.WebView出现net::ERR_UNKNOWN_URL_SCHEME错误

https://www.jianshu.com/p/119823e5cfb5

17.Glide显示图片为上面圆角,下面直角

让Glide输出指定位置的圆角图片 2018年,部分方法为Glide4.0以前的,所以无法使用,但方法值得借鉴
Glide 加载部分圆角图片2019年,新的方法,且行为更合理
圆角不圆:有可能是因为图片高度或宽度过大,导致部分圆角不圆
Glide ViewTarget及SimpleTarget加载问题:
Android 使用Glide下载图片的几种方式(老版本)
Glide下载图片并保存到本地

因为我的心](https://www.jianshu.com/u/a509cdb1b51e)关注 赞赏支持

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保存成图片
两个Bitmap合并为一个
Android:将一个Activity、某块布局转换成图片

注意:view转bitmap时,视图view的位置可能发生改变。这个原因多半时因为:

19.使用xml绘制虚线好后,4.0以上需要使用 android:layerType="software"属性,不然虚线会变为实线

https://blog.csdn.net/Small_Lee/article/details/52153557

20.微信分享到朋友圈,调用系统分享,需要对Uri单独处理一下

https://blog.csdn.net/qq_34900897/article/details/85320646

21.support版本冲突的解决办法

https://blog.csdn.net/yuzhiqiang_1993/article/details/78214812

22.TabLayout+ViewPager垂直方向联动,用于分类页面

https://www.jianshu.com/p/9266e58cc4f5

23.精确计算RecyclerView滑动高度

SlideRecyclerView

24.adb无线调试

https://jingyan.baidu.com/article/066074d610f4f3c3c21cb0ab.html

25.打包名称自定义

https://www.cnblogs.com/bluestorm/p/6228085.html
https://blog.csdn.net/weixin_33709364/article/details/87160660
打包方法过时警告: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
如何判断你的apk是否已经签名:
https://blog.csdn.net/qq_21376985/article/details/53337977

27.混淆

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

29.Android:通过Glide保存图片到本地,并同步到相册

30.Android 调用系统分享(不使用第三方),指定QQ、微信等

https://blog.csdn.net/u010356768/article/details/78246691
qq空间限制:
https://blog.csdn.net/weixin_41239127/article/details/78743421

Android系统分享使用Intent分享文本到QQ和文本+图片到微信朋友圈及纯图到微信

31.安卓从imageview中获得bitmap的方法

https://blog.csdn.net/yj1499945/article/details/47079621

32.怎样获取到Android控件的高度,通过监听的方式

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

33.解决android.permission.WRITE_APN_SETTINGS

https://blog.csdn.net/qq_36437339/article/details/81015715

34.新手引导库

GuideView

在Fragment中由于控件位置绘制流程和生命周期的关系,需要监听控件View宽高,有值后才进行引导层的绘制,同时用Handler进行一定的延迟绘制,保证高亮区域的定位精确度

35.Java中对对象进行排序

https://blog.csdn.net/qq_37937537/article/details/80445731

通过Comparable

36.监控当前APP是否回到前台

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

39.ViewPager2,支持横竖布局,支持0预加载布局

https://juejin.im/post/5cda3964f265da035d0c9d8f

40.RadioGroup.onCheckedChanged() 会调用多次

https://blog.csdn.net/qq_32452623/article/details/80474487

41.RecycleView notifyItemChanged() 局部刷新报错

原因一:https://blog.csdn.net/qq_29598727/article/details/80515725
原因二:还有一种原因就是RecycleView对应的mAdapter添加了头部,那么添加了几个头部就需要在刷新的位置加几。

  • 我以为我的问题是原因一导致的,没想到使用原因二的解决办法后,去掉了原因一的方法依然没有问题,说明就是原因二导致的。

42.Java多线程

Java多线程系列目录(共43篇)

43.ArrayList进行排序

24版本以前的老方法:
Collections.sort(arrays);
新方法
arrays.sort();

44.对时间的转换,对比,添加,格式输出

joda-time

45. tools:replace的使用

tools:replace=""有时候需要替换多个项,使用逗号分割
tools:replace="android:allowBackup,android:appComponentFactory"

46. 解决 导入三方时出现: appComponentFactory 错误

https://blog.csdn.net/qq_34224268/article/details/83861897

47. 滚轮时间选择器

可以采用 XPopupWheelPicker
组合的方式来生成一个时间选择器如: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

51.ArgbEvaluator一个计算颜色渐变值的类

https://blog.csdn.net/u013581141/article/details/68063469
使用事例:自定义CoordinatorLayout.Behavior颜色渐变的TitleBar

52.交互效果咋了系列:

自定义ViewGroup第十三式之移花接木

53.数据绑定DataBind

android {
    …
    dataBinding {
        enabled = true
    }
}

告别findView和ButterKnife
Android开发教程 - 使用Data Binding(七)使用BindingAdapter简化图片加载

54.错误检查命令

gradlew processDebugManifest --stacktrace

55.new Handler的警告说明和解决办法@SuppressLint(“HandlerLeak”)

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

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

61.升级AndroidX后出现缺失AppBarLayout$ScrollingViewBehavior

https://www.jianshu.com/p/6b8104787617

62.APP启动时间,性能优化,启动页白屏

https://www.jianshu.com/p/75b0b128c470

63.AndroidStudio安装失败

image.png

方法一:clean项目
方法二:重启大法:重启AndroidStudio,重启手机,重启电脑,重启...
方法三:检查是否只开启了开发者模式和USB调试,却没有开启了USB安装


image.png

64.adb命令

https://www.jianshu.com/p/56fd03f1aaae

65.ConstraintLayout基础 及动态控件(动画效果)

https://www.jianshu.com/p/7888cde8292f

66.获取videoView第一针视频报错:

https://blog.csdn.net/guohesheng/article/details/80236799

67.Math函数相关计算

//角度换算为对应数值
double skewRot = Math.toRadians(30);

68. banner库一张图片无效和第一次进入没有轮播的时候点击第一张无效

一张图片的时候需要设置banneronClick事件,多张图第一次还未开始轮播点击无效,需要先设置点击事件,再调用bannerstart方法

69. <activity-alias> 标签的使用

<activity-alias> 标签的使用

70.Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK

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://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

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

76.简单实现ImageView宽度填满屏幕,高度自适应的两种方式

https://www.jianshu.com/p/c9424615e99d

77.RecyclerView瀑布流问题

https://www.jianshu.com/p/4e142909b824
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/

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

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

83.Android获取外网IP

参考: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://www.jianshu.com/p/f29ad4beef59

86.'gradle' 不是内部或外部命令,也不是可运行的程序 或批处理文件

https://blog.csdn.net/u014743890/article/details/84316176

87.使用ImmersionBar时,加载出现Dialog的显示会导致导航栏出现不消失:

88.Dagger2-Android不支持泛型Activity的注入

https://blog.csdn.net/ybf326/article/details/82931587

89.删除git配置

删除

90.把本地项目复制到github

分享

91.AndroidStudio设置github账号

image.png

92.邀请码识别来源

https://www.openinstall.io/

93.Android 自动抓取网站图标实现分享样式的定制

https://blog.csdn.net/dnsliu/article/details/57122535

94.Fragment实现懒加载

https://blog.csdn.net/vic6329063/article/details/82838430

95.版权登记

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

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

99.悬浮球:

适合单页添加一个悬浮球:无需权限的悬浮球,快收到你碗里去吧!
一个极好的全局悬浮窗框架:EasyFloat

100.Android视频播放,选择,压缩,上传

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://yanzhenjie.com/AndPermission/cn/
不再维护,改用
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

104.关于Android弹出软键盘“顶起”View的问题

Activity - > android:windowSoftInputMode="adjustPan|stateHidden"
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

109'Android打开系统拍照&相册获取头像

参考Android打开系统拍照&相册获取头像

使用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
android 7.0+ FileProvider 访问隐私文件 相册、相机、安装应用的适配

111. view 可见性 监听探究

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

        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定位实现
Andriod 手机定位 解决location为null的问题
Android地图开发中的地理编码与地理反编码
经纬度查询:https://www.juhe.cn/cellmap/lat
Android之GPS定位类 LocationManager、LocationListener、GpsStatus.Listener、Location详解

总结,使用:
(方式一)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

116.Android中给View设置阴影的三种方式

https://blog.csdn.net/wang29169/article/details/84206379
关于CardView参考:使用CardView实现卡片式设计
或者第三方的控件 shadow-layout

117.Android去掉阻尼效果

只需要在控件中添加如下属性即可:android:overScrollMode="never"

118.Android版本升级坑

https://www.jianshu.com/p/123c12218b7a

119.startActivityForResult的用法,以及intent传递图片

startActivityForResult的用法
fragment中的startActivityForResult无效:
谈谈Fragment中的onActivityResult

120.第三方地图接入相关

高德地图接入笔记

121.34个省份,可以用死数据

        private String[] provinceList =new String[]{"北京市", "天津市", "河北省", "山西省", "内蒙古自治区", "辽宁省", "吉林省","黑龙江省", "上海市", "江苏省", "浙江省", "安徽省", "福建省", "江西省", "山东省","河南省", "湖北省", "湖南省", "广东省", "广西壮族自治区", "海南省", "重庆市","四川省", "贵州省", "云南省", "西藏自治区", "陕西省", "甘肃省", "青海省", "宁夏回族自治区", "新疆维吾尔自治区", "台灣", "香港特别行>政区", "澳门特别行政区"};

122.Android数据库

注释类型数据库:greenDAO
GreenDAO 完美解决数据库升级数据丢失问题
关系型数据库 LitePal
数据库调试 Glance
数据库可视化调试工具Glance

123.3.6.1AndroidStudio开启离线编译

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

126.Android开发之SVG的使用

https://www.jianshu.com/p/845ac47cb1f1

127.Android移除含有动画的View

今天遇到removeAllViews无效的问题,原来需要结束里面子控件的动画后再移除:

layoutParent.getChildAt(i).clearAnimation();
//然后
layoutParent.removeAllViews();

128.Android Studio项目中文件错乱问题

Android Studio项目中文件错乱问题
AndroidStudio 代码报错(引入红色),但可正常运行解决方法(4种方式)

129.Android代码中设置颜色选择器

 btn.setTextColor(getResources().getColorStateList(R.color.tab_black_brown_selector));

130.【Android】WebView的input上传照片的兼容问题

https://juejin.im/post/5a322cdef265da43176a2913

131.系统选择器SwitchCompat

support.v7.widget.SwitchCompat(带文字的开关)
Android SwitchCompat开关按钮自定义颜色和样式

132Retrofit学习之文件和参数上传,上传头像

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

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>
GIF.gif

注意:如果要设置开关的文字

            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.贝塞尔曲线

必须要理解掌握的贝塞尔曲线(原创)

140.APP上架需要准备的材料清单(上架规范和流程)

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.屏幕适配,字体适配,图标适配

屏幕适配
今日头条适配方案AndroidAutoSize
字体大小适配
图标适配

144.获取签名工具

https://open.weixin.qq.com/cgi-bin/readtemplate?t=resource/app_download_android_tmpl&lang=zh_CN

145.Android Glide4.0+图片加载进度监听

Android Glide4.0+图片加载进度监听

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使用 及 滑动时加载图片优化方案
优化大量图片加载recyclerview列表的滑动效果

148.其实很多图片不是必须的,用xml的bitmap来创建不同颜色的图片

https://www.jianshu.com/p/249b10f5c4b7

149.Android Studio 安装APK在真机安装时报 Installation failed due to: 'null' 解决

https://www.jianshu.com/p/31edef558c99
除了上面说的,我还遇到一种情况就是使用了RenderScript,去掉RenderScript并删除对应的rs文件即可。

150.android之Animation自定义动画轨迹

CSDN:https://blog.csdn.net/kaikevin01/article/details/78871197
简书: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实现水波纹效果
Android自定义View-水波纹progressbar

155.自定义水波纹中心扩散

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请求广告。
解决:对穿山甲广告的初始化进行异常捕获

159.git merge后,后悔了如何回退

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

161.RecyclerView更全解析之 - 仿支付宝侧滑删除和拖动排序

RecyclerView更全解析之 - 仿支付宝侧滑删除和拖动排序
Android侧滑-RecyclerView实现简单高效的侧滑菜单

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

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

166.Android js交互 与 Html启动App

https://www.jianshu.com/p/447659afb619

167.Android项目加载后图标不显示解决方案

https://blog.csdn.net/iteye_568/article/details/82565572

168.仿探探卡片滑动选择

仿探探卡片滑动选择
玩转仿探探卡片式滑动效果

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

171.Glide加载图片会闪烁,我是两个综合起来使用不再闪烁

Android使用Glide加载图片刷新时会闪烁
解决Glide加载图片闪烁的问题(感觉加载了两遍 !!!)
RecyclerView用notifyDataSetChanged()刷新时候的图片闪烁

172.Android 区间选择控件收集

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

175.微信支付宝自动续费

https://www.jianshu.com/p/5a3a07045706

176.这么用GridLayoutManager,你可能还真没尝试过

https://www.jianshu.com/p/60aa2fc17870

177.Android日历控件相关

Android精美日历控件CalendarView自定义使用完全解析

178.Android拨打电话的两种实现方法

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 吧!
Gradle 爬坑指南 -- 导论

182.Android Jetpack

还不知道Android Jetpack是什么?你就out了
学习Android Jetpack? 实战和教程这里全都有!

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冲突

implementation('org.eclipse.paho:org.eclipse.paho.android.service:1.0.2') {
        exclude(group: 'com.google.android', module: 'support-v4')
    }

排除aar/jar中冗余或者冲突包、类
AndroidStudio-依赖jar包版本不一致导致冲突

186.内存泄漏可视化检查

leakcanary-Github
leakcanary文档

187.AndroidStudio上登陆github账号异常

invalid authentication data.404 Not Found -Not Found,但确定了我的账号密码正确,最好的方法就是用Token来登录,参考:关于Android Studio绑定github登陆不上的问题

188. 设置Activity为singleTask之后,如何在getIntent().getStringExtra()获取数据

189.无限循环卡片?我选择自定义LayoutManager

无限循环卡片?我选择自定义LayoutManager

190.Android官方架构组件Navigation:大巧不工的Fragment管理框架

https://juejin.cn/post/6844903773366648840

191. 解决升级到 Android Studio 4.1 后一些插件(GsonFormat、LayoutCreator ...)无法使用

192. JetPark组件

“终于懂了“系列:Jetpack AAC完整解析(一)Lifecycle 完全掌握!
Lifecycle与Http融合- [Retrofit结合Lifecycle, 将Http生命周期管理到极致]wulai(https://www.jianshu.com/p/07fe489a53f2#comments)

193.git忽略文件

https://github.com/github/gitignore/blob/master/Android.gitignore

194. 不会Kotlin,你好意思说你是Android开发者

Kotlin文档Google官方文档中文博客,练习网站

195.Gradle系列之Groovy

Gradle系列之Groovy基础篇

196.Dagger替代者Koin,前提你用的Kotlin开发

Koin--适用于Kotlin的超好用依赖注入框架,Dagger替代者

197.数据库

LitePal使用-对象操作好用
Android架构组件JetPack之Room(三)
GreenDao最全面的使用详解

198.数据存储MMKV

SharedPreferences替换:MMKV集成与原理

199.Android相关工具镜像下载地址

AndroidDevTools

200.大前端

名称 语言 团队简介
flutter Dart 自己的API渲染机制107k
微信小程序 javascropt,wxml 样式weui-wxss13.2k,界面weui25.2k,组件wepy20.8k
uni-app vue.js 跨10端,27.2k
taro JavaScript,TypeScript 27.5k
jetpack Compose kotlin google推出的布局和逻辑统一一种语言写法的高效原生安卓app,有希望跨端.相关文章:相似度99%?Jetpack Compose 与 React Hooks API对比
其他 前端语言 大团队

总结:

  • 不知不觉,终于完成了200项的收集,说实话,最先这篇文章,不能叫文章,只能叫收藏列表吧。基本就是我平时遇到的问题和觉得好用的工具的收藏记录。慢慢的居然记录了200项了,如果再次遇到相同的问题,打开这篇文章搜索一下就能快速定位错误。
  • 但发现了这种记录有一些问题:1杂乱无章,无分类 2.内容重复(删除了一些重复的,但有的依然重复)3.过时(有的控件已经过时,不推荐使用了)4.格式混乱(有的用的表格,有的用的排序,有的用的代码块)5.链接失效或不必要(有的链接已经失效了404,有的链接就是简单几行代码,没必要跳转)
  • 时代在变迁,代码在变幻,2019年IO大会确定了Kotlin作为Android开发第一语言,但用了这么久的java来开发Android,确实还是很排斥的。但当我买了一本书,其中通过实例穿插讲解,我才知道了Kotlin原来如此优秀,原来它也发展了很多年,是最近才火的,以后的发展将会更好。于是我开始反思,决定改变自己。
  • 改变:首先写个项目模板,用Kotlin写了TempAndroid,然后再写一篇专门记录Kotlin相关问题的收藏列表Kotlin学习笔记

推荐阅读更多精彩内容