自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(468)
  • 资源 (17)
  • 收藏
  • 关注

转载 依赖检查的插件

作者:codelang写这款工具主要是看了优酷的几篇 向工程腐化开炮 的系列文章,觉得其中的几个点可以通过依赖检查的方式提前找到问题,所以着手找了几个点写了下,并输出 report html 方便查看。一、检查目前该检查工具提供了 5 项内容的检查:so 文件检查64 位 so 未适配检查更安全的导出组件检查未匹配的权限检查uses-sdk 检查1、so 文件检查so 文件检查可以分析出依赖里面包含了多少个 so 文件,并且展示 so 大小,做这个可以辅助 apk 包体积优化来提.

2022-03-08 13:39:30 461

原创 面试官:讲讲AOP与OOP有什么区别?

面试官: 你知道什么是AOP吗?AOP与OOP有什么区别,谈谈AOP的原理是什么心理分析:一旦问到aop面试官在开发自己的项目中 肯定是用到了aop切面编程的。这个时候求职者需要格外注意,特别是aop 在编译时的性能优势,apk编译的原理讲起。切勿将aop的概念弄混,一定要将oop面向对象与aop面向切面的场景说出来求职者: aop实现的三大方式(反射 (xutil) apt注解(ButterKnife) aspect (本文即将讲到的)) 说出各自的优缺点一、AOP概念百度百科中对AO.

2022-03-07 21:30:00 861 1

转载 为何推荐在自定义View中抽Drawable

作者:Jere_Chen前言在实际业务开发中,当我们需要展示一些简单的图标时,我们都会找UI帮我们切个图,然后直接展示。但一旦遇到一些复杂的图或特殊需求时,比如:股票的分时图、折线图等等,此时UI就帮不了我们了,需要我们自己通过自定义View来进行绘制。而此类自定义View往往都比较复杂,既要处理繁琐的计算及绘制工作,还要处理与用户的交互工作。今天我就来介绍一下,如何通过自定义Drawable来让我们复杂的自定义View变得更加的层次清晰,提升代码可读性。自定义Drawable既然要介绍自定义.

2022-03-07 14:21:43 507

原创 Jetpack Lifecycle 该怎么看?

前言本篇从Lifecycle开始将正式进入Jetpack 各个组件的分析。通过本篇文章你将了解到:1、为什么需要Lifecycle?2、Lifecycle 如何使用?3、Lifecycle 如何感知生命周期4、Lifecycle 内存泄漏?5、总结1、为什么需要Lifecycle?生命周期的起始Android 中常见的拥有生命周期的组件如Activity、Fragment、Service等,其中Activity、Fragment最为常见,而Fragment的生命周期依托于Activi

2022-03-06 21:38:23 252

原创 今年的面试,是真的卷啊~

**很多开发者都知道,**现在的面试从头到尾都是比较有深度的技术问题,虽然那些问题看上去在网上都能查到相关的资料,但面试官基本都是根据你的回答持续深入,如果没有真正对技术原理和底层逻辑有一定的了解是无法通过的。不少求职者抱怨大厂“面试造火箭,工作拧螺丝”,但实际上,面试中的大多数问题都是在全方位地考察你对技术的理解深度,以及解决问题的能力。那些看似无理甚至无用的问题,其实是面试官想借此看看你对相关原理的理解情况和掌握程度,甚至进而引伸到你对架构,设计模式的理解。只有在熟知原理的前提下,你才能够获得面试官的

2022-03-04 20:34:59 298

转载 Android编译优化系列-kapt篇

作者:字节跳动终端技术———王龙海 封光 兰军健一、背景本文是编译优化系列文章之 kapt 优化篇,后续还会有 build cache, kotlin, dex 优化等文章,敬请期待。本文由Client Infra->Build Infra团队出品,powered by 王龙海,封光,兰军健相信 android 开发对于 kapt 并不陌生,之前也有很多文章在编译优化过程中谈及过 Kapt,主要是针对增量编译场景。抖音火山版同学在接入 hilt 过程中,遇到了更严重的问题: 在 16G.

2022-03-04 13:44:55 1127

转载 看这一篇,你也可以自如的掌握字节码插桩

作者:8K吃萝卜本文简介使用GradlePlugin、Transform和ASM实现字节码插桩,GradlePlugin相关代码全部由Kotlin编写,所以不熟悉groovy的小伙伴也可以无障碍阅读。什么是字节码插桩要了解字节码插桩,首先要了解AOP(Aspect Oriented Programming)思想,对比来说,OOP(面向对象编程)主要针对的是业务处理过程的实体极其属性和行为进行抽象封装,为的是有更清晰高效的逻辑单元划分。AOP(面向切面编程)则是针对业务处理过程中的切面进行提取,.

2022-03-03 14:59:39 2665

转载 随便嵌套?Jetpack Compose 到底优秀在哪里

作者:业志陈单纯看官方的介绍或者是网络上的文章,开发者也许已经对 Jetpack Compose 有这么一个印象了:使用 Jetpack Compose 时我们可以深层次地嵌套布局而不用担心会影响性能。这是 Google 在介绍 Jetpack Compose 时经常拿来和原生 View 体系进行比较的一个特性,也是介绍其优势时的一个着重点,本文就来介绍这一方面的相关知识点,涉及到的内容有:原生 View 体系下,我们一直强调 要减少布局的嵌套层次,那这么做的意义是什么呢Jetpack Comp.

2022-03-02 14:59:07 362 1

转载 Retrofit 源码解析

作者:liyihuanx梳理retrofit源码时做的一些记录,但也是挺久之前写的东西呢,感觉分析的不够流畅,跳跃性比较大当时是带着这几个问题去看源码的1.整个请求的流程是怎样的?2.底层是如何用 OkHttp 请求的?3.Okhttp是异步的,retrofit是怎么帮我们切换到主线程的?4.注解是什么时候解析的,怎么解析的?5.Converter和CallAdapter的作用?6.如何支持 Kotlin协程的suspend挂起函数的?Retrofit的简单流程图根据流程图进入,首.

2022-03-01 15:50:02 215

转载 Android 13 来了,你不能错过的 10+ 新变化~

作者:TechMerger当部分 Pixel 用户还在纠结要不要试试面向大屏设备优化的 Android 12L Beta 时,Google 毫不客气地放出了 Android 13 首个开发者预览版本(以下简称 DP1)。作为开发者预览版本,比起 UI 上的变化 Android 13 DP1 更多地是向开发者展示即将在下一个版本中到来的新功能特性和 API 接口。比如主题图标 API、快速设置开关 API、系统相册选择器、独立应用语言设置等等。让我们一起来看一看。▍测试版要提前了虽然首个开发者.

2022-02-28 14:03:21 2769

转载 JetpackCompose之状态管理

作者:CoderZuoState即,状态。官方的解释是:State in an application is any value that can change over time. And ****event can notify a part of a program that something has happened.可以这样说,应用中的状态是指可以随时间变化的任何值。这个定义很广泛,包括数据库或类中变量的所有内容。放在常见的业务场景中,可以说用户点击按钮发生的动画、Text中的文字等.

2022-02-26 21:04:38 705

转载 Android 性能优化之内存优化

众所周知,内存优化可以说是性能优化中最重要的优化点之一,可以说,如果你没有掌握系统的内存优化方案,就不能说你对Android的性能优化有过多的研究与探索。本篇,笔者将带领大家一起来系统地学习Android中的内存优化。可能有不少读者都知道,在内存管理上,JVM拥有垃圾内存回收的机制,自身会在虚拟机层面自动分配和释放内存,因此不需要像使用C/C++一样在代码中分配和释放某一块内存。Android系统的内存管理类似于JVM,通过new关键字来为对象分配内存,内存的释放由GC来回收。并且Android系统在内存

2022-02-25 15:14:38 1072

转载 这次,我想把内存泄漏讲明白

作者:codelang检测内存是否泄漏非常简单,只要在任意位置调用 Debug.dumpHprofData(file) 即可,通过拿到 hprof 文件进行分析就可以知道哪里产生了泄漏,但 dump 的过程会 suspend 所有的 java 线程,导致用户界面无响应,所以又不能随意 dump。为了能找到合理的 dump 时机,leakCanary 就采用预判的方式,在 onDestroy 中先检测一下当前 Activity 是否存在泄漏的风险,如果有这种情况,就开始 dump。需要注意的是,在 on.

2022-02-24 14:35:13 1496

转载 再学一遍android:fitsSystemWindows属性

作者:郭霖对于android:fitsSystemWindows这个属性你是否感觉又熟悉又陌生呢?熟悉是因为大概知道它可以用来实现沉浸式状态栏的效果,陌生是因为对它好像又不够了解,这个属性经常时灵时不灵的。其实对于android:fitsSystemWindows属性我也是一知半解,包括我在写《第一行代码》的时候对这部分知识的讲解也算不上精准。但是由于当时的理解对于我来说已经够用了,所以也就没再花时间继续深入研究。而最近因为工作的原因,我又碰上了android:fitsSystemWindows.

2022-02-23 14:04:30 373

转载 Kotlin + Flow 实现的 Android 应用初始化任务启动库

作者:ezy特性Kotlin + Flow 实现的 Android 应用初始化任务启动库。支持模块化,按模块加载任务可指定工作进程名称,main 表示仅在主进程运行,all 表示在所有进程运行,默认值all可指定任务仅在工作线程执行可指定任务仅在调试模式执行可指定任务在满足合规条件后执行可指定任务优先级,决定同模块内无依赖同步任务的执行顺序可指定依赖任务列表,能检测循环依赖使用 Flow 调度任务仅200多行代码,简单明了有耗时统计引入依赖项目地址:github.com/.

2022-02-22 14:06:49 261

转载 Gradle 7连问,你应该知道这些~

作者: 程序员江同学前言对于Android开发者来说,Gradle也可以说是熟悉的陌生人了,可以说天天会用到Gradle,但对于Gradle的一些原理与细节又往往不太了解本文主要介绍Gradle的一些基础知识与原理,如果对你有所帮助,欢迎点赞本文主要包括以下内容:Gradle到底是什么?Gradle Wrapper是什么?AGP到底是什么?gradle.properties是什么?settings.gradle是什么?build.gradle是什么?Gradle生命周期是怎样的?.

2022-02-21 14:34:36 427

转载 Android使用ViewPager2实现页面滑动切换

作者:QiShare1.引言在很多应用中,我们经常会看到多个页面之间滑动切换的场景,ViewPager2是ViewPager的升级版,本文将简要介绍如何使用ViewPager2、FragmentStateAdapter和Fragment来实现页面之间的滑动切换。2.实现页面滑动切换2.1 引入ViewPager2库要使用ViewPager2,需要引入ViewPager2库,引入方法如下:implementation "androidx.viewpager2:viewpager2:1.0.0".

2022-02-19 20:44:16 1855

转载 LiveData组件解析

作者:者文一、为什么使用LiveData在Android开发之初,大部分代码都放在一个Activity中,导致Activity臃肿且难以单元测试。后来,诞生了MVC、MVP、MVVM等开发架构,通过分层抽离Activity中的代码。这种分层架构模式可以将逻辑从View中分离出来,但是无法感知Activity的生命周期,Activity 的生命周期必须通知这些组件。LiveData是一种可观察的数据存储器类,具有生命周期感知能力,并可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者.

2022-02-18 16:32:00 302

转载 Flutter混编工程之通讯之路

作者:xuyisheng这个系列开始,我们将从「能用的Flutter」到「可用的Flutter」的迁移过程来讲解如何在实际项目中更好的使用Flutter,下面是第一篇。对于混编工程来说,最常用的需求就是双端的数据通信。在Flutter中,SDK提供了platform_channels来进行跨端通信,它的整体架构如下所示。官方文档中提供了一个比较全的例子,下面我们通过这个例子,来好好分析下,如何使用Flutter和原生的通信管道。github.com/flutter/sam…:https://.

2022-02-16 21:03:31 269

转载 kotlin混淆后mapping定位

作者:玉刚说这个文章聊下kotlin的mapping文件的定位,以具体一个线上崩溃为例线上崩溃日志Caused by: java.lang.IndexOutOfBoundsException: toIndex = 10at java.util.AbstractList.subListRangeCheck(AbstractList.java:507)at java.util.ArrayList$SubList.subList(ArrayList.java:1238)at b.a.a.a.p.y.

2022-02-15 15:34:33 435

转载 kotlin 协程 + Retrofit 搭建网络请求方案对比

作者:FredYe近期在调研使用Kotlin协程 + Retrofit做网络请求方案的实践,计划后面会引入到新项目中,Retrofit的使用非常的简单,基本上看个文档就能立马接入,也在github上找了大量的Demo来看别人是怎么写的,看了大量网上的文章,但发现很多文章看下来也只是一个简单的接入Demo,不能满足我当下的业务需求。以下记录近期调研的结果和我们的使用。 首先我们先对比从网上找到的几种方案:方案一代码摘自这里 这是一篇非常好的Kotlin 协程 + Retrofit 入门的文章,其代码.

2022-02-14 14:21:25 862 1

转载 Flutter混编工程之通讯之路

作者:徐益生这个系列开始,我们将从「能用的Flutter」到「可用的Flutter」的迁移过程来讲解如何在实际项目中更好的使用Flutter,下面是第一篇。对于混编工程来说,最常用的需求就是双端的数据通信。在Flutter中,SDK提供了platform_channels来进行跨端通信,它的整体架构如下所示。官方文档中提供了一个比较全的例子,下面我们通过这个例子,来好好分析下,如何使用Flutter和原生的通信管道。https://github.com/flutter/samples/blob.

2022-02-12 21:47:50 229

转载 Android 13 首个开发者预览版到来

作者 / Dave Burke, VP of Engineering每天,全世界都有数十亿人使用 Android 设备来完成各种事项。这些用户在使用 Android 时的优良体验离不开大家,也就是开发者社区的鼎力支持,是开发者的反馈和建议帮助我们打造出更强大的 Android。今天,我们为大家带来 Android 的下一个版本,即 Android 13 的首个开发者预览版。在 Android 13 中,我们将继续推进一些重要的主题: 隐私和安全,以及开发者生产力。我们还将基于 12L 上最近发布的.

2022-02-11 13:45:30 268 1

转载 Androd Gradle 使用技巧之模块依赖替换

作者:汪海游龙背景我们在多模块项目开发过程中,会遇到这样的场景,工程里依赖了一个自己的或者其他同事的 aar 模块,有时候为了开发调试方便,经常会把 aar 改为本地源码依赖,开发完毕并提交的时候,会再修改回 aar 依赖,这样就会很不方便,开发流程图示如下:解决一开始我们通过在 app 的 build.gradle 里的 dependency 判断如果是需要本地依赖的 aar,就替换为 implementation project 依赖,伪代码如下:dependencies { i.

2022-02-10 14:28:07 202 1

转载 对应用架构的理解与 MVI 介绍

作者:korilin应用架构介绍2022 年 Android 官方推荐的应用架构已经从 MVVM 转变为 MVI 了,虽然现有项目还不着急做架构转变,但对于 MVI 的架构设计理念还是要先了解一下。Android 架构的两个设计原则主要有两个:关注点分离 和 数据模型驱动界面。关注点分离个人认为一个 Android APP 包含的代码类型主要有以下几部分(当然可能不止这些):UI 构建代码,这些代码用于构建用户所看到的界面状态处理逻辑,包含向 UI 提供数据、用户行为处理、状态改变逻辑.

2022-02-09 16:43:05 321 1

转载 浅析如何快速实现wanAndroid客户端

来自 | RicardoMJiang的博客地址:https://juejin.cn/user/6681014310094962021年七月底,Google 正式发布了Jetpack Compose的1.0稳定版本,这说明Google认为Compose已经可以用于生产环境了。相信Compose的广泛应用就在不远的将来,现在应该是学习Compose的一个比较好的时机。在了解了Compose的基本知识与原理之后,通过一个完整的项目继续学习Compose应该是一个比较好的方式。本文主要基于Compose.

2022-02-08 14:41:32 322 2

原创 掌握 binder 机制?别想绕开 binder 驱动源码分析

作者:Android面试官binder 是 Android 系统的进程间通信机制,是了解 Android 运行机制必须要掌握的一个知识点,更是一线企业面试必问的知识点!比如:binder 有什么优势?(字节)binder 一次拷贝原理?(腾讯)Intent 传递大数据限制?(阿里)AIDL 原理?(字节)谈谈你对 binder 驱动的了解?(字节)你都能回答上来吗!?到底怎样才能彻底掌握 binder 机制、游刃有余的应对 binder 面试问题,让面试官对你刮目相看,斩获高薪 .

2022-02-07 20:30:00 221

转载 Jetpack DragAndDrop 库——拖放操作如此轻松

拖放是最基本的手势操作,用户可以点击并按住图片、文本或其他数据元素,然后将其拖动至另一个应用 (或者同一个应用的其他位置) 并松手,即可将数据放置到新的位置上。手势通常在触摸屏上表现为长按,在使用鼠标时则为点击并拖动。拖放https://developer.android.google.cn/guide/topics/ui/drag-drop虽然 Android 很早以前便一直支持拖放操作 (DragEvent 在 Android 3.0 即 API 级别为 11 中引入),但事实证明实现对处理.

2022-02-07 16:20:34 216

转载 探索移动端音视频与GSYVideoPlayer之旅 | Agora Talk

作者:恋猫de小郭基础知识首先是基础知识,本次分享在这一块会占据很大比例,为什么要和大家聊音视频的基础知识?这就又要考古我很久前的一个经典 issue ,如图所示:在维护 GSYVideoPlayer 这 5 年多的时间里,关于类似的基础的问题其实收到不少,只是这个比较典型,而处理这些问题的流程都是类似,举个例子,我收到最多的视频播放问题应该是“播放黑屏,播放失败,xxx能播为什么GSY不能播?”每次遇到这种问题的时候,我都会问:“视频编码是什么?”而很大概率我收到的回复就是 :.

2022-01-26 20:49:14 590

转载 Android使用GestureDetector进行手势检测

作者:QiShare1.引言在操作应用的时候,会有很多不同的手势操作,如按下、单击、双击、长按等手势,我们可以在这些手势事件中添加相应的业务逻辑,那么如何检测不同的手势操作就比较重要了,本文将带大家了解如何使用GestureDetector进行手势检测。2.进行手势检测2.1 创建GestureDetector进行手势检测之前,需要先新建GestureDetector对象,示例如下:gestureDetector = new GestureDetector(context, new Gest.

2022-01-26 14:33:45 148

转载 Lifecycle源码解析

作者:Gs2000本篇文章只讲解Lifecycle源码,不涉及如何使用。1、猜想如果是我们实现Lifecycle的功能,我们会怎么设计?一定是在生命周期回调的时候,调用了Lifecycle相应的分发方法,把生命周期方法被调用通知给LifecycleObserver。那么Activity或者Fragment作为生命周期的所有者,在他们的生命周期方法中一定有分发的逻辑。Lifecycle一定会有一个集合,用于保存所有的LifecycleObserver。收到生命周期方法被调用的通知后,调用集.

2022-01-25 16:21:24 248

转载 Handler全解

作者:佛迦技师1. 一句话解释HandlerHandler的作用是发送并处理一个线程关联的Message或Runable2.基本使用方法在UI线程中创建Handlerprivate Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); int arg1=msg.arg1.

2022-01-24 16:32:29 774 2

转载 从Preference组件迭代看Jetpack发展历史

作者:TechMerger谈到Jetpack,大家都以为是一堆框架,事实上它的内容要大的多。本文以大家熟知的Preference组件为切入点,逐步探究它的前世今生。Preference作为设置画面的标准实现,大家都不陌生。这个组件跟随Android系统一同诞生,之后便不断地变更。先是Support库中出现了独立版本,接着整合到了AndroidX中,最后在Android 10的时候完全废弃了SDK版本。1. Preference的设计Preference组件的API设计得非常简单、清晰。.

2022-01-21 14:28:29 174 1

转载 LeakCanary源码简单分析

作者:奔波儿灞取经Java四大引用强引用: 绝不回收软引用: 内存不足才回收弱引用: 碰到就回收虚引用: 等价于没有引用,只是用来标识下指向的对象是否被回收。弱引用的使用我们可以为弱引用指定一个引用队列,当弱引用指向的对象被回收时,此弱引用就会被添加到这个队列中,我们可以通过判断这个队列中有没有这个弱引用,来判断该弱引用指向的对象是否被回收了。// 创建一个引用队列ReferenceQueue<Object> queue = new ReferenceQueue<.

2022-01-20 22:09:17 140

转载 【源码解读】抽丝剥茧的分析ViewModel的核心原理

作者:罗恩不带土ViewModel背景ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。摘自 ViewModel概览详细讲,ViewModel有如下几个特点:对于简单数据,Activity被销毁的时候,可以使用onSaveInstanceState()方法从onCreate中恢复其绑定数据,但不适用数量较大的数据,如用户列表或位图。而Viewmodel支持大量数据,也不需要序列化和反序列化操作。视图控.

2021-12-13 14:34:32 261 2

原创 了解Android架构组件后,构建APP超简单

Google 为了帮助 Android 开发者更快更好地开发 App,推出了一系列组件,这些组件被打包成了一个整体,称作 Android Jetpack,它包含的组件如下图所示:老的 support 包被整合进了 Jetpack,例如上图 Foundation 模块的 AppCompat,整合进去之后,包名做了一下修改,全部以 androidx 开头。Android Studio 提供的迁移工具(Refactor > Migrate to AndroidX)可以将源码中的旧包名替换成新的,但是如果

2021-11-29 21:18:21 497 2

原创 细说Jetpack中那些LiveData们

1)低调的CoroutinLiveDataliveData { this.emit("data") //1处}.observe(owner){ data -> doSomething(data)//2处}开局一段代码liveData()全局方法,他的返回值是一个LiveData因此我们可以直接监听其返回值。最后一个参数是一个带接收者的Lambda表达式,这个lambda表达式的接收者是LiveDataScope,可以直接在代码块中(1处)中调用其emit方法,向LiveDa

2021-11-26 20:51:04 388 1

转载 Android 无所不能的 hook,让应用不再崩溃

好文推荐:作者:鸿洋之前推送了很多大厂分享,很多同学看完就觉得,大厂输出的理论知识居多,缺乏实践。那这篇文章,我们将介绍一个大厂的库,这个库能够实打实的帮助大家解决一些问题。今天的主角:初学者小张,资深研发老羊。三方库中的 bug这天 QA 上线前给小张反馈了一个 bug,应用启动就崩溃,小张一点不慌,插入 USB,触发,一看日志,原来是个空指针。想了想,空指针比较好修复,大不了判空防御一下,于是回答:这个问题交给我,马上修复。根据堆栈,找到了空指针的元凶。忽然间,小张愣住了,这个空指.

2021-11-25 22:05:24 1100 1

转载 RecyclerView.ViewCacheExtension 使用及踩坑

作者:Delicia_Lani前言最近遇到一个需求,需求实现上并不复杂,大概长这个样:基本上就是一个RecyclerView 嵌套多个子 RecyclerView ,有横向的,也有竖向的。RecyclerView 实现多类型布局有各种各样的实现方式,这里就不多说了。本来很开心的实现完了,在测试中确遇到了非常严重的性能问题,也就了本篇文章的诞生。具体的讲,嵌套的横向滑动的RecyclerView 没有任何问题,嵌套的竖向RecyclerView 在上下滑动时却遇到了非常严重的性能问题,表现为在从.

2021-11-24 16:55:57 669 3

转载 嵌套滑动吸顶效果

作者:blue吸顶效果是各家 App 或多或少都会用的一个交互,这种交互也常见于 PC、H5,可以说是一种通用性很强的前端交互体验,相比较来说京东首页的嵌套滑动吸顶效果是各个类似效果中体验比较好的一个,因为在嵌套布局中滑动连贯性处理得非常好,今天我们就来实现同样的交互效果。这篇文章我会讲些什么对于吸附效果实现的思路3 个版本的 NestedScrollingParent、NestedScrollingChild 简单介绍嵌套组件滑动连贯性(一致性)的处理(事件分发、Fling 等)交互的优.

2021-11-22 15:11:35 472 2

组件化通信之LiveData.rar

都说新项目组件通信一定用LiveData,我们就从源码层来聊聊LiveData的魅力 内容点: 1.LiveData到底是什么? 2.LiveData源码解读? 3.通过手写LiveData让大家了解更清晰 4.如何将LiveData封装得更加好用?

2020-12-18

resized_img.zip

手势识别图片,主要是训练神经网络图片。用tersorflow实现手势识别

2019-07-12

WangyiPush.zip

直播详解,可运行,通过rtmp协议实现,直播的详细说明可参考https://github.com/interviewandroid/AndroidInterView/blob/master/android/live.md 关于直播的视频讲解可以加qq 1051917835 免费的 哦

2019-06-22

RabbitMQ消息中间件技术精讲

RabbitMQ消息中间件技术精讲(渐进式,深入RabbitMQ高级特性,手把手,整合RabbitMQ&Spring;家族,高可靠,构建RabbitMQ集群架构,追前沿,领略SET化架构衍化与设计)

2019-02-24

MySQL原理分析与架构设计视频教程

MySQL原理分析与架构设计(数据库索引优化,数据库的分库分表, SQL查询优化,mysql行内锁原理)

2019-02-24

2019Java高级面试专题

2019Java高级面试专题包含(微服务+数据库原理+Java性能优化面试视频)

2019-02-24

rxjava 1.0.14

rxjavah和rxandroid下载

2016-11-23

eclipse svn插件

2016-08-12

jbox2d愤怒的小鸟游戏源码

jbox2d愤怒的小鸟游戏源码

2016-07-14

FreScon源码下载

FreScon源码下载

2016-06-30

sdk build-tool 19.1.0

build-tool 19.1.0 解压到sdk下面的build-tools目录就可以了

2016-01-07

Xcode6.4 beta2 下载

Xcode6.4 ios版 适合mac 10.10以下版本

2015-12-27

cdt插件下载

Eclipse 中CDT插件 将文件解压后 分别放到对应的eclipse目录中的features 和plugins文件夹中

2015-12-27

城市选择器

城市选择器,自动定位,城市列表自动按拼音排序

2015-10-21

可以拨动的时间选择器

直接可以运行。漂亮的时间选择空间 public class MainActivity extends FragmentActivity implements OnDateSetListener, TimePickerDialog.OnTimeSetListener { public static final String DATEPICKER_TAG = "datepicker"; public static final String TIMEPICKER_TAG = "timepicker"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final Calendar calendar = Calendar.getInstance(); final DatePickerDialog datePickerDialog = DatePickerDialog.newInstance(this, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), isVibrate()); final TimePickerDialog timePickerDialog = TimePickerDialog.newInstance(this, calendar.get(Calendar.HOUR_OF_DAY) ,calendar.get(Calendar.MINUTE), false, false); findViewById(R.id.dateButton).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { datePickerDialog.setVibrate(isVibrate()); datePickerDialog.setYearRange(1985, 2028); datePickerDialog.setCloseOnSingleTapDay(isCloseOnSingleTapDay()); datePickerDialog.show(getSupportFragmentManager(), DATEPICKER_TAG); } }); findViewById(R.id.timeButton).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { timePickerDialog.setVibrate(isVibrate()); timePickerDialog.setCloseOnSingleTapMinute(isCloseOnSingleTapMinute()); timePickerDialog.show(getSupportFragmentManager(), TIMEPICKER_TAG); } }); if (savedInstanceState != null) { DatePickerDialog dpd = (DatePickerDialog) getSupportFragmentManager().findFragmentByTag(DATEPICKER_TAG); if (dpd != null) { dpd.setOnDateSetListener(this); } TimePickerDialog tpd = (TimePi

2015-10-21

annotations注解

android annotations注解 快速注解,findbyid

2015-10-21

仿QQ自定义ListView 下拉刷新

package me.maxwin; import java.util.ArrayList; import me.maxwin.view.XListView; import me.maxwin.view.XListView.IXListViewListener; import me.maxwin.view.XListView.RemoveListener; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; public class XListViewActivity extends Activity implements IXListViewListener ,RemoveListener,OnItemClickListener{ private XListView mListView; // private ArrayAdapter<String> mAdapter; private ItemAdapter adapter; // private Context context; private ArrayList<String> items = new ArrayList<String>(); private Handler mHandler; private int start = 0; private static int refreshCnt = 0; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); geneItems(); init(); mHandler = new Handler(); } private void init() { // TODO Auto-generated method stub mListView = (XListView) findViewById(R.id.xListView); mListView.setPullLoadEnable(true); mListView.setRemoveListener(this); mListView.setOnItemClickListener(this); // mListView.setPullLoadEnable(false); // mListView.setPullRefreshEnable(false); mListView.setXListViewListener(this); adapter=new ItemAdapter(this); adapter.setData(items); mListView.setAdapter(adapter); } private void geneItems() { for (int i = 0; i != 20; ++i) { items.add("refresh cnt " + (++start)); } } private void onLoad() { mListView.stopRefresh(); mListView.stopLoadMore(); mListView.setRefreshTime("刚刚"); } @Override public void onRefresh() { mHandler.postDelayed(new Runnable() { @Override public void run() { start = ++refreshCnt; items.clear(); geneItems(); // mAdapter.notifyDataSetChanged(); adapter=new ItemAdapter(XListViewActivity.this); adapter.setData(items); mListView.setAdapter(adapter); onLoad(); } }, 2000); } @Override public void onLoadMore() { mHandler.postDelayed(new Runnable() { @Override public void run() { geneItems(); adapter.notifyDataSetChanged(); onLoad(); } }, 2000); } @Override public void removeItem(int position) { // TODO Auto-generated method stub mListView.isSlide = false; mListView.itemView.findViewById(R.id.tv_coating).setVisibility(View.VISIBLE); items.remove(position); adapter.notifyDataSetChanged(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub Intent intent = new Intent(getApplicationContext(), ItemActivity.class); intent.putExtra("item", items.get(position)); startActivity(intent); overridePendingTransition(R.anim.slide_in_from_right, R.anim.remain_original_location); } } 自定义LIstView * * @file XListView.java * @package me.maxwin.view * @create Mar 18, 2012 6:28:41 PM * @author Maxwin * @description An ListView support (a) Pull down to refresh, (b) Pull up to load more. * Implement IXListViewListener, and see stopRefresh() / stopLoadMore(). */ package me.maxwin.view; import me.maxwin.R; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.DecelerateInterpolator; import android.view.animation.ScaleAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.Scroller; import android.widget.TextView; public class XListView extends ListView implements OnScrollListener { private float mLastY = -1; // save event y private Scroller mScroller; // used for scroll back private OnScrollListener mScrollListener; // user's scroll listener // the interface to trigger refresh and load more. private IXListViewListener mListViewListener; // -- header view private XListViewHeader mHeaderView; // header view content, use it to calculate the Header's height. And hide it // when disable pull refresh. private RelativeLayout mHeaderViewContent; private TextView mHeaderTimeView; private int mHeaderViewHeight; // header view's height private boolean mEnablePullRefresh = true; private boolean mPullRefreshing = false; // is refreashing. // -- footer view private XListViewFooter mFooterView; private boolean mEnablePullLoad; private boolean mPullLoading; private boolean mIsFooterReady = false; // total list items, used to detect is at the bottom of listview. private int mTotalItemCount; // for mScroller, scroll back from header or footer. private int mScrollBack; private final static int SCROLLBACK_HEADER = 0; private final static int SCROLLBACK_FOOTER = 1; private final static int SCROLL_DURATION = 400; // scroll back duration private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px // at bottom, trigger // load more. private final static float OFFSET_RADIO = 1.8f; // support iOS like pull // feature. private int slidePosition, preSlidePosition; private int downY; private int downX; public static View itemView, preItemView; // private Scroller scroller; private static final int SNAP_VELOCITY = 600; private VelocityTracker velocityTracker; public static boolean isSlide = false; private boolean isResponse = true; public static boolean isObstruct = true; private int mTouchSlop; private RemoveListener mRemoveListener; private static Animation scaleHideAnimation; private static Animation scaleShowAnimation; /** * @param context */ public XListView(Context context) { super(context); initWithContext(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } public XListView(Context context, AttributeSet attrs) { super(context, attrs); initWithContext(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } public XListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // mScroller = new Scroller(context, new DecelerateInterpolator()); initWithContext(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } private void initWithContext(Context context) { mScroller = new Scroller(context, new DecelerateInterpolator()); // XListView need the scroll event, and it will dispatch the event to // user's listener (as a proxy). super.setOnScrollListener(this); // init header view mHeaderView = new XListViewHeader(context); mHeaderViewContent = (RelativeLayout) mHeaderView .findViewById(R.id.xlistview_header_content); mHeaderTimeView = (TextView) mHeaderView .findViewById(R.id.xlistview_header_time); addHeaderView(mHeaderView); // init footer view mFooterView = new XListViewFooter(context); // init header height mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mHeaderViewHeight = mHeaderViewContent.getHeight(); getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }); } @Override public void setAdapter(ListAdapter adapter) { // make sure XListViewFooter is the last footer view, and only add once. if (mIsFooterReady == false) { mIsFooterReady = true; addFooterView(mFooterView); } super.setAdapter(adapter); } /** * enable or disable pull down refresh feature. * * @param enable */ public void setPullRefreshEnable(boolean enable) { mEnablePullRefresh = enable; if (!mEnablePullRefresh) { // disable, hide the content mHeaderViewContent.setVisibility(View.INVISIBLE); } else { mHeaderViewContent.setVisibility(View.VISIBLE); } } /** * enable or disable pull up load more feature. * * @param enable */ public void setPullLoadEnable(boolean enable) { mEnablePullLoad = enable; if (!mEnablePullLoad) { mFooterView.hide(); mFooterView.setOnClickListener(null); } else { mPullLoading = false; mFooterView.show(); mFooterView.setState(XListViewFooter.STATE_NORMAL); // both "pull up" and "click" will invoke load more. mFooterView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startLoadMore(); } }); } } /** * stop refresh, reset header view. */ public void stopRefresh() { if (mPullRefreshing == true) { mPullRefreshing = false; resetHeaderHeight(); } } /** * stop load more, reset footer view. */ public void stopLoadMore() { if (mPullLoading == true) { mPullLoading = false; mFooterView.setState(XListViewFooter.STATE_NORMAL); } } /** * set last refresh time * * @param time */ public void setRefreshTime(String time) { mHeaderTimeView.setText(time); } private void invokeOnScrolling() { if (mScrollListener instanceof OnXScrollListener) { OnXScrollListener l = (OnXScrollListener) mScrollListener; l.onXScrolling(this); } } private void updateHeaderHeight(float delta) { mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight()); if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头 if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mHeaderView.setState(XListViewHeader.STATE_READY); } else { mHeaderView.setState(XListViewHeader.STATE_NORMAL); } } setSelection(0); // scroll to top each time } /** * reset header view's height. */ private void resetHeaderHeight() { int height = mHeaderView.getVisiableHeight(); if (height == 0) // not visible. return; // refreshing and header isn't shown fully. do nothing. if (mPullRefreshing && height <= mHeaderViewHeight) { return; } int finalHeight = 0; // default: scroll back to dismiss header. // is refreshing, just scroll back to show all the header. if (mPullRefreshing && height > mHeaderViewHeight) { finalHeight = mHeaderViewHeight; } mScrollBack = SCROLLBACK_HEADER; mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION); // trigger computeScroll invalidate(); } private void updateFooterHeight(float delta) { int height = mFooterView.getBottomMargin() + (int) delta; if (mEnablePullLoad && !mPullLoading) { if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load // more. mFooterView.setState(XListViewFooter.STATE_READY); } else { mFooterView.setState(XListViewFooter.STATE_NORMAL); } } mFooterView.setBottomMargin(height); // setSelection(mTotalItemCount - 1); // scroll to bottom } private void resetFooterHeight() { int bottomMargin = mFooterView.getBottomMargin(); if (bottomMargin > 0) { mScrollBack = SCROLLBACK_FOOTER; mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION); invalidate(); } } private void startLoadMore() { mPullLoading = true; mFooterView.setState(XListViewFooter.STATE_LOADING); if (mListViewListener != null) { mListViewListener.onLoadMore(); } } //RemoveListener public void setRemoveListener(RemoveListener removeListener) { this.mRemoveListener = removeListener; } public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { Log.e("lg", "dispatchTouchEvent ACTION_DOWN isSlide1111 = " + isSlide); addVelocityTracker(event); downX = (int) event.getX(); downY = (int) event.getY(); slidePosition = pointToPosition(downX, downY); if (slidePosition == AdapterView.INVALID_POSITION) { return super.dispatchTouchEvent(event); } if (preItemView != null && preItemView.findViewById(R.id.tv_coating).getVisibility() == View.GONE) { itemView = preItemView; slidePosition = preSlidePosition; } else { itemView = getChildAt(slidePosition - getFirstVisiblePosition()); preItemView = itemView; preSlidePosition = slidePosition; } break; } case MotionEvent.ACTION_MOVE: { if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY || (Math.abs(event.getX() - downX) > mTouchSlop && Math.abs(event.getY() - downY) < mTouchSlop)) { isSlide = true; } break; } case MotionEvent.ACTION_UP: recycleVelocityTracker(); isObstruct = true; break; } return super.dispatchTouchEvent(event); } public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_MOVE: if (itemView.findViewById(R.id.tv_coating).getVisibility() == View.VISIBLE) { isSlide = false; } else { isSlide = true; } break; default: break; } return super.onInterceptTouchEvent(ev); } //dispatchTouchEvent ACTION_DOWN isSlide // @Override dispatchTouchEvent public boolean onTouchEvent(MotionEvent ev) { if (isSlide && slidePosition != AdapterView.INVALID_POSITION) { addVelocityTracker(ev); final int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_MOVE: if (isObstruct) { if (itemView.findViewById(R.id.tv_coating).getVisibility() == View.VISIBLE && isResponse == true) { scaleHideAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 1.0f); scaleHideAnimation.setDuration(250); scaleHideAnimation.setAnimationListener(new AnimationListener() { public void onAnimationStart(Animation animation) { isResponse = false; isObstruct = false; } public void onAnimationRepeat(Animation animation) { } public void onAnimationEnd(Animation animation) { isResponse = true; itemView.findViewById(R.id.tv_coating).setVisibility(View.GONE); itemView.findViewById(R.id.tv_functions).setClickable(true); itemView.findViewById(R.id.tv_functions).setOnClickListener(new OnClickListener() { public void onClick(View v) { mRemoveListener.removeItem(slidePosition); } }); } }); itemView.findViewById(R.id.tv_coating).startAnimation(scaleHideAnimation); } else if (itemView.findViewById(R.id.tv_coating).getVisibility() == View.GONE && isResponse == true) { scaleShowAnimation = new ScaleAnimation(0.0f, 1.0f, 1.0f, 1.0f); scaleShowAnimation.setDuration(250); scaleShowAnimation.setAnimationListener(new AnimationListener() { public void onAnimationStart(Animation animation) { isResponse = false; isObstruct = false; } public void onAnimationRepeat(Animation animation) { } public void onAnimationEnd(Animation animation) { isSlide = false; isResponse = true; itemView.findViewById(R.id.tv_coating).setVisibility(View.VISIBLE); } }); itemView.findViewById(R.id.tv_coating).startAnimation(scaleShowAnimation); } } break; case MotionEvent.ACTION_UP: isObstruct = true; recycleVelocityTracker(); isSlide = true; break; } return true; } if (mLastY == -1) { mLastY = ev.getRawY(); } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mLastY = ev.getRawY(); break; case MotionEvent.ACTION_MOVE: final float deltaY = ev.getRawY() - mLastY; mLastY = ev.getRawY(); if (getFirstVisiblePosition() == 0 && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) { // the first item is showing, header has shown or pull down. updateHeaderHeight(deltaY / OFFSET_RADIO); invokeOnScrolling(); } else if (getLastVisiblePosition() == mTotalItemCount - 1 && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) { // last item, already pulled up or want to pull up. updateFooterHeight(-deltaY / OFFSET_RADIO); } break; default: mLastY = -1; // reset if (getFirstVisiblePosition() == 0) { // invoke refresh if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) { mPullRefreshing = true; mHeaderView.setState(XListViewHeader.STATE_REFRESHING); if (mListViewListener != null) { mListViewListener.onRefresh(); } } resetHeaderHeight(); } else if (getLastVisiblePosition() == mTotalItemCount - 1) { // invoke load more. if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA && !mPullLoading) { startLoadMore(); } resetFooterHeight(); } break; } return super.onTouchEvent(ev); // return super.onTouchEvent(ev); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { if (mScrollBack == SCROLLBACK_HEADER) { mHeaderView.setVisiableHeight(mScroller.getCurrY()); } else { mFooterView.setBottomMargin(mScroller.getCurrY()); } postInvalidate(); invokeOnScrolling(); if (mScroller.isFinished()) { if (mRemoveListener == null) { throw new NullPointerException("RemoveListener is null, we should called setRemoveListener()"); } itemView.scrollTo(0, 0); } } super.computeScroll(); } //addVelocityTracker private void addVelocityTracker(MotionEvent event) { if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } velocityTracker.addMovement(event); } private void recycleVelocityTracker() { if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } } private int getScrollVelocity() { velocityTracker.computeCurrentVelocity(1000); int velocity = (int) velocityTracker.getXVelocity(); return velocity; } public interface RemoveListener { public void removeItem(int position); } @Override public void setOnScrollListener(OnScrollListener l) { mScrollListener = l; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mScrollListener != null) { mScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // send to user's listener mTotalItemCount = totalItemCount; if (mScrollListener != null) { mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public void setXListViewListener(IXListViewListener l) { mListViewListener = l; } /** * you can listen ListView.OnScrollListener or this one. it will invoke * onXScrolling when header/footer scroll back. */ public interface OnXScrollListener extends OnScrollListener { public void onXScrolling(View view); } /** * implements this interface to get refresh/load more event. */ public interface IXListViewListener { public void onRefresh(); public void onLoadMore(); } }

2015-05-04

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除