通信录列表+复杂Adapter分析
概述
最近写论文之余玩起了github,发现有个citypicker挺不错的,高仿了美团城市选择和定位的一些功能
地址链接 效果图如下:
自己手动写了一遍优化了一些内容,学到了一些姿势,下面对其中一些技术点做下总结。
- 清晰的结构
- SideLetterBar实现城市列表
- 如何显示字母浮窗
- 复杂的Adapter
- ScrollView中嵌入ListView,GridView冲突的解决
清晰的结构
一般看到一个项目之前我会先看下他的结构规划,学习一下高手们架构上的意识,下面是目录结构
从这里清晰的看出MVC的模型,将工具类util、自定义view都进行了规整划分,这里由于只有一个Activity因此没有用单独的package,一般在项目大的时候还要单独划分,还有对应的网络模块
SideLetterBar.java
这个是仿通信录的的自定义view,主要涉及到了 拦截事件的分发处理、ondraw()方法、经典的回调机制
(关于回调可以参考android中的回调机制)
非常非常适合新手学习事件处理时的练习材料!
- 拦截事件的分发机制
当用于点击到这个区域时,事件分发时需要拦截该ActionDown事件,处理流程就是:点击那里就会出现一个字母的overlay,ActionMove时字母随之改变,ActionUp时字母的overlay就会消失,处理完return true表示该事件被本view消耗了,事件结束。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();
final int oldChoose = choose;
final OnLetterChangedListener listener = onLetterChangedListener;
final int c = (int) (y / getHeight() * b.length);
switch (action) {
case MotionEvent.ACTION_DOWN:
showBg = true;
if (oldChoose != c && listener != null) {
if (c >= 0 && c < b.length) {
listener.onLetterChanged(b[c]);
choose = c;
invalidate();
if (overlay != null){
overlay.setVisibility(VISIBLE);
overlay.setText(b[c]);
}
}
}
break;
case MotionEvent.ACTION_MOVE:
if (oldChoose != c && listener != null) {
if (c >= 0 && c < b.length) {
listener.onLetterChanged(b[c]);
choose = c;
invalidate();
if (overlay != null){
overlay.setVisibility(VISIBLE);
overlay.setText(b[c]);
}
}
}
break;
case MotionEvent.ACTION_UP:
showBg = false;
choose = -1;
invalidate();
if (overlay != null){
overlay.setVisibility(GONE);
}
break;
}
return true;
}
从上面可以清晰看出这里使用event.getAction()来获得点击事件,然后通过switch分别处理,这里要说一下通过点击位置获取准确字符的过程,关键代码及注释
final float y = event.getY();//获取当前像素长度
……
final int c = (int) (y / getHeight() * b.length);//通过当前像素长度/总像素长度*数组长度=当前点击在数组的index
- onDraw()方法
该方法主要做俩件事,第一件将A-Z的字母绘绘制到控件中,第二件,点击时将控件背景颜色变化(这里选了透明色)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (showBg) {
canvas.drawColor(Color.TRANSPARENT);
}
int height = getHeight();
int width = getWidth();
int singleHeight = height / b.length;
for (int i = 0; i < b.length; i++) {
paint.setTextSize(getResources().getDimension(R.dimen.side_letter_bar_letter_size));
paint.setColor(getResources().getColor(R.color.gray));
paint.setAntiAlias(true);
if (i == choose) {
paint.setColor(getResources().getColor(R.color.gray_deep));
paint.setFakeBoldText(true); //加粗
}
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();
}
}
这里核心代码是如何找到每个字母的绘制位置,关键代码如下:
int singleHeight = height / b.length;//找出每个字母在当前手机上占的像素大小
……
float xPos = width / 2 - paint.measureText(b[i]) / 2;//x的起始位置,计算了字符的宽度
float yPos = singleHeight * i + singleHeight;//for循环从0开始这里要为i+1
canvas.drawText(b[i], xPos, yPos, paint);//画出字符
但是我在csdn上随便一找,便看到2012年有人转载过这个控件文章,作者直接拿来用了,没做啥改动,这里的代码明显有些地方不够优雅,比如在ondraw中的for循环中每次都要设置画笔大小颜色啥的,变量定义也不明确比如ondraw中的c是什么鬼。。虽然无伤大雅但是看着总有些不舒服,这个控件在我的github中稍微做了一点改写
主要将paint画笔初始化位置做了改变、规范了变量,做了一些注释。
如何显示字母浮窗
主要使用overlay覆盖上去,在SideLetterBar 中留了一个函数
/**
* 设置悬浮的textview
* @param overlay
*/
public void setOverlay(TextView overlay){
this.overlay = overlay;
}
然后在activty_city_list.xml布局文件中定义了一个长宽一致固定的textview,android:id=”@+id/tv_letter_overlay”先把属性设置为gone,在CityPickerActivity中获取该空间通过setOverlay传到sideLetterBar中,在ActionDown处理中将该空间设置为visible然后通过settext设置对应的字母
case MotionEvent.ACTION_DOWN:
showBg = true;
if (oldChoose != c && listener != null) {
if (c >= 0 && c < b.length) {
listener.onLetterChanged(b[c]);
choose = c;
invalidate();
if (overlay != null){
overlay.setVisibility(VISIBLE);
overlay.setText(b[c]);
}
}
}
break;
复杂的Adapter
在之前的博客中也介绍过这方面的内容
ListView中ConvertView和ViewHolder,
一般是继承BaseAdapter然后遵循以下几步:
1.继承BaseAdapter
2.实现getCount()
,getItem()
,getItemId()
,getView()
3.写一个ViewHolder内部类去存储复用的View
4.在getView()
中实现数据的设置
这里要谈的是多个布局同时出现在一个listview中,如上图中在一个listview中出现了3个布局!如何做到的?
这时候就需要
重写 getViewTypeCount() – 返回你有多少个不同的布局
重写 getItemViewType(int) – 由position返回view type id
然后在getview中
int viewType = getItemViewType(position);//获取对应的类型
然后通过switch case 不同的viewType在getView中convertView(即getView的第二个参数代码中为view)使用的不同的布局文件,简要代码如下:
@Override
public View getView(final int position, View view, ViewGroup parent) {
CityViewHolder holder;
int viewType = getItemViewType(position);
switch (viewType){
case 0: //定位
view = inflater.inflate(R.layout.view_locate_city, parent, false);
ViewGroup container = (ViewGroup) view.findViewById(R.id.layout_locate);
……
});
break;
case 1: //热门
view = inflater.inflate(R.layout.view_hot_city, parent, false);
WrapHeightGridView gridView = (WrapHeightGridView) view.findViewById(R.id.gridview_hot_city);
// GridView gridView = (GridView) view.findViewById(R.id.gridview_hot_city);
……
});
break;
case 2: //所有
if (view == null){
view = inflater.inflate(R.layout.item_city_listview, parent, false);
……
});
}
break;
}
return view;
}
可以看出不同的case中通过inflate使用了不同的布局文件,关于inflate可以参看以前写的一篇博客
LayoutInflater和inflate的用法,有图有真相
ScrollView中嵌入ListView,GridView冲突的解决
可以看到作者在处理处理GridView时候特意对控件的高做了重写,否则gridview显示不全的,代码如下
public class WrapHeightGridView extends GridView {
public WrapHeightGridView(Context context) {
this(context, null);
}
public WrapHeightGridView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapHeightGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightSpec);
}
}
这里关键代码
int heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);//表示测量格式的高最大是无穷大,因此按照实际情况完全显示出来
结束!
第一次尝试markdown感觉还不赖~
参考文章
http://blog.csdn.net/guozh/article/details/7568668 (2012年的仿通信录代码)
http://blog.csdn.net/jdsjlzx/article/details/8273661 (adapter)
http://www.aitinan.com/3885.html (adapter)
通信录列表+复杂Adapter分析的更多相关文章
- 管理Android通信录
Android提供了Contacts应用程序来管理联系人,并且Android系统还为联系人管理提供了ContentProvider,这就同意其他应用程序以ContentResolver来管理联系人数据 ...
- 黎活明8天快速掌握android视频教程--22_访问通信录中的联系人和添加联系人
Android系统中联系人的通讯录的contentProvide是一个单独的apk,显示在界面的contact也是一个独立的apk,联系人apk通过contentProvide访问底层的数据库. 现在 ...
- 非智能手机通信录备份并还原至Android智能手机方法
随着智能手机早已深入普通用户的生活,2-3线城市的用户也逐渐从使用非智能机换成使用智能机.最近便遇见了这样一个转移通讯录的需求.之前使用的手机型号是BBK K201,通信录中绝大部分保存在了手机中,最 ...
- (五)backbone - DEMO - 通信录改造之使用requirejs
DEMO介绍是 DEMO通信录的扩展,使用requirejs模块化整合 大体实现 • model文件 model/contact.js define(function (){ // user cont ...
- 今天研究了一下手机通信录管理系统(C语言)
题目:手机通信录管理系统 一.题目要求 二.需求分析 三.设计步骤/编写代码 四.上机/运行结果 五.总结 一.题目要求 模拟手机通信录管理系统,实现对手机中的通信录进行管理操作.功能要求: (1)查 ...
- 优雅的实现多类型列表的Adapter
1引言 在开发中经常会遇到,一个列表(RecyclerView)中有多种布局类型的情况.前段时间,看到了这篇文章 [译]关于 Android Adapter,你的实现方式可能一直都有问题(http:/ ...
- iOS:通信录(完成)(18-01-18更)
1.读取通信录 1).9.0以前:AddressBook 2).9.0以后:Contacts 2.调用通信录UI(不弄) 1).9.0以前:AddressBookUI 2).9.0以后:Contact ...
- arm上的参数列表传递的分析(以android为例)
1. Linux中可变列表实现的源码分析 查看Linux0.11的内核源代码,对va_list, va_start, va_arg 的实现如下: va_list的实现没有差别,chartypedef ...
- Cocos2d-X3.0 刨根问底(五)----- Node类及显示对象列表源码分析
上一章 我们分析了Cocos2d-x的内存管理,主要解剖了 Ref.PoolManager.AutoreleasePool这三个类,了解了对象是如何自动释放的机制.之前有一个类 Node经常出现在各种 ...
随机推荐
- java虚拟机 jvm 局部变量表实战
java局部变量表是栈帧重要组中部分之一.他主要保存函数的参数以及局部的变量信息.局部变量表中的变量作用域是当前调用的函数.函数调用结束后,随着函数栈帧的销毁.局部变量表也会随之销毁,释放空间. 由于 ...
- Retrofit2.0 ,OkHttp3完美同步持久Cookie实现免登录(二)
原文出自csdn: http://blog.csdn.net/sk719887916/article/details/51700659: 通过对Retrofit2.0的<Retrofit 2.0 ...
- 用API给用户添加职责
DECLARE lc_user_name VARCHAR2(100) := 'PRAJ_TEST'; lc_resp_appl_short_name VARCHAR2(100) := 'FND' ...
- android开源库发布到jcenter图文详解与填坑
相信很多人都用过开源项目,特别是android studio普及以后,使用开源库更方便简单.而如何上传开源库到jcenter供大家方便使用,虽然网上也有教程,但还是遇坑了,最后总结一下,希望可以帮助大 ...
- RMI方式Ehcache集群的源码分析
Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示: Ehcache支持多种集群方式,下面以RMI通信方式为例,来具体分 ...
- java设计模式---状态模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述状态(State)模式的: 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为 ...
- RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)
在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇 ...
- Linux进程实践(3) --进程终止与exec函数族
进程的几种终止方式 (1)正常退出 从main函数返回[return] 调用exit 调用_exit/_Exit (2)异常退出 调用abort 产生SIGABOUT信号 由信号终止 Ctrl+ ...
- 【Unity Shaders】Using Textures for Effects——通过修改UV坐标来滚动textures
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 【linux学习笔记】在ubuntu下使用QT Cmake支持C++11
今天在ubuntu下使用QT来进行C++编程,选择了Cmake,当用到initializer_list的时候提示不支持C++11,现提供一下解决方案: 错误提示: error: This file r ...