Android仿微信实现快速索引选择联系人

原创 2016年03月05日 13:19:20
  • 1640
  • 3
  • 1

一.概述

先看效果图,然后在给大家慢慢介绍 

二.实现

先给大家说说这些城市的数据是怎么来的,在实际开发中,一般都是从服务器里获取过来的,这里因为不太方便,我就没有使用服务器,而是直接把数据保存在了本地文件,模拟访问服务器然后去读取数据,过程不重要,我们这里重点是获得数据,我们先看看这些数据放在哪吧。 
 
在当前项目的assets目录下,我们放入了json格式的数据,我们打开看一下

{"state": 1,"datas": [{"id": "820","name": "安阳","sortKey": "A"},{"id": "68","name": "安庆","sortKey": "A"},{"id": "1269","name": "鞍山","sortKey": "A"},{"id": "22","name": "蚌埠","sortKey": "B"},{"id": "1372","name": "包头","sortKey": "B"},{"id": "2419","name": "北京","sortKey": "B"},{"id": "649","name": "保定","sortKey": "B"},{"id": "1492","name": "宝鸡","sortKey": "B"},

由于数据比较多,我们只列举了一部分,其他格式完全相同,我们就把这个当做从模拟器获取到的数据,然后进行操作。我们如何操作呢,这里写了一个类

public class AppJsonFileReader {/*** 获取json文件中的数据* @param context* @param fileName* @return json字符串数据*/public static String getJson(Context context,String fileName){StringBuilder builder = new StringBuilder();AssetManager manager = context.getAssets();try {InputStream stream = manager.open(fileName);BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream));String line = null;while((line = bufferedReader.readLine())!=null){builder.append(line);}} catch (IOException e) {e.printStackTrace();}return builder.toString();}/*** 从json字符串数据中获取对象集合* @param str* @return 对象集合*/public static List<City> setData(String str){List<City> list = new ArrayList<>();City city ;try {JSONObject result = new JSONObject(str);JSONArray array = result.getJSONArray("datas");int len = array.length();for (int i = 0; i <len ; i++) {JSONObject object = array.getJSONObject(i);city = new City();city.setId(object.getString("id"));city.setName(object.getString("name"));city.setSortKey(object.getString("sortKey"));list.add(city);}} catch (JSONException e) {e.printStackTrace();}return list;}
}

接下来我们先看布局文件:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="example.lxn.com.app.view.CityIndex"><RelativeLayout
        android:id="@+id/rlTop"android:layout_width="match_parent"android:padding="10dp"android:background="#FF6633"android:layout_height="wrap_content"><TextView
            android:layout_width="wrap_content"android:layout_height="wrap_content"android:textColor="#FFFFFF"android:text="选择城市"android:textSize="20sp"android:layout_centerInParent="true"/></RelativeLayout><ListView
        android:id="@+id/cityList"android:layout_below="@+id/rlTop"android:scrollbars="none"android:layout_width="match_parent"android:layout_height="wrap_content"></ListView><example.lxn.com.app.view.SliderBar
        android:id="@+id/sliderBar"android:layout_below="@+id/rlTop"android:layout_alignParentRight="true"android:layout_width="40dp"android:layout_height="match_parent" /><TextView
        android:id="@+id/tv_center"android:layout_width="100dp"android:gravity="center"android:textSize="60sp"android:visibility="invisible"android:textColor="#ffffff"android:layout_height="100dp"android:layout_centerInParent="true"android:background="@drawable/text_index"/>
</RelativeLayout>

我们的重点在这个SliderBar上面,这是一个自定义的View,接下来看如何实现: 
我们先来画字母,这里的难点是确定每个字母的位置,我们先来看一张图 
 
Android中绘制文字时,默认的起始位置是当前坐标的左下角,也就是图中字母A的左边第一个点,这个位置我们可以设置,一共有三个位置

         paint.setTextAlign(Paint.Align.CENTER);//文字中间paint.setTextAlign(Paint.Align.LEFT);//文字左边paint.setTextAlign(Paint.Align.RIGHT);//文字右边

在这里我们会将起始点左边设为中间位置,也就是图中的第二个红点,便于计算。

那么第二个红点的坐标如何确定呢,我们先看x坐标,因为我们的字母是绘制在view之中的,我们很容易看出当前其实的x坐标为当前view宽度的一半。 
然后我们看y坐标,这个稍微有点复杂,假设图中的每个黑色格子代表文字的绘制区域,大家看那条蓝色的线,就是格子的平分线,那么第一个字母的y坐标就等于格子的一半加上文字高度的一半,格子的一半我们可以计算出来,因为每个格子的高度等于view的总高度除以字母的个数,然后我们就可以得到每个格子的高度,然后除以2。

接下来我们看如何得到每个文字高度的一半,我们通过下面这个函数来获取

public void getTextBounds(String text, int start, int end, Rect bounds)

这个函数的作用是得到文字所在矩形的宽和高,也就是图中字母C周围的那个蓝色的矩形,这个函数有四个参数,第一个参数为要获得边界的文字,第二个参数为文字长度的起始位置,第三个参数为文字长度的结束位置,最后一个参数为一个Rect对象,当我们调用这个函数以后就把当前文字所在矩形的宽和高存到了第四个参数Rect中。

经过上面的讲解,我们就应该知道如何获得当前文字所要绘制的起点y坐标了吧。下面给出代码:

public class SliderBar extends View {private String[] siderBar = { "热门", "A", "B", "C", "D", "E", "F", "G", "H", "J","K", "L", "M", "N", "Q", "S", "T", "W", "X", "Y", "Z" ,"#"};private Paint paint = new Paint();//创建画笔对象private Context context;public SliderBar(Context context) {super(context);this.context = context;}public SliderBar(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;}@Overrideprotected void onDraw(Canvas canvas) {paint.setColor(Color.BLACK);//设置画笔颜色为黑色paint.setTypeface(Typeface.DEFAULT_BOLD);//默认字体为粗体paint.setAntiAlias(true);//反锯齿paint.setTextSize(DisplayUtil.px2sp(context, 15));//设置姚绘制的文字大小paint.setTextAlign(Paint.Align.CENTER);//设置绘制的起点为中心int height = getHeight();//当前view的高度,用于计算每个文字占用的高度int width = getWidth();//当前viewde宽度int eachHeight = height/siderBar.length;//每个字母的高度 = 总高度/文字个数for (int i = 0 ; i < siderBar.length; i++) {Rect bounds = new Rect();paint.getTextBounds(siderBar[i],0,siderBar[i].length(),bounds);float x = width/2;float y = eachHeight/2+ bounds.height()/2+ i*eachHeight;canvas.drawText(siderBar[i],x,y,paint);//根据x,y坐标画出当前文字}}

这时候我们看一下效果图 
 
此时已经正确画出了字母,

接下来我们为ListView填充数据,也就是显示出左边的城市信息。我们先写出adapter,这里的难点事如何显示如一个字母以及下面的城市信息,我们这里的思路是把索引和当前城市的信息看做一个item,然后代码里面动态去判断是否要显示当前城市的索引。

public class CityAdapter extends BaseAdapter {private Context context;private List<City> list;//存放索引的信息StringBuilder builder = new StringBuilder();//存放城市的名称private List<String> nameList = new ArrayList<>();public CityAdapter(Context context,List<City> list){this.context = context;this.list = list;}@Overridepublic int getCount() {return list.size();}@Overridepublic City getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){holder = new ViewHolder();convertView = View.inflate(context,R.layout.city_list_item_view,null);holder.index = (TextView) convertView.findViewById(R.id.tv_index);holder.name = (TextView) convertView.findViewById(R.id.tv_city);convertView.setTag(holder);}else{holder = (ViewHolder) convertView.getTag();}City city = list.get(position);String sortKey=  city.getSortKey();String cityName = city.getName();//当前索引不存在,添加索引和城市名称if(builder.indexOf(sortKey)==-1){builder.append(sortKey);nameList.add(cityName);}//城市名称存在,显示索引if(nameList.contains(cityName)){holder.index.setVisibility(View.VISIBLE);holder.index.setText(sortKey);}else{holder.index.setVisibility(View.GONE);}holder.name.setText(cityName);return convertView;}public class ViewHolder{public TextView name;public TextView index;}
}

接着我们来获取数据,显示数据

public class MainActivity extends ActionBarActivity {private ListView listView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listView = (ListView) findViewById(R.id.cityList);//执行获取数据的任务new GetJsonTask().execute();}public class GetJsonTask extends AsyncTask<Void,Void,String>{@Overrideprotected String doInBackground(Void... params) {//读取json数据String result = AppJsonFileReader.getJson(MainActivity.this,"city.json");return result;}@Overrideprotected void onPostExecute(String s) {super.onPostExecute(s);//处理数据listView.setAdapter(new CityAdapter(MainActivity.this,AppJsonFileReader.setData(s)));}}
}

然后我们看效果图 

接下来我们写最后的一步,为字母所在的view添加触摸事件并且控制定位左边联系人,这里我们使用回调,直接上代码

public class SliderBar extends View {private String[] siderBar = { "热门", "A", "B", "C", "D", "E", "F", "G", "H", "J","K", "L", "M", "N", "Q", "S", "T", "W", "X", "Y", "Z" ,"#"};Paint paint = new Paint();private Context context;private OnTouchLetterChangedListener listener;public interface OnTouchLetterChangedListener{void onTouchLetterChanged(String s);}public void setOnTouchLetterChangedListener(OnTouchLetterChangedListener listener){this.listener = listener;}public SliderBar(Context context) {super(context);this.context = context;}public SliderBar(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);paint.setAntiAlias(true);paint.setTypeface(Typeface.DEFAULT_BOLD);paint.setColor(Color.BLACK);paint.setTextAlign(Paint.Align.CENTER);paint.setTextSize(15);int height = getHeight();int width = getWidth();int eachHeight = height/siderBar.length;for(int i = 0;i<siderBar.length;i++){Rect rect = new Rect();paint.getTextBounds(siderBar[i],0,siderBar[i].length(),rect);float x = width/2;float y = eachHeight/2+rect.height()/2+i*eachHeight;canvas.drawText(siderBar[i],x,y,paint);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {int action = event.getAction();float y = event.getY();int index = (int) (y/getHeight()*siderBar.length);switch (action){case MotionEvent.ACTION_UP:setBackgroundResource(android.R.color.transparent);invalidate();break;default:setBackgroundResource(R.drawable.text_index);if(index>0&&index<siderBar.length){if(listener!=null){listener.onTouchLetterChanged(siderBar[index]);invalidate();}}break;}//注意,这里必须要返回true,目的就是不让listview响应触摸事件,因为我们要通过滑动控制listview的显示return true;}
}
public class MainActivity extends Activity implements SliderBar.OnTouchLetterChangedListener {private ListView listView;private List<City> list;private TextView tvCenter;private SliderBar sliderBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listView = (ListView) findViewById(R.id.cityList);tvCenter = (TextView) findViewById(R.id.tv_center);sliderBar = (SliderBar) findViewById(R.id.sliderBar);sliderBar.setOnTouchLetterChangedListener(this);new GetJsonTask().execute();}@Overridepublic void onTouchLetterChanged(String s) {int index = findIndex(list, s);if(index!=-1) {listView.setSelection(index);showText(s);}}/*** 查找选中字母在集合中的位置* @param list* @param s* @return*/public int findIndex(List<City> list,String s){for(int i = 0;i<list.size();i++){City city = list.get(i);if(city.getSortKey().equals(s)){return i;}}return -1;}/*** 在屏幕中央显示选中的文字* @param text*/public void showText(String text){tvCenter.setVisibility(View.VISIBLE);tvCenter.setText(text);new Handler().postDelayed(new Runnable() {@Overridepublic void run() {tvCenter.setVisibility(View.GONE);}},200);}public class GetJsonTask extends AsyncTask<Void,Void,String>{@Overrideprotected String doInBackground(Void... params) {String result = AppJsonFileReader.getJson(MainActivity.this,"city.json");return result;}@Overrideprotected void onPostExecute(String s) {super.onPostExecute(s);list = AppJsonFileReader.setData(s);listView.setAdapter(new CityAdapter(MainActivity.this,list));}}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

Android仿微信实现快速索引选择联系人相关推荐

  1. android 仿微信联系人 首字母分组快速索引

    总结是一种习惯,不能停,一停人就懒了,都快一个月没有写了!该提提神了! 进入正题:android 仿微信联系人 首字母快速索引,先用下美团的索引效果图: 1.自定义View字母索引栏(右边那一列): ...

  2. Android仿微信添加联系人列表,内附有截图和demo源码

    最新demo地址,仿微信添加联系人WXAddPersonDemo 分享一个Android仿微信选择联系人页面 之前做的App主要是工具类的,而且公司的产品经理也喜欢在App里设计很多自定义控件,所以比 ...

  3. android 仿微信demo————微信通讯录界面功能实现(移动端,服务端)

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  4. php仿微信底部菜单,Android实现简单底部导航栏 Android仿微信滑动切换效果

    Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...

  5. android仿微信的activity平滑水平切换动画,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    Android实现简单底部导航栏 Android仿微信滑动切换效果 发布时间:2020-10-09 19:48:00 来源:脚本之家 阅读:96 作者:丶白泽 Android仿微信滑动切换最终实现效果 ...

  6. android滑动菜单图标,Android实现简单底部导航栏 Android仿微信滑动切换效果

    Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...

  7. android 底部滑动效果怎么做,Android实现简单底部导航栏 Android仿微信滑动切换效果...

    android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义view配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...

  8. android 底部tab效果,Android 仿微信底部渐变Tab效果

    先来看一下效果图 除了第三个的发现Tab有所差别外,其他的基本还原了微信的底部Tab渐变效果 每个Tab都是一个自定义View,根据ImageView的tint属性来实现颜色渐变效果,tint属性的使 ...

  9. Android仿微信通讯录

    Android仿微信通讯录 分3部: 1.listview实现显示头像.名字(太简单,这里就不写了) 通讯录页面xml布局代码: <LinearLayout xmlns:android=&quo ...

最新文章

  1. [ZZ]知名互联网公司Python的16道经典面试题及答案
  2. 在线和本地两种方法构建 RAxML 进化树方法和解读
  3. You are what you say!
  4. 【必看】 一篇 CPU 占用高,导致请求超时的故障排查
  5. 论文阅读: Direct Monocular Odometry Using Points and Lines
  6. java 创建string对象机制 字符串缓冲池 字符串拼接机制 字符串中intern()方法...
  7. Could not execute query against OLE DB provider 'OraOLEDB.Oracle'
  8. 《Linux就该这么学》培训笔记_ch06_存储结构与磁盘划分
  9. 简述计算机图形的图形应用主要有哪些,5计算机图形学考试简答题复习.doc
  10. Tips/Tricks#3:利用JavaScript选择GridView行
  11. android布局共享,android布局属性详解分享
  12. 【opencv-python】 cv2.subtract(...)图片减法
  13. 1 区 IF:5+ | JGG 专刊征稿:人体微生物组
  14. 什么是物联网?有哪些应用?终于有人讲明白了
  15. 使用计算机结束时断开终端的连接属于什么,计算机结束时断开终端的连接属于什么...
  16. 前端开发之SEO(搜索引擎优化)
  17. 基于STC89C52单片机的蔬菜大棚实时温度测量控制系统
  18. 非线性规划 - 数学建模
  19. 网站建设需要要考虑到哪些细节
  20. Linux C++ TCP编程

热门文章

  1. 计算机桌面组成部分教案,计算机教案模板
  2. 相同点安卓和iosui的相同点_iOS 8和安卓L详尽界面对比 风格有些相似?
  3. java计算机毕业设计学生就业创业管理系统源程序+mysql+系统+lw文档+远程调试
  4. 关于vue2与vue3
  5. android将内容分享到QQ和微信
  6. 解析json文件、执行批量修改sql
  7. windows server : windows server backup 服务(自动定时备份,一次备份,恢复),安装搭建恢复(图形化)
  8. 小米推送点击无效的原因
  9. 未发现缺陷(NDF)定义及预防
  10. matlab中run按钮是灰色的,Android Studio中Run按钮是灰色的快速解决方法