Android NFC标签 开发深度解析 触碰的艺术(笔者使用的,强烈推荐!!!)
分类:android
【转】Android编程实现自定义ProgressBar样式示例(背景色及一级、二级进度条颜色)
转载自:Android编程实现自定义ProgressBar样式示例(背景色及一级、二级进度条颜色)
本文实例讲述了Android编程实现自定义ProgressBar样式。分享给大家供大家参考,具体如下:
效果图如下,本例中设置了第一级进度条和第二级进度条。
样式资源:progressbar_bg.xml,放在drawable文件夹下:
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <!--3个层的顺序即为显示时的叠加顺序--> <!--背景色--> <item android:id="@android:id/background"> <shape> <corners android:radius="5dip" /> <solid android:color="#CCCCCC" /> </shape> </item> <!--二级进度条的颜色--> <item android:id="@android:id/secondaryProgress"> <clip> <shape> <corners android:radius="5dip" /> <solid android:color="#88F56100" /> </shape> </clip> </item> <!--一级进度条的颜色,也可以直接替换成图片--> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dip" /> <solid android:color="#F56100" /> </shape> </clip> </item> </layer-list>
布局代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ProgressBar android:id="@+id/progress_bar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="100dp" android:minHeight="20dp" android:padding="10dp" android:progressDrawable="@drawable/progressbar_bg" /> </LinearLayout>
【转】Toast 在 Android 7.1 崩溃排查及修复
转载自:Toast 在 Android 7.1 崩溃排查及修复
崩溃详情
尝试复现
- 通过崩溃信息从网上找到的一些论述,发现这个问题是因为主线程被阻塞了,而 Toast 没有及时销毁导致的,那么接下来让我们对它进行复现
- 为什么出现这个问题,是因为 Toast 的显示是通过 Handler.sendMessage,所以这个操作是异步的,而 Thread.sleep 会阻塞主线程,从而导致 Handler.handleMessage 在接收到消息的时候 WindowToken 已经失效了
- 经过实际的测试:如果是短吐司,sleep 2000 毫秒的时候还是会抛出异常,sleep 1500 毫秒则不会发生异常;如果是长吐司,sleep 3500 毫秒的时候也是会抛出异常,sleep 3000 毫秒的时候就不会发生异常
- 由此可见,WindowToken 失效的时间是跟 Toast 的显示时长有关,如果是短吐司,那么 WindowToken 有效时长只能在 2 秒以内;如果是长吐司,那么 WindowToken 的有效时长只能在 3.5 秒以内
- 然后再通过 WindowManager.addView 的时候,它会对 WindowToken 例行检查,如果是失效状态则会抛出异常给上层,而这个机制恰好是 Android 7.1 的时候才有的,谷歌那个时候并没有考虑到对 Toast 的一些处理。因为通过浏览 Android 7.0 和 Android 6.0 的源码,发现谷歌也是没有进行 try 处理,但是崩溃的机型却全是清一色的 Android 7.1
问题排查
- 通过查看这个崩溃都是在 Android 7.1 的机型才会出现,那么我们可以对比 Android 7.1 的源码和 Android 8.0 看看
- 通过追踪不同 API 等级的源码,发现这个问题在 Android 8.0 上面已经被被修复了
- 通过查看 Toast 的源码,发现 Toast 其实就是一个 WindowManager,并且通过 Handler 来显示和隐藏。
- 而产生崩溃的地方是在 handleShow 方法里面
- 而 handleShow 方法是被 Toast 中的名为 TN 静态内部类中的 Handler 对象调用
进行修复
- 那么解决这一问题的方式的思路是,将这个 Handler 对象通过反射获取到,然后使用静态代理的方式对它进行回调并对进行捕获异常
- 最后经过验证,是 OK 的,已经没有崩溃的问题出现了。
- 但是新的问题又出现了,我们以前写 Toast 是这样的
Toast.makeText(this, "666", Toast.LENGTH_LONG).show();
- 但是如果为了修复这个崩溃问题,我们需要这样写
Toast toast = Toast.makeText(this, "666", Toast.LENGTH_LONG); if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { try { // 获取 mTN 字段对象 Field mTNField = Toast.class.getDeclaredField("mTN"); mTNField.setAccessible(true); Object mTN = mTNField.get(toast); // 获取 mTN 中的 mHandler 字段对象 Field mHandlerField = mTNField.getType().getDeclaredField("mHandler"); mHandlerField.setAccessible(true); final Handler mHandler = (Handler) mHandlerField.get(mTN); // 偷梁换柱 mHandlerField.set(mTN, new Handler() { @Override public void handleMessage(Message msg) { // 捕获这个异常,避免程序崩溃 try { mHandler.handleMessage(msg); } catch (WindowManager.BadTokenException ignored) {} } }); } catch (IllegalAccessException | NoSuchFieldException ignored) {} } toast.show();
- 这样写感觉心好累,不想这样写,有没有一种方式可以一劳永逸?
- 答案当然是有了,使用第三方 Toast 封装的框架:https://github.com/getActivity/Toaster,框架内部已经处理了这个问题,调用者无需关心此问题。
- 使用框架后,可以这么写
Toaster.show("666");
- 还是一句代码,就问你 6 不 6
问题总结
- 问题描述:Toast 在主线程阻塞情况下会导致 WindowToken 失效,从而导致应用崩溃
- 涉及范围:所有 Android 版本为 7.1 的用户,并且项目中使用了原生 Toast 的地方都有可能触发崩溃
- 解决方案:不直接使用原生 Toast,而使用第三方 Toast 框架
【转】android实现扫码枪功能
转载自:android实现扫码枪功能
扫码枪扫码效果等同于键盘录入,会回调dispatchKeyEvent键盘按下事件。
开发环境:有线扫码枪,支持二维码
1. 接收数据
/** * 扫码枪扫码处理 */ @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { int keyCode = event.getKeyCode(); char aChar = (char) event.getUnicodeChar(); if (aChar != 0) { mStringBufferResult.append(aChar); } mHandler.removeCallbacks(mScanningFishedRunnable); //若为回车键,直接返回 if (keyCode == KeyEvent.KEYCODE_ENTER) { mHandler.post(mScanningFishedRunnable); } else { //延迟post,若500ms内,有其他事件 mHandler.postDelayed(mScanningFishedRunnable, 500L); } return true; } return super.dispatchKeyEvent(event); }
2. 处理数据
/** * 二维码信息对象 */ private QRCode qrCodeBean; /** * 二维码信息原始数据容器 */ private StringBuilder mStringBufferResult = new StringBuilder(); private Handler mHandler = new Handler(); private Runnable mScanningFishedRunnable = new Runnable() { @Override public void run() { scanOk = false; String qrcode = mStringBufferResult.toString(); if (!TextUtils.isEmpty(qrcode)) { // 扫码确定参数 Gson gson = new Gson(); try { qrCodeBean = gson.fromJson(qrcode, QRCode.class); // 你的代码... // 如果要支持中文,数据用可以URLEncoder/URLDecoder编解码 } catch (JsonSyntaxException e) { // 解析失败... } catch (UnsupportedEncodingException e) { // 解码失败... } finally { mStringBufferResult.setLength(0); } } } };
【转】Android按键事件KeyEvent的分发处理流程解析
节选自:Android按键事件KeyEvent的分发处理流程解析
1. 在 Activity 里重写 dispatchKeyEvent()—-最常用
举个栗子:
这在 Tv 开发中是很常见的,经常会在 Activity 里重写 dispatchKeyEvent(),然后要么去预先处理一些工作,要么就是对特定的按键进行拦截。
上面这段代码能看懂么?如果你已经清楚这代码是对左右方向按键的拦截,那么你清楚各种 return 的作用么,为什么又有 return true,又有 return false,还有 return super.dispatchKeyEvent() 的?
先说结论:这里的 return true 和 return false 都能起到按键拦截的作用,也就是子 View 不会接收到事件的分发或处理,Activity 的 onKeyDown/Up() 也不会收到任何消息。
要明白这点,先得搞清楚什么是 return, return 是返回的意思,什么情况下需要返回,不就是调用你的那个方法需要你给个反馈,所以 return 的消息是给上一级的调用者的,所以 return 只会对上一级的调用者的行为有影响。调用 Activity.dispatchKeyEvent() 的是 DecorView 的 dispatchKeyEvent() 里,如下图:
那么,既然 Activity 返回 true 或 false 都只对 DecorView 的行为有影响,那么为什么都能起到拦截事件分发的作用呢?
这是因为,事件的分发逻辑其实是在 Activity.java 的 dispatchKeyEvent() 里实现的,如果你重写了 Activity 的 dispatchKeyEvent() 方法,那么根据
Java 的特性程序就会执行你写的 dispatchKeyEvent(),而不会执行基类 Activity.java 的方法,因此你在重写的方法里没有自己实现事件的分发逻辑,事件当然就停止分发了啊。这也是为什么返回 super.dispatchKeyEvent() 时事件会继续分发,因为这最终会调用到基类 Activity.java 的 dispatchKeyEvent() 方法来执行事件分发的逻辑。
既然在 Activity 里返回 true 或 false 都表示拦截,那么有什么区别么?
当然有,因为会影响 DecorView 的行为,比如我们点击遥控器的方向键时界面上的焦点会跟随着移动,这部分逻辑其实是在 DecorView 的上一级调用者中实现的,Activity 返回 true 的话,会导致 DecorView 也返回 true,那么上一级将根据 DecorView 返回 true 的结果停止焦点的移动,这就是我们常见的在 Activity 里重写 dispatchKeyEvent() 返回 true 来实现停止焦点移动的原理。那么,如果 Activity 返回的是 false,DecorView 也跟随着返回 false,那么上一级会继续执行焦点移动的逻辑,表现出来的效果就是,界面上的焦点仍然会移动,但不会触发 Activity 和 View 的事件分发和处理方法,因为已经被 Activity 拦截掉了。
最后,还有一个问题,在 View 或 ViewGroup 里面重写 dispatchKeyEvent() 作用会跟 Activity 一样么?
return true 或 false 或 super 的含义还是一样的,但这里要明白一个层次结构。上层:Activity,中层:ViewGroup,下层:View。
不管在哪一层重写 dispatchKeyEvent(),如果返回 true 或 false,那么它下层包括它本层都不会接收到事件的分发处理,但是它的上层会接收。因为拦截的效果只作用于该层及下层,而上层只会根据你返回的值,行为受到影响。
比如在 ViewGroup 中返回 true,Activity 的 onKeyDown/Up() 就不会被触发,因为被消费了;如果返回 false,那么事件就交由 Activity 处理。但不管返回 true 或 false,子 View 的 dispatchKeyEvent()、各种 onClick() 等事件处理方法都不会被触发到了。
雷电模拟器4.0无法设置代理的问题
添加代理
adb shell settings put global http_proxy 192.168.10.6:8888
移除代理
adb shell settings put global http_proxy :0
解决 Android 副屏(Presentation)View 拉伸问题
使用IntelliJ IDEA将项目打包为jar包
【转】国密算法SM2实现基于hutool工具类
首先引入maven
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15to18</artifactId> <version>1.69</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.4.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency>
直接上代码
import lombok.Data; /** * 1. @description: * 2. @author: xh * 3. @time: 2022/7/14 */ @Data public class SMKeyPair { //私钥 private String priKey; //公钥 private String pubKey; public SMKeyPair(String priKey, String pubKey) { this.priKey = priKey; this.pubKey = pubKey; } }
【转】解决IntelliJ IDEA控制台输出中文乱码问题
转载自:解决IntelliJ IDEA控制台输出中文乱码问题
1.打开IntelliJ IDEA本地安装目录中bin文件夹下的idea.exe.vmoptions和idea64.exe.vmoptions这两个文件。
2.分别在这两个文件内容的末尾添加-Dfile.encoding=UTF-8
3.打开IntelliJ IDEA>File>Setting>Editor>File Encodings,将Global Encoding、Project Encoding、Default encodeing for properties files这三项都设置成UTF-8,点击OK或者Apply。