通信录列表+复杂Adapter分析
来源:互联网 发布:c语言获取当前时间 编辑:程序博客网 时间:2024/05/16 10:50
概述
最近写论文之余玩起了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中稍微做了一点改写
我的githubSideSelectBar.java
主要将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通信录操作
- 通信录操作
- Android通信录
- iOS6访问通信录
- 读取通信录
- Android 通信录操作
- 管理Android通信录
- 个人简单通信录
- android----通信录操作
- 通信录的访问
- 部分数据前台显示为null
- #ifdef,#else,#endif,#if用法详解
- poj 1276 多重背包+二进制
- git rebase
- 算法复习2-3种初级排序
- 通信录列表+复杂Adapter分析
- IOS TextField 输入银行卡号格式化(每四位中间空一格)
- python下spark_RDD认识与操作
- hdfs 查看文件分布所属块
- 基于Qt4.7的ADC测试程序
- Android学习之解决多个Fragment切换时重新实例化的问题
- leetcode52 N-Queens II
- bzoj1412[狼和羊的故事]最小割
- 总结