3.ListView设计
从上面的截图来看,这个ListView在每个最先出现的字母上多了一个字母编号,这是怎么实现的呢?
答案就是当是第一次出现某个字母的时候显示首字母,当不是第一个显示的时候隐藏首字母。
也就是ListView里面有两个TextView,一个用来显示首字母,一个用来显示名字,当不是首次出现的字母时,隐藏第一个TextView。这样这个 效果就实现了。
下面我们就来实现我们的ListView。
Ⅰ首先实现联系人信息类MessageItem
它有两个属性,一个字母,一个名字,代码如下:
public class MessageItem { public String firstChar; public String name; }
Ⅱ实现ListView的适配器
还要用我们常用的BaseAdapter,代码如下:
private List<MessageItem> nameList = new ArrayList<>(); private class NameAdapter extends BaseAdapter { private LayoutInflater inflater; NameAdapter() { super(); this.inflater = getLayoutInflater(); } @Override public int getCount() { return nameList.size(); } @Override public Object getItem(int position) { return nameList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; View view = convertView; if (view == null) { view = this.inflater.inflate(R.layout.list_item, null); holder = new ViewHolder(view); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } MessageItem item = nameList.get(position); holder.name.setText(item.name); if (item.firstChar != null) {//如果有首字母 holder.title.setText(item.firstChar);//设置其首字母 holder.title.setVisibility(View.VISIBLE);//显示TextView } else { holder.title.setVisibility(View.GONE);//隐藏TextView } return view; } } public class ViewHolder { public TextView title; public TextView name; public ViewHolder(View view) { this.title = (TextView) view.findViewById(R.id.title); this.name = (TextView) view.findViewById(R.id.name); } }
需要讲解View.GONE与View.INVISIBLE,两样是隐藏,前一个隐藏后不占位置,后一个隐藏后会占位置。但都不会显示。
其他都是最基本的Android知识,这里不作过多的描述,如果看不懂,请认真学习Android基础在来。
Ⅲ排序联系人,确立首字母
我偷了一个小懒,不排序联系人,直接给了排序好的联系人如下:
private String[] name = {"aimengyuan", "aili", "bibo", "bbmb", "caonima", "changjiang", "debug", "eoog", "fengxinyao", "git", "hefan", "inne", "ppoppo", "renmingbi", "xinxin", "you", "zzzzzzz"};
这里之所以不用汉字,是因为汉字排序的工具类,我没有下载,你可以去下载后使用,这里具体讲解功能实现,排序汉字不是本章重点。原谅我偷懒一下。
现在我们假设我们有了排序好的联系人。那么怎么设置首字母在该显示的地方显示,不该显示的地方不显示呢?
代码如下:
private int[] charFlag = new int[26];//设置标志符,当设置了某个字母的首字母后,标志为1 public void initView() { for (int i = 0; i < name.length; i++) { MessageItem item = new MessageItem();//创建一个联系人 item.name = name[i];//获取它的名字,如果是汉字,获取他对应的拼音 int ascii = (int) name[i].charAt(0);//获取名字首字母,ASCII编码 for (int j = 0; j < 26; j++) {//遍历标志符 int flag = (int) (ascii - 97); if (flag == j) {//如果是小写字母 if (charFlag[j] != 1) {//如果该小写字母不等于1 item.firstChar = String.valueOf(((char) (j + 65)));//设置其首字母 charFlag[j] = 1;//将标志符设置为1,表示该字母有首字母在出现这个字母,不设置其首字母。 } } } this.nameList.add(item);//添加联系人 } this.adapter = new NameAdapter(); this.mListView.setAdapter(this.adapter);//设置适配器 }
小写字母a~z,ASCII编码为:97到122
大写字母A~Z,ASCII编码为:65到90
这里减去97就是让其在26之内,好作判断之用。
字符强转会变为ASCII编码,int强转也会成为字符。
ⅣMianActivity初始化
代码如下:
private RightView rightView; private EditText editText; private TextView dialogString; private ListView mListView; private NameAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.rightView = (RightView) findViewById(R.id.rightView); this.editText = (EditText) findViewById(R.id.searchEdit); this.dialogString = (TextView) findViewById(R.id.dialogString); this.mListView = (ListView) findViewById(R.id.myList); this.rightView.setMdialog(this.dialogString); this.rightView.setOnTextViewChange(this); initView(); }
Ⅴ实现自定义控件回调接口
代码如下:
@Override public void onTextChange(String s) { for (int i = 0; i < nameList.size(); i++) { MessageItem item = nameList.get(i);//获取item信息 if (item.firstChar != null && item.firstChar.equals(s)) {//判断首字母不为空,并且首字母与弹出TextView字符串相等 final int finalI = i; mListView.post(new Runnable() { @Override public void run() { mListView.requestFocusFromTouch();//获取焦点 mListView.setSelection(finalI);//跳转到该首字母的item } }); adapter.notifyDataSetChanged();//更新ListView } } }
为什么ListView不直接调用run中的方法,而是使用post,原因有2:
原因一:界面初始化完成之后listview失去了焦点
原因二:因为listview的item高度不一致,setSelection无法准确定位。
这样更新UI,不会阻塞。
最终的运行效果如下所示:
4.最后的漏洞
如图所示:
当输入法出现后,会挤压自定义控件,这个问题很是苦恼,经过百度多篇文章后,提到如下方法在AndroidManifest.xml的该Activity控件下设置如下属性:
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
就可以完美解决上面的漏洞。
该属性的参数详情如下:
【A】stateUnspecified:软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置
【B】stateUnchanged:当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示
【C】stateHidden:用户选择activity时,软键盘总是被隐藏
【D】stateAlwaysHidden:当该Activity主窗口获取焦点时,软键盘也总是被隐藏的
【E】stateVisible:软键盘通常是可见的
【F】stateAlwaysVisible:用户选择activity时,软键盘总是显示的状态
【G】adjustUnspecified:默认设置,通常由系统自行决定是隐藏还是显示
【H】adjustResize:该Activity总是调整屏幕的大小以便留出软键盘的空间
【I】adjustPan:当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分