自定义View学习之12/4(仿IOS联系人列表)
来源:互联网 发布:下载商城软件 编辑:程序博客网 时间:2024/05/20 05:26
今天我们来仿一个IOS联系人列表,首先得支持字母行置顶(有阴影和没阴影置顶)。支持右边字母视图点击和滑动到置顶的字母行。搜索栏支持中英文搜索。有了这个需求,我们现在就得开始动手做。
动手前我们得理清思路:
1、需要重写一个有置顶功能的列表控件;
2、需要写一个右边字母控件竖排视图;
3、支持中英文就得把中文转成拼音,这里我用了google系统用的HanziToPinyin这个类来做。
4、然后我们得有一个全部联系人的集合。一个显示UI联系人的集合和一个每个联系人的首字母第一次出现的Map集合来保存下标;
5、先贴下动态图,马上开始动手做;
首先我们先写右边的字母视图代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):
package com.example.phonedemo;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;public class LetterIndexView extends LinearLayout { /** * 上下文环境 */ private Context context; /** * 字母控件 */ private TextView[] lettersTxt = new TextView[28]; /** * 触碰字母索引接口 */ private OnTouchLetterIndex touchLetterIndex; public LetterIndexView(Context context) { super(context); this.context = context; } public LetterIndexView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; } /** * 初始化控件. */ public void init(OnTouchLetterIndex touchLetterIndex) { this.touchLetterIndex = touchLetterIndex; this.setBackgroundColor(getResources().getColor(R.color.transparent)); this.setOrientation(LinearLayout.VERTICAL); this.setGravity(Gravity.CENTER); //创建字母控件实例 for (int i = 0; i < 28; i++) { lettersTxt[i] = new TextView(context); lettersTxt[i].setGravity(Gravity.CENTER); char tab = (char) (i + 63); if (i == 0) lettersTxt[i].setText("!"); else if (i == 1) lettersTxt[i].setText("#"); else lettersTxt[i].setText("" + tab); lettersTxt[i].setPadding(10, 0,10, 0); lettersTxt[i].setBackgroundColor(0xFF0000); lettersTxt[i].setTextSize(12); lettersTxt[i].setTextColor(Color.BLACK); LayoutParams letterParam = new LayoutParams(LayoutParams.WRAP_CONTENT, 0); letterParam.weight = 1; lettersTxt[i].setLayoutParams(letterParam); this.addView(lettersTxt[i]); } this.setOnTouchListener(new OnTouchListener() { //移动y轴的距离 private int y; //控件的高度 private int height; //按到了哪个字母 private String tab; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //按下时改变背景和字体颜色 setTextColor(Color.WHITE); LetterIndexView.this.setBackgroundColor(getResources().getColor(R.color.ff7c7c7c)); case MotionEvent.ACTION_MOVE: // 获取触发事件点的纵坐标 y = (int) event.getY(); height = LetterIndexView.this.getHeight(); int location = (int) (y / (height / 28) + 0.5f); if (location == 0) { tab = "!"; } else if (location == 1) { tab = "#"; } else if (location > 0 && location <= 27) { tab = String.valueOf((char) (location + 63)); } if (LetterIndexView.this.touchLetterIndex!=null) { //调用接口 LetterIndexView.this.touchLetterIndex.touchLetterWitch(tab); } break; case MotionEvent.ACTION_UP: LetterIndexView.this.setBackgroundColor(getResources().getColor(R.color.transparent)); if (LetterIndexView.this.touchLetterIndex!=null) { //调用接口 LetterIndexView.this.touchLetterIndex.touchFinish(); } setTextColor(Color.BLACK); break; } return true; } }); } /** * 设置字体颜色 */ private void setTextColor(int color) { for (int i = 0; i < 28; i++) { lettersTxt[i].setTextColor(color); } } /** * 触碰字母索引接口 */ public interface OnTouchLetterIndex { /** * 触摸字母空间接口. */ void touchLetterWitch(String letter); /** * 结束查询 */ void touchFinish(); }}
由于时间原因自己也没研究置顶功能的listview,于是我拿的网上开源控件这里就不贴出来了。主界面代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):
package com.example.phonedemo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.text.Editable;import android.text.TextUtils;import android.text.TextWatcher;import android.view.View;import android.widget.AdapterView;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import java.text.Collator;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;public class MainActivity extends Activity { /** * 搜索栏 */ EditText edit_search; /** * 列表 */ PinnedSectionListView listView; /** * 右边字母列表 */ LetterIndexView letterIndexView; /** * 中间显示右边按的字母 */ TextView txt_center; /** * 所有名字集合 */ private ArrayList<PhoneBean> list_all; /** * 显示名字集合 */ private ArrayList<PhoneBean> list_show; /** * 列表适配器 */ private PhoneAdapter adapter; /** * 保存名字首字母 */ public HashMap<String, Integer> map_IsHead; /** * item标识为0 */ public static final int ITEM = 0; /** * item标题标识为1 */ public static final int TITLE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); edit_search = (EditText) findViewById(R.id.edit_search); listView = (PinnedSectionListView) findViewById(R.id.phone_listview); letterIndexView = (LetterIndexView) findViewById(R.id.phone_LetterIndexView); txt_center = (TextView) findViewById(R.id.phone_txt_center); initView(); initData(); } private void initView() { // 输入监听 edit_search.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { list_show.clear(); map_IsHead.clear(); //把输入的字符改成大写 String search = editable.toString().trim().toUpperCase(); if (TextUtils.isEmpty(search)) { for (int i = 0; i < list_all.size(); i++) { PhoneBean bean = list_all.get(i); //中文字符匹配首字母和英文字符匹配首字母 if (!map_IsHead.containsKey(bean.getHeadChar())) {// 如果不包含就添加一个标题 PhoneBean bean1 = new PhoneBean(); // 设置名字 bean1.setName(bean.getName()); // 设置标题type bean1.setType(MainActivity.TITLE); list_show.add(bean1); // map的值为标题的下标 map_IsHead.put(bean1.getHeadChar(), list_show.size() - 1); } // 设置Item type bean.setType(MainActivity.ITEM); list_show.add(bean); } } else { for (int i = 0; i < list_all.size(); i++) { PhoneBean bean = list_all.get(i); //中文字符匹配首字母和英文字符匹配首字母 if (bean.getName().indexOf(search) != -1|| bean.getName_en().indexOf(search) != -1) { if (!map_IsHead.containsKey(bean.getHeadChar())) {// 如果不包含就添加一个标题 PhoneBean bean1 = new PhoneBean(); // 设置名字 bean1.setName(bean.getName()); // 设置标题type bean1.setType(MainActivity.TITLE); list_show.add(bean1); // map的值为标题的下标 map_IsHead.put(bean1.getHeadChar(), list_show.size() - 1); } // 设置Item type bean.setType(MainActivity.ITEM); list_show.add(bean); } } } adapter.notifyDataSetChanged(); } }); // 右边字母竖排的初始化以及监听 letterIndexView.init(new LetterIndexView.OnTouchLetterIndex() { //实现移动接口 @Override public void touchLetterWitch(String letter) { // 中间显示的首字母 txt_center.setVisibility(View.VISIBLE); txt_center.setText(letter); // 首字母是否被包含 if (adapter.map_IsHead.containsKey(letter)) { // 设置首字母的位置 listView.setSelection(adapter.map_IsHead.get(letter)); } } //实现抬起接口 @Override public void touchFinish() { txt_center.setVisibility(View.GONE); } }); /** listview点击事件 */ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view,int i, long l) { if (list_show.get(i).getType() == MainActivity.ITEM) {// 标题点击不给操作 Toast.makeText(MainActivity.this,list_show.get(i).getName(), Toast.LENGTH_LONG).show(); } } }); // 设置标题部分有阴影 // listView.setShadowVisible(true); } protected void initData() { list_all = new ArrayList<PhoneBean>(); list_show = new ArrayList<PhoneBean>(); map_IsHead = new HashMap<String, Integer>(); adapter = new PhoneAdapter(MainActivity.this, list_show, map_IsHead); listView.setAdapter(adapter); // 开启异步加载数据 new Thread(runnable).start(); } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { adapter.notifyDataSetChanged(); } }; private Runnable runnable = new Runnable() { @Override public void run() { String[] str = getResources().getStringArray(R.array.phone_all); for (int i = 0; i < str.length; i++) { PhoneBean cityBean = new PhoneBean(); cityBean.setName(str[i]); list_all.add(cityBean); } //按拼音排序 MemberSortUtil sortUtil = new MemberSortUtil(); Collections.sort(list_all, sortUtil); // 初始化数据,顺便放入把标题放入map集合 for (int i = 0; i < list_all.size(); i++) { PhoneBean cityBean = list_all.get(i); if (!map_IsHead.containsKey(cityBean.getHeadChar())) {// 如果不包含就添加一个标题 PhoneBean cityBean1 = new PhoneBean(); // 设置名字 cityBean1.setName(cityBean.getName()); // 设置标题type cityBean1.setType(MainActivity.TITLE); list_show.add(cityBean1); // map的值为标题的下标 map_IsHead.put(cityBean1.getHeadChar(),list_show.size() - 1); } list_show.add(cityBean); } handler.sendMessage(handler.obtainMessage()); } }; public class MemberSortUtil implements Comparator<PhoneBean> { /** * 按拼音排序 */ @Override public int compare(PhoneBean lhs, PhoneBean rhs) { Comparator<Object> cmp = Collator .getInstance(java.util.Locale.CHINA); return cmp.compare(lhs.getName_en(), rhs.getName_en()); } }}
然而适配器则是用的getViewTypeCount()这种方式,缓存2个不同的view来实现,个人认为是已经不能再优化的方案了。如有更好方案欢迎提出来,我加以改正,代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):
package com.example.phonedemo;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.ArrayList;import java.util.HashMap;import com.example.phonedemo.PinnedSectionListView.PinnedSectionListAdapter;public class PhoneAdapter extends BaseAdapter implements PinnedSectionListAdapter { private LayoutInflater layoutInflater; /** * 数据集 */ private ArrayList<PhoneBean> list; /** * 首字母 */ public HashMap<String, Integer> map_IsHead; public PhoneAdapter(Context context, ArrayList<PhoneBean> list, HashMap<String, Integer> map_IsHead) { this.list = list; this.map_IsHead = map_IsHead; layoutInflater = LayoutInflater.from(context); } @Override public int getCount() { return list.size(); } @Override public Object getItem(int i) { return list.get(i); } @Override public long getItemId(int i) { return i; } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { return list.get(position).getType(); } //实现自定义listview的接口 @Override public boolean isItemViewTypePinned(int viewType) { return viewType == MainActivity.TITLE; } @Override public View getView(int i, View view, ViewGroup viewGroup) { final ViewHolder viewHolder; switch (getItemViewType(i)) { case MainActivity.ITEM: if (view == null) { viewHolder = new ViewHolder(); view = layoutInflater.inflate(R.layout.item_phone_item, null); viewHolder.txt = (TextView) view.findViewById(R.id.item_phone_txt_name); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } //设置名字 viewHolder.txt.setText(list.get(i).getName()); break; case MainActivity.TITLE: if (view == null) { viewHolder = new ViewHolder(); view = layoutInflater.inflate(R.layout.item_phone_title, null); viewHolder.txt = (TextView) view.findViewById(R.id.item_phone_txt_head); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } //设置标题 viewHolder.txt.setText(list.get(i).getHeadChar()); break; } return view; } private class ViewHolder { private TextView txt; }}
而中文切成拼音,我则是直接放到bean里面来做,方便点代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):
package com.example.phonedemo;import java.util.ArrayList;import android.text.TextUtils;public class PhoneBean { /** * 城市名字首字母 */ private String headChar; /** * 城市id */ private String city_id; /** * 省id */ private String pro_id; /** * 城市名字 */ private String name; /** * 城市字母名字 */ private String name_en; /** * 是否是标题 */ private int type; public String getHeadChar() { return headChar; } public String getName() { return name; } public String getName_en() { return name_en; } public int getType() { return type; } public void setType(int type) { this.type = type; } public void setName(String name) { this.name = name; name_en = getPinYin(name);//获取字母名称 name_en = name_en.toUpperCase();//把小写字母换成大写字母 if (!TextUtils.isEmpty(name_en)) { char head = name_en.charAt(0); if (head < 'A' || head > 'Z') { head = '#'; } headChar = head + ""; } } /** * 汉字转换拼音,字母原样返回,都转换为小写 */ public String getPinYin(String input) { ArrayList<HanziToPinyin.Token> tokens = HanziToPinyin.getInstance().get(input); StringBuilder sb = new StringBuilder(); if (tokens != null && tokens.size() > 0) { for (HanziToPinyin.Token token : tokens) { if (token.type == HanziToPinyin.Token.PINYIN) { sb.append(token.target); } else { sb.append(token.source); } } } return sb.toString().toLowerCase(); } public String getCity_id() { return city_id; } public void setCity_id(String city_id) { this.city_id = city_id; } public String getPro_id() { return pro_id; } public void setPro_id(String pro_id) { this.pro_id = pro_id; }}
最后贴下xml界面布局如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <EditText android:id="@+id/edit_search" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:drawableLeft="@drawable/edit_search" android:drawablePadding="10dp" android:hint="请输入名字或者拼音" android:padding="15dp" android:singleLine="true" android:textColor="@android:color/black" android:textColorHint="#ff333333" android:textSize="16sp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/edit_search" android:background="@android:color/white" > <com.example.phonedemo.PinnedSectionListView android:id="@+id/phone_listview" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@null" /> <com.example.phonedemo.LetterIndexView android:id="@+id/phone_LetterIndexView" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentRight="true" /> </RelativeLayout> <TextView android:id="@+id/phone_txt_center" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerInParent="true" android:background="#30000000" android:gravity="center" android:textColor="#ff7c7c7c" android:visibility="gone" /></RelativeLayout>
好了,只有部分代码没有贴出来,我觉得是无关紧要的,所以就没贴。其他的基本上都贴出来了。我写代码的习惯就是边写代码边注释,所以在博客上就不浪费时间做重复的事情了。望见谅;
希望大家多多关注我的博客,多多支持我。
如有好意见或更好的方式欢迎留言谈论。
尊重原创转载请注明:(http://blog.csdn.net/u013895206) !
下面是地址传送门:
http://download.csdn.net/detail/u013895206/9273575
- 自定义View学习之12/4(仿IOS联系人列表)
- 自定义view之——联系人列表
- 自定义view例子(【图片移动】【仿联系人边上字母列表】)
- 自定义View学习之12/3(仿Twitter拍照按钮)
- Android 自定义View学习(3)--仿IOS风格滑动按钮
- Android自定义控件之仿通讯录联系人
- 仿QQ联系人列表
- 仿IOS开关自定义View的实现
- 自定义View:仿ios开关按钮控件
- iOS 之自定义view
- 仿Android6.0联系人列表
- Android仿联系人列表分组悬浮列表实现,自定义PinnedHeaderListView实现
- 自定义View之仿淘宝详情页
- 自定义控件之AndroidSegmentControlView,仿IOS平台UISegmentControlView,继承自View
- Android进阶之自定义View实战(一)仿iOS UISwitch控件实现
- Android自定义View之popupwindow进阶封装:高仿ios “item动画弹出”效果的popupwindow。
- [IOS]联系人列表
- Android Contacts之三自定义的联系人列表特效
- 递归和分治思想1 – 数据结构和算法31
- 组合查询
- ssh学习笔记
- H5移动端页面一些坑总结1
- 递归和分治思想2 – 数据结构和算法32
- 自定义View学习之12/4(仿IOS联系人列表)
- Unity3D Editor 编辑器简易教程
- 15-11-16 Eclipse 操作菜单汉译之 Project [项目]
- 模型
- Android 判断TextView 是否为空
- jQuery EasyUI开发指南——互动出版网
- 医药采购之供货商药品目录删除
- <1>Android HAL 作用
- JetBrains系列新版本注册激活包括IDEA15,PHPSTORM10,WEBSTORM11