自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

黎程雨的博客

粉丝交流群:872206502

  • 博客(370)
  • 资源 (16)
  • 收藏
  • 关注

原创 网易云--手机QQ的换肤是怎么做到的,你对换肤有了解吗?看过换肤的原理没?
原力计划

面试官: 网易云QQ的换肤是怎么做到的,你对换肤有了解吗?看过换肤的原理没?心理分析:没有接触过换肤技术 第一次听到该名词肯定会很茫然。面试官考的是对资源加载,监听布局,有没有了解.本文从换肤实战一对一讲解。告诉你如何做以及实现。文章末尾带换肤项目源码求职者:从监听布局开始到 换肤原理,详细给面试官讲解换肤的原理接下来我们一起分享这篇干货。 Android的主题换肤 ,可插件化提供皮肤包,无需Activity的重启直接实现无缝切换,可高仿网易云音乐的主题换肤。这个链接是本次的D..

2020-06-20 22:46:08 1816 4

原创 Android App开发之Jetpack架构

还记得15年那个时候,好像Android就是解析数据,列表展示,了解下四大组件生命周期,已经可以干活了,还记得当年一个View只要能滑动,就大呼自定义控件牛逼。不过,近几年的变化颇大。为了丰富大家日常开发的UI效果效果: Material Design进入大家的视野,后面陆陆续续涌现出一大批UI组件:DrawerLayout,AppbarLayout,CoordinateLayout,ToolBar,NestedScrollView,FlexBoxLayout等...在UI组件日益完善的同时,也

2020-11-30 22:18:19 5

转载 Android开发面试之RxJava

好文推荐:作者:xiangcman大家都知道RxJava上手是非常难的一个框架,为什么说是难呢,因为它的功能非常强大,各种操作符让人很难上手,搭配使用带生命周期的框架有RxLife等。以至于后面出了很多类似Rxjava的框架,有RxAndroid,我们用的RxJava切换主线程就是出自该框架,后面ACC架构中有LiveData、Lifecycle、包括后面协成中出来的flow异步流,都是向Rxjava靠拢,不过ACC出来的这些框架他们最大的特点就是操作简单,上手简单。所以RxJava强大的框架背后.

2020-11-30 21:52:34 4

原创 Android 开发还有必要深耕吗? 未来前景将会怎样?

身边总有人问这些问题:“现在学习 Android 开发还有前景吗?”“Android 开发还有什么可以研究的?”近几年来,许许多多的移动端的Android开发者们,对于职业的未来,都有一些迷茫和焦虑。为什么会这样呢?从技术的角度来看,今年移动端的技术变革也有点缓慢。大前端的概念虽然说了很久也很多,但 React Native、PWA 的效果依然不尽人意。在插件化热潮之后,移动端的精品文章开始变少。去年“安利”完 Kotlin 之后,今年讲得比较多的还是 Flutter。遗憾的是这些都像蜻蜓点水一般

2020-11-28 22:18:03 136 1

原创 那些炫酷到把隔壁产品都馋哭的UI效果如何实现?

在开发过程中,没有差不多这个概念,差之毫厘谬以千里。一款好的产品,经过了很多的工序,包括市场调研,产品策划,美工设计,开发测试上线等。每一个环节扣在一起,完美对接才能被用户所接受。很多开发者,把UI看的不是很重要,但是恰恰相反,**UI是一款APP给用户的第一直观感受,如果UI做不好,哪怕是业务逻辑再好,体验再好,最终也只能被用户所抛弃。**因为用户不会管你的业务逻辑,他们只会管他们所能看到的,所以一款产品的成功,跟产品的UI是息息相关的。一些精美的UI▼一些精美的UI ▼一些精美的UI▼其

2020-11-27 21:10:38 13

原创 Android 开发5年经验,薪资竟然被应届生倒挂了!

脉脉和微博上总有人吐槽公司校招和社招薪资倒挂,有的 Android 工程师好几年工作经验的还不如校招工资高。 比如有个老哥在腾讯工作了5年,薪资被21届的应届生Offer倒挂了,心里很不是滋味。工资倒挂,是很多公司都有的现象。这有啥好吐槽的,有的人工作个两三年就开始晃荡,不再进行自我提升,抱着那点不值钱的经验倚老卖老,被后浪拍在沙滩上一点也不意外。年轻人学习能力强,精力充沛,也能在公司比较稳定地干下去,公司愿意多给一些钱。你那所谓的几年经验,可能优秀的校招生不用几个月就学明白了。世界迭代这么快,经验

2020-11-26 22:47:27 29 1

原创 带有活力的属性动画源码分析与实战——Android高级UI

作者:猛猛的小盆友一、前言对于越来越追求丰富的交互体验的客户端,一个带有动态效果的界面已经是不可避免。属性动画就是这其中必不可少的一把利器,所以今天便来分享下 属性动画融合在实际场景中 的使用,以及进行 源码分析。话不多说,先看看今天的实战效果图,然后开始进行分享之旅。1、多维雷达图2、表盘指示器3、活力四射的购物车老规矩,代码在实战篇会给出,透过 API 看清源码,各种效果便是顺手拈来????二、插值器与估值器对于属性动画来说,一定是绕不开 插值器 与 估值器。接下来便一一道来

2020-11-25 21:51:10 14

转载 深度遍历讲解Android事件分发机制

作者:字节小站Android事件分发是一个老生常谈的知识点。日常开发和求职过程中,都会碰到Android事件分发的问题。Android的控件分为两类,ViewGoup和View。ViewGroup是控件的容器,可以包含多个子控件。View是控件的最小单位,它不能包裹其它的View。Android的ViewGroup对应的数据结构是树。网上的事件分发的文章大多数是用线性的思维去分析控件树的事件遍历,我深以为不妥,经常让读者云里雾里,只见树木不见森林。本文将以树的深度遍历方式来讲解DOWN事件的分发.

2020-11-24 22:44:03 37

原创 在腾讯从事Android 开发8年的老王,尽然在一轮裁员风暴中绊了跟头!

最近在网络上看见一则帖子,是关于鹅厂(腾讯)的一则裁员消息,该则消息表示:腾讯继去年裁撤一批中层干部后,近期又在认真劝退一批高龄员工,而我的朋友老王就在这个名单里,并且他们对高龄的定义是……(80年-85年出生的尚未成为高管的互联网从业者!)天哪,曾几何时我眼里的80后还是那群怀抱着理想的追风少年,怎么放到了互联网行业,就成了高龄人士呢?(一口老血)这是一种现象,但腾讯绝对不是互联网大厂里第一个干这事的人,阿里、百度之前也纷纷在内部宣布和执行了他们的裁员计划:3月15日,百度正式推出了高管退休计划,

2020-11-24 22:27:36 105 1

原创 Android程序员对35岁时的担忧:未来真的没有什么好的前景吗?

我们都知道在互联网高速发展的现在,程序员已经成为了一个高薪职业,与其他行业相比,程序员的工资一直是名列前茅,因此很多大学生以及应届毕业生都想加入互联网这个行业。但是学编程真的有前途吗?做Android程序员发展前景怎么样?在很多人眼里,程序员就是高工资的代名词,也是绝大部分人对程序员的第一印象,可是我们绝不会像羡慕公务员那样,去夸赞程序员:这么高的工资,一辈子就不用愁了,在现实生活中,似乎也没见过五六十岁的码农,佝偻着在电脑旁码这代码。显然,程序员并不是一个吃青春饭的职业,但存在着一个怪圈,5年前,大部

2020-11-24 22:23:01 74 1

原创 Android —— 自定义View中,你应该知道的知识点

什么是自定义View?在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了。下面,我就来讲讲自定义控件的那些事。首先,我来讲讲Android的控件架构。Android的控件可以被分为两类,分别是ViewGroup和View。在ViewGroup中可以包含多个View,并且管理他们。控件树就是有这两个部分组成的,控件树的上层负责的是下层控件的绘制和测量以及交互。我

2020-11-23 22:34:20 45

原创 三次失力美团Android 开发岗后,时隔一年终于成功拿下offer

作者:霍比特前言在写这份美团面经的一年前,我已经挂了三次,但我始终不甘心就这么挂这了。在经过一年时间一边工作一边学习,又再次的来到这个地方挑战,这次面试和前面三次不一样了,因为之前是没有进行准备而来。而这是有备而来,我将当初面试失力的面试题和往年大厂面试进行了整理,并把对应的参考答案也进行了整理。就在要面试的前几天,我就开始狂刷这些题,我就不信这次还不过关。请看下方面经。美团一面:技术面手写算法题。一共有几个,面试官会随机抽一个作为题目。给出一个序列包含n个正整数的序列A,然后给出一个正整.

2020-11-22 22:18:19 33

原创 眼看寒冬将至,Android 程序员们该何去何从?

寒冬真的来了吗?可能是吧,因为最近几年每年都说是寒冬,说的真的叫人焦虑。不过互联网的整体环境确实不如前几年,即使不说我想大家也能隐约的感受到了。作为曾经炙手可热的Android工程师,现在面临着以下的问题:互联网整体环境不乐观Android开发人员众多但岗位减少,导致要求越来越苛刻大前端趋势、跨平台方案频出、小程序肆虐,Android不再是应用开发唯一的方案人工智能、大数据、区块链等技术炒的火热,培训机构倾巢出动,学校学生疯狂学习,而培训Android出来就失业,大学学Android出来基本也失

2020-11-21 22:21:57 35

原创 十月的Android面试之旅,惨败在字节三面,幸斩获小米Offer

我是10月份开始的面试之旅,大小公司面试了十几家,但最终我拿下了小米的offer,但是我自己也满足了,想把经验分享出来,进而帮助更多跟我一样想进大厂的同行朋友们,希望大家可以拿到理想offer。自我介绍我是14年从南京工业大学毕业,直到现在,我已经在Android开发的这条路走了6年。在这6年的时间,我见证了移动互联网的“盛衰交替”,身边的同事都是转行换业,所见的市场行情也是一片萧瑟。如今,虽然Android已风光不再,但是“它”还是那个Android,还是那个我赖以生存、夜以继日陪伴着我的朋友.

2020-11-20 22:31:47 55

原创 你对Jetpack 架构组件了解多少?

本文是我在学习Jetpack的过程中做的一些记录,如有错误,欢迎指正本文包含了 ViewModel、Lifecycles、LiveData、Room、WorkManager 的相关用法,你可以通过目录直接跳转到你想了解的地方ViewModel简单介绍下 ViewModel:ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。这是官网给出的介绍,简单解释一下 ViewModel 就是将界面 (Activity或F.

2020-11-20 22:18:45 35

原创 2020的互联网寒冬即将来袭,Android 开发者们该度过?

一年一季度的互联网寒冬又将来袭,你做好了迎接的准备嘛?最近在网上看见一折去年的帖子,讲一名程序员从某公司离职后,找工作半个月有余,情况不是很理想,其在互联网社区吐槽:找工作半个月,被互联网寒冬吓得瑟瑟发抖,越来越慌,收到几个创业小公司的offer,2万,要不要先度过寒冬再说?很显然,这名程序员有点着急了,找工作情况不是太好,只拿到小公司的offer,不知道是否该接,接了的话恐怕对以后的职业发展不利,不接的话又恐怕后面很难找工作,给自己带来巨大的心理压力。很快,网友们就给出了这样的评论:你觉得他们能

2020-11-19 22:46:31 27 1

原创 未来五年,做纯应用开发的Android 程序员,该如何通过音视频破局?

“一名应届毕业生,该如何快速地成长起来?”“只懂应用层,目前越来越多的企业要求音视频技术,我该怎么办?”“到底该不该学习音视频,怎么抉择?"“想从事音视频开发,该怎么入门?如何进阶 ?”如果你有这样的问题,从侧面反映了你是一个积极向上,想不断努力来提升自己的人。先从一个简单的问题聊起,“到底 Android 应用层开发还是做 Android音视频开发更有前途?”从薪资和市场来分析音视频比普通开发的薪资高出不少,音视频工程开发的薪资比Android应用层开发高出40%。音视频开发单从薪.

2020-11-19 18:07:36 53

原创 Android菜鸡想逆袭进入阿里,需要达到什么要求?

听上去好像挺厉害,挺光鲜,但是实际上并不那么容易,在这背后有很多我自己摸索和努力的故事。在这里我想分享一些我的经验,送给那些跟我一样,没名校背景没大厂背景,但是想进阿里(或其他大厂),又有点迷茫不知该如何前进的人。其实在进阿里之前,我也挺迷茫,内心有一些谜团一直困扰着我,比如阿里招人标准是什么?,自己距离这个标准有多少差距?那时候一直不知道,就好像置身于沙漠之中,却不知道往哪里走。这时候,找到方向最为关键。(很多人跟我那时候一样迷茫,因为不少人问过我,到底怎么样才能加入阿里。)当时我问了几个在阿里

2020-11-18 21:38:53 30 1

转载 Android进阶——解密笔记

作者:AKidAndroid 系统启动过程1. init进程启动过程开机键引导芯片从ROM加载BootLoader到RAM。引导BootLoader拉起Android OS。Linux内核启动,执行init.cpp的main函数,创建init进程。init进程中创建和挂载启动所需的文件目录,初始化属性服务、启动属性服务、解析init.rc配置文件并启动Zygote进程。2. Zygote进程启动过程Zygote进程Android系统中DVM(Dalvik虚拟机)和ART、应用程.

2020-11-17 22:50:58 38

转载 OpenGL ES 如何传输一个超大数组给着色器程序?

作者:字节流动如何传输一个超大数组给着色器程序?在 OpenGL ES 图形图像处理中,会经常遇到一种情况:将一个超大的数组传给着色器程序。目前常用的有三种方式:使用将数组加载到 2D 纹理的方式,然后使用 texelFetch 取数据;使用 uniform 缓冲区对象,即 UBO ;使用纹理缓冲区对象,即 TBO 。将数组加载到纹理使用将数组加载到纹理的方式来传输大数组,是最容易想到的一种方式。要想精确地换取每个像素的值,这个时候就不能使用采样函数 texture ,因为采样函数.

2020-11-17 22:39:30 24

原创 现在音视频前景怎么样?上船还来得及嘛?

前言从历史来看,2G打开了了移动互联网天下,3G带来了即时通信,诞生了QQ 微信等巨头,4G 带来了短视频兴起,字节跳动等公司崛起。2、3、4G的出现促成了移动互联网10年繁荣。而5G的出现,也会促成至少10年音视频行业的繁荣。现在随抖音、快手、B站等App软件的火热,用户量也在日益增加,我们可以看下图短视频用户规模及增长从上图可以看出用户量的不断增长,说明音视频的开发前景是广阔。对于很早看出音视频前景的同学来说,已经开始通过各种渠道搜集相关的学习资料,及早的投入音视频研发的队伍。**作为And

2020-11-17 22:21:24 113

原创 5G时代已经到来了,你还觉得Android行业凉了嘛?

前言随着移动时代、5G时代、物联网时代的大幕开启,它们对Android开发者们的开发技术要求越来越高,同时提升了Android开发者们的学习台阶和面试的门槛。在这个发展迅速的时代,Android程序员们该如何有效的提升自己的技能,才能紧跟着时代发展的步伐。有些Android开发者在遇到技术瓶颈的时候,没有找到方法去突破最终选择了转行,还为自己找了一个借口说:“因为Android 凉了,所以我选择了转行”。Android行业真的凉了吗?其实并没有,你可以想一想现在国内的手机市场,大多少都是国产手机

2020-11-17 22:06:36 80

原创 有Android开发者问:35 岁真的是分水岭吗?不妨来看看这16条建议

前言几年前见到类似的说法,这个数字还是30岁。估计再过几年,这个数字会变成40岁。其实长期来看,大家都会习惯,互联网和其他各个行业一样,所有行业存在的问题,互联网行业都会存在。什么是危机?感觉周围的人都在危机,甚至包括一些财富、地位都已经很高的人,仍然逃不过焦虑。似乎焦虑就是自然而然的,伴随着每个人的成长。焦虑多了,就开始危机了。一些感想:之前写过一篇回答,和这个问题很相关,我想持续写一些感想,可能对各年龄段的朋友都有一些帮助。其实很多人真到这个年龄,见识和能力都已经很厉害了,我帮忙总结一下.

2020-11-16 22:54:49 84

原创 从事Android开发这么多年了,你知道初、中、高级、资深及专家怎么划分嘛?

许多Android开发者做了几年的开发,还不知道自己处于那个等级,对未来比较迷茫,不知道自己技能该怎么提升,并且对于初级中级高级需要怎么进行学习,很多人都比较迷惑。为了方便大家成才,我把初级、中级、高级和资深四个常见的岗位需要掌握的一些知识进行如下整理。初级初级Android开发工程师的定义是掌握基础的Android知识,能够独立完成一个功能,工作年限大概在1-2年,这个层级大部分人通过看一些资料书籍再经过项目练习很快可以达到。这个级别的人往往需要掌握如下一些技能:掌握Android 四大组件.

2020-11-16 22:21:44 53

原创 Android音视频开发之——音频非压缩编码和压缩编码

音视频在开发中,最重要也是最复杂的就是编解码的过程,在上一篇的《Android音视频开发:踩一踩“门槛”》中,我们说音频的编码根据大小划分有两种:压缩编码和非压缩编码,那到底是怎么实现的这两中编码的呢?这一次就详细了解Android中如何使用这两种方式进行音频编码前景提要这里先回顾一下音频的压缩编码和非压缩编码:非压缩编码:音频裸数据,也即是我们所说的PCM压缩编码:对数据进行压缩,压缩不能被人耳感知到的冗余信号因为非压缩编码实在是太大了,所以我们生活中所接触的音频编码格式都是压缩编码,而.

2020-11-16 21:44:13 65

原创 Android音视频开发:踩一踩“门槛”

音视频行业已经发展很多年了,随着近几年移动端越来越多的音视频APP的出现,将音视频推向一个高潮,但是由于音视频的学习成本很高,很多开发者望而却步,为了跟紧时代的步伐,我写了这篇音视频基础,讲解了音视频的相关知识,给大家破除音视频的“高门槛”,希望可以共同进步。音频将声音保存成音频的过程,其实就是将模拟音频数字化的过程,为了实现这个过程,就需要对模拟音频进行采样、量化和编码。接下来我们详细讲解这一过程。采样采样是将信号从连续时间域上的模拟信号转换到离散时间域上的离散信号的过程(离散就是不连续),根.

2020-11-16 21:08:58 79

转载 BroadcastReceiver源码分析

作者:杨大嘴本文是对BroadcastReceiver发送和接收场景的源码分析。主要分析普通消息,有序消息和粘性消息原理差不多。一、BroadcastReceiver动态注册1.1 registerReceiver调用的是Context的registerReceiver我们查看App进程发起注册,ContextImpl的registerReceiver @Override public Intent registerReceiver(BroadcastReceiver receiver.

2020-11-15 22:01:22 15

原创 App启动优化-基于有向无环图的sdk初始化方案

1.背景1.1 在日常开发时经常会在Application的onCreate()方法中对三方SDK,或者自己封装的SDK进行初始化。class Application{ ... onCreate(){ initSDKA(); initSDKB(); initSDKC(); .... } ...}上面是通常写法,这里总结了几个信息点初始化耗时。整体都在主线程一条线程初始化。部分机型无法充分利用cpu资源。SDK依赖。部分sdk 存在顺序依赖关系。比如SD

2020-11-15 21:50:21 30

原创 阿里辞退35岁程序员,只因年龄太大?其实背后原因在自己身上

很早以前就有阿里的员工曝出自己因为年龄太大而被公司辞退,之后便一直流传35岁是程序员的一个坎,过了35岁的程序员很快就会被公司辞退。这也使得许多大型互联网公司的程序员十分焦虑,不知道自己一旦被公司辞退,该如何养活自己。确实随着年龄的增大,人的精力会渐渐降低,而许多互联网公司的节奏都非常快,要求员工能适应高强度的加班,这也是大家公认的被辞退的原因。也因此程序员成了人们口中的“青春饭”。然而站在公司的角度,原因却更加复杂。大龄程序员可能的确无法满足高强度的加班需求,但是同样这些程序员的工作经验都极为丰富

2020-11-15 21:34:31 52

原创 Jetpack系列——ViewModel

对于Android传统的代码编写方式,一般地,将页面UI的处理,数据的加载,全部放在Activity或Fragment中进行,但这并不满足“单一功能原则”,也不易于维护和扩展。我们应该将项目结构进行分层,传统的MVC,MVP和MVVM,都是将项目结构分了三层,“各管一摊”,这三种模式各有特点、各有利弊,但它们都有一个共同点,就是区分出了M层与V层,M即Model层,V即View层,M层负责数据的处理,View层负责UI的展示,不同的地方在于如何将M层与V层进行结合。其中,MVVM模式除了M层和V层之外,就

2020-11-13 22:39:50 25

原创 迟来的面试通知,开启了我去华为的面试之旅(Android开发岗)

今年因公司业绩不景气,最终倒闭了,而我也被迫失业,就只能老老实实在家呆着找工作。这一呆就才不多2个月,在这段时间内面试了差不多10家公司,有一半都给了Offer,但是没有想去的公司。原因:1、就是薪资给的少了点;2、薪资给的还可以,但是没有什么空间可以提升自己。经过一段时间朋友介绍,让我把简历投到华为去尝试一下,于是怀着侥幸的心理试了试。进入大厂想必都是每个开发者们最初的想法,而我也是一样。想想总是那么美好的????????????等待了两个多星期接收到了面试通知约,我三天后去面试,下面就是

2020-11-13 22:14:19 75

原创 从源码分析Handler面试问题

Handler 老生常谈的问题了,非常建议看一下Handler 的源码。刚入行的时候,大佬们就说 阅读源码 是进步很快的方式。Handler的基本原理Handler 的 重要组成部分Message 消息MessageQueue 消息队列Lopper 负责处理MessageQueue中的消息消息是如何添加到队列的对照着上面的大的逻辑图,我们深入一下,看一下,一个消息 是如何被发送到 MessageQueue 又是如何被 Lopper 处理的handler 发送一个message 的方法如

2020-11-12 22:19:32 59

原创 三年多的Android开发经验该如何进阶提升?我真的是太菜了

最近在知乎看见这么一则问答,说的是“自己做了三年的Android开发,还会觉得自己很菜”其实有许多开发者都有经历过,还有一些开发者可能正在尽力这段过程。以前刚出学校找工作的时候,是抱着一个学习的心态工作,经过岁月的磨练,天天就是复制粘贴,导致自己学习的心态就慢慢的放下了,等到自己想换工作跳槽的时候,才意识到这几年荒废了自己,技术不但没有什么提升反倒退不了。我们可以通过上图这个案例就可以看出,经过自己三年的颓废,通过跳槽面试才发现自己的技术水平是多么的差,面试官问的问题基本没几个能答的上来。我的技术水

2020-11-12 21:05:30 1078

原创 深入理解自定义ViewGroup的布局测量过程

要理解如何自定义一个viewgroup的测量和布局 其实不是一件容易的事。 多数人对自定义viewgroup的布局和测量的了解仅限于 网上随处可见的taglayout的写法(对taglayout还不清楚的同学 建议都去搜搜看)但是大部分人应该看完以后 也是懵逼的,不知道为什么应该这么写,导致这部分人以后对自定义一个viewgroup显的很没信心。今天这篇文章 就来告诉你 为什么taglayout 或者是其他viewgroup的 测量和布局 要那么写争取一篇文章把这个知识点搞透父view 到底是在什么时

2020-11-11 22:27:27 34

转载 Handler的初级、中级、高级问法,你都掌握了吗?

作者:伤心的猪大肠Handler是Android中的消息处理机制,是一种线程间通信的解决方案,同时你也可以理解为它天然的为我们在主线程创建一个队列,队列中的消息顺序就是我们设置的延迟的时间,如果你想在Android中实现一个队列的功能,不妨第一时间考虑一下它。本文分为三部分:Handler的源码和常见问题的解答一个线程中最多有多少个Handler,Looper,MessageQueue?Looper死循环为什么不会导致应用卡死,会耗费大量资源吗?子线程的如何更新UI,比如Dialog,To.

2020-11-11 18:08:01 70

原创 2020届毕业生的社招之旅,一举斩获滴滴Android开发岗OFFER

作者:李同学前言本人是2020年毕业于大工软院,刚开始工作是在TPLINK做Android开发。因一直有者一个大厂的梦,在得知要大厂在社招时,我决心裸辞掉了TPLINK的工作,开启我的社招面试之旅。下面是我在滴滴的面经分享面试过程(省略了自己介绍):一面(1小时20分钟)1.介绍项目(介绍了2个实习+1个项目,主要问了都有什么收获以及技术难点)2.了解android里的Map么(ArrayMap和SparseArray)3.了解java的·四种引用类型吗(弱引用一定是在内存不够的时候.

2020-11-11 17:36:29 1750 6

原创 Bitmap Drawable View 三者之间真正的联系和区别

这个问题很重要吗?这个问题还真的是很重要,想明白了 对你理解android的系统 是有好处的。否则每次遇到问题都去百度 真的很累。而且百度也无法告诉你这个问题的本质,多数人都是搜下bitmap和drawable 互转也就结束了。那到底三者之间有啥关系呢?bitmap: 仅仅就是一个位图 你可以理解为一张图片在内存中的映射。 就这么简单。这个很多人都知道view: 这个就是android的核心了,你看到的一切东西都是view 这个很多人也知道。 但是这个理解成都还不够,view最大的作用是2个 一

2020-11-10 22:22:44 32

原创 年龄不是阻碍自己学习的借口(Android开发岗)

人到中年不得已,保温杯里泡枸杞最近在看到一个问答,问的是“30岁转行做IT,来得及吗?”人们总是不停在问,我还来得及吗?孩子还来得及吗?人生还有转机吗?答案永远只有一个:来得及!先有80岁作画的摩西奶奶,后有79岁走红的王德顺爷爷,82岁的DJ纯子奶奶…今天,我们还要去认识一位84岁日本程序员奶奶。看了他们的故事,你会更加能体会一句话,人生没有太晚的开始,哪怕你白发苍苍,一切都还来得及。时间不能为我们设限,能否重新启程在于我们的勇气和决心。“活到老就要学到老,人生没有太晚这一说。”有没

2020-11-10 21:50:17 60 1

原创 这些Android核心知识点你知道吗?(建议收藏好)

目录:Java 基础&容器&同步&设计模式Java 虚拟机&内存结构&GC&类加载&四种引用&动态代理网络Android 基础&性能优化&FramworkAndroid 模块化&热修复&热更新&打包&混淆&压缩音视频&FFmpeg&播放器1、Java 基础&容器&同步&设计模式StringBuilder、StringBuffer

2020-11-09 22:04:25 48

转载 NDK | 说说 so 库从加载到卸载的全过程

作者:彭丑丑前言在 JNI 开发中,必然需要用到 so 库,那么你清楚 so 库从加载到卸载的全过程吗?在这篇文章里,我将带你建立对 so 库从加载进内存到卸载整个过程的理解。另外,文末的应试建议也不要错过哦,如果能帮上忙,请务必点赞加关注,这真的对我非常重要。目录1. 获取 so 库关于 获取 so 库的具体步骤,我在这篇文章里讨论,《NDK | 一篇文章开启你的 NDK 技能树》,请关注。通常来说,最终生成的 so 库命名为lib[name].so,例如系统内置的 so 库:.

2020-11-09 18:13:55 43

FreScon源码下载|Fresco源码.rar

FreScon源码下载

2016-06-30

jbox2d愤怒的小鸟游戏源码|愤怒的小鸟游戏源码.zip

jbox2d愤怒的小鸟游戏源码

2016-07-14

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

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 = (TimePickerDialog) getSupportFragmentManager().findFragmentByTag(TIMEPICKER_TAG); if (tpd != null) { tpd.setOnTimeSetListener(this); } } } private boolean isVibrate() { return ((CheckBox) findViewById(R.id.checkBoxVibrate)).isChecked(); } private boolean isCloseOnSingleTapDay() { return ((CheckBox) findViewById(R.id.checkBoxCloseOnSingleTapDay)).isChecked(); } private boolean isCloseOnSingleTapMinute() { return ((CheckBox) findViewById(R.id.checkBoxCloseOnSingleTapMinute)).isChecked(); } @Override public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) { Toast.makeText(MainActivity.this, "new date:" + year + "-" + month + "-" + day, Toast.LENGTH_LONG).show(); } @Override public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) { Toast.makeText(MainActivity.this, "new time:" + hourOfDay + "-" + minute, Toast.LENGTH_LONG).show(); } }

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关注的人 TA的粉丝

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