
要实现这种功能,只需要在 android系统联系人分组特效实现(1)---分组导航和挤压动画 的基础上再加上一个自定义控件即可完成.
1.新建项目,继续新建一个java类,BladeView,用于作为导航栏
public class BladeView extends View {
private Handler handler = new Handler();
public BladeView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.NewMyElement);
textSize=a.getDimension(R.styleable.NewMyElement_textSize, 15);
a.recycle();
}
private PopupWindow mPopupWindow;
private TextView mPopupText;
float textSize;
public interface OnMyImteClickListener{
void onItemClick(int s);
}
private OnMyImteClickListener mListener;
public void setOnMyItemClickListener(OnMyImteClickListener listener){
this.mListener=listener;
}
String b[]={ "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
"Y", "Z" };
int choose=-1;
Paint paint=new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height=getHeight();
int width=getWidth();
int singleHeight=height/b.length;
for (int i = 0; i < b.length; i++) {
paint.setTextSize(textSize);
paint.setColor(Color.BLACK);
if(i==choose){
paint.setColor(Color.BLUE);
}
float xPos=width/2-paint.measureText(b[i])/2;
float yPos=singleHeight*i+singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
float y=event.getY();
int oldChoose=choose;
int c=(int)(y/getHeight()*b.length);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(oldChoose!=c){
if(c>=0&&c<b.length){
choose=c;
mListener.onItemClick(c);
showPopup(c);
invalidate();
}
}
break;
case MotionEvent.ACTION_MOVE:
if(oldChoose!=c){
if(c>=0&&c<b.length){
choose=c;
mListener.onItemClick(c);
showPopup(c);
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
choose=-1;
mListener.onItemClick(-1);
dismissPopup();
invalidate();
break;
default:
break;
}
return true;
}
private void showPopup(int item) {
if (mPopupWindow == null) {
handler.removeCallbacks(dismissRunnable);
mPopupText = new TextView(getContext());
mPopupText.setBackgroundColor(Color.GRAY);
mPopupText.setTextColor(Color.CYAN);
mPopupText.setTextSize(50);
mPopupText.setGravity(Gravity.CENTER_HORIZONTAL
| Gravity.CENTER_VERTICAL);
mPopupWindow = new PopupWindow(mPopupText, 100, 100);
}
String text = "";
if (item == 0) {
text = "#";
} else {
text = Character.toString((char) ('A' + item - 1));
}
mPopupText.setText(text);
if (mPopupWindow.isShowing()) {
mPopupWindow.update();
} else {
mPopupWindow.showAtLocation(getRootView(),
Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL, 0, 0);
}
}
private void dismissPopup() {
handler.postDelayed(dismissRunnable, 800);
}
Runnable dismissRunnable = new Runnable() {
@Override
public void run() {
if (mPopupWindow != null) {
mPopupWindow.dismiss();
}
}
};
}
2.打开activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:my="http://schemas.android.com/apk/res/com.example.mycontact"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mycontact.MainActivity" >
<ListView
android:id="@+id/lv_contact"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:scrollbars="none"
android:fadingEdge="none"
></ListView>
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="fill_parent"
android:layout_height="18dp"
android:layout_alignParentTop="true"
android:orientation="vertical"
android:background="#303030"
>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dp"
android:textColor="#ffffff"
android:textSize="13sp"
/>
</LinearLayout>
<com.example.mycontact.BladeView
android:id="@+id/BladeView"
android:layout_width="30dp"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
my:textSize="23sp"
/>
<RelativeLayout
android:id="@+id/section_toast_layout"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerInParent="true"
android:background="@drawable/section_toast"
android:visibility="gone"
>
<TextView
android:id="@+id/section_toast_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#ffffff"
android:textSize="30sp"
/>
</RelativeLayout>
</RelativeLayout>
3.实现主要功能之前,还需一个实体类
public class Contact {
private String name;//姓名
private String sortKey;//排序字母
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSortKey() {
return sortKey;
}
public void setSortKey(String sortKey) {
this.sortKey = sortKey;
}
}
4.还需要一个ListView展示所显示的子布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/sort_key_layout"
android:layout_width="fill_parent"
android:layout_height="18dp"
android:orientation="vertical"
android:background="#303030"
>
<TextView
android:id="@+id/sort_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dp"
android:textColor="#ffffff"
android:textSize="13sp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:id="@+id/name_layout"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/icon"
/>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="#ffffff"
android:textSize="22sp"
/>
</LinearLayout>
</LinearLayout>
5.展示数据的适配器
public class ContactAdapter extends ArrayAdapter<Contact> {
//需要渲染的item布局文件
private int resource;
//字母表分组工具
private SectionIndexer mIndexer;
public ContactAdapter(Context context,
int textViewResourceId, List<Contact> objects) {
super(context, textViewResourceId, objects);
resource=textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Contact contact=getItem(position);
LinearLayout layout=null;
if(convertView==null){
layout=(LinearLayout) LayoutInflater.from(getContext()).inflate(resource, null);
}
else{
layout=(LinearLayout) convertView;
}
TextView name=(TextView) layout.findViewById(R.id.name);
LinearLayout sortKeyLayout=(LinearLayout) layout.findViewById(R.id.sort_key_layout);
TextView sort_key=(TextView) layout.findViewById(R.id.sort_key);
name.setText(contact.getName());
int section=mIndexer.getSectionForPosition(position);
if(position==mIndexer.getPositionForSection(section)){
sortKeyLayout.setVisibility(View.VISIBLE);
sort_key.setText(contact.getSortKey());
}
else{
sortKeyLayout.setVisibility(View.GONE);
}
return layout;
}
//给当前适配器传入一个分组工具
public void setIndexer(SectionIndexer indexer){
mIndexer=indexer;
}
}
6.最关键的MainActivity.java
public class MainActivity extends ActionBarActivity {
//分组的布局
private LinearLayout titleLayout;
//弹出式分组的布局
private RelativeLayout sectionToastLayout;
//右侧可滑动字母表
private BladeView bView;
//分组上显示的字母
private TextView title;
//弹出式分组上的文字
private TextView sectionToastText;
//联系人ListView
private ListView contactListView;
//联系人列表适配器
private ContactAdapter adapter;
//用于对字母表进行分组
private AlphabetIndexer indexer;
//存储手机中所有联系人
private List<Contact> contactsList=new ArrayList<Contact>();
//定义字母表的排序规则
private String alphabet="#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
//记录上一个元素,用于滚动时记录标志
private int lastFirstVisibleItem=-1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter=new ContactAdapter(this, R.layout.item,contactsList);
titleLayout=(LinearLayout) findViewById(R.id.title_layout);
title=(TextView) findViewById(R.id.title);
sectionToastLayout=(RelativeLayout) findViewById(R.id.section_toast_layout);
sectionToastText=(TextView) findViewById(R.id.section_toast_text);
bView=(BladeView) findViewById(R.id.BladeView);
contactListView=(ListView) findViewById(R.id.lv_contact);
Uri uri=ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
Cursor cursor=getContentResolver().query(uri, new String[]{"display_name","sort_key"}, null, null, "sort_key");
if(cursor.moveToFirst()){
do {
String name=cursor.getString(0);
String sortKey=getSortKey(cursor.getString(1));
Contact contact=new Contact();
contact.setName(name);
contact.setSortKey(sortKey);
contactsList.add(contact);
} while (cursor.moveToNext());
}
startManagingCursor(cursor);
indexer=new AlphabetIndexer(cursor, 1, alphabet);
adapter.setIndexer(indexer);
if(contactsList.size()>0){
setupContactListView();
setAlphabetListener();
}
}
//为联系人ListView设置滑动监听
private void setupContactListView(){
contactListView.setAdapter(adapter);
contactListView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView arg0, int arg1) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int section=indexer.getSectionForPosition(firstVisibleItem);
int nextSectionPosition=indexer.getPositionForSection(section+1);
if(firstVisibleItem!=lastFirstVisibleItem){
MarginLayoutParams params=(MarginLayoutParams) titleLayout.getLayoutParams();
params.topMargin=0;
titleLayout.setLayoutParams(params);
title.setText(String.valueOf(alphabet.charAt(section)));
}
if(nextSectionPosition==firstVisibleItem+1){
View childView=view.getChildAt(0);
if(childView!=null){
int titleHeight=titleLayout.getHeight();
int bottom=childView.getBottom();
MarginLayoutParams params=(MarginLayoutParams) titleLayout.getLayoutParams();
if(bottom<titleHeight){
float pushedDistance=bottom-titleHeight;
params.topMargin=(int)pushedDistance;
titleLayout.setLayoutParams(params);
}
else{
if(params.topMargin!=0){
params.topMargin=0;
titleLayout.setLayoutParams(params);
}
}
}
}
lastFirstVisibleItem=firstVisibleItem;
}
});
}
//设置字母表上的触摸事件
private void setAlphabetListener(){
bView.setOnMyItemClickListener(new OnMyImteClickListener() {
@Override
public void onItemClick(int s) {
if(s!=-1){
contactListView.setSelection(indexer.getPositionForSection(s));
}
}
});
}
//获取sort_key的首字母
private String getSortKey(String sortKeyString){
String key=sortKeyString.substring(0, 1).toUpperCase();
if(key.matches("[A-Z]")){
return key;
}
return "#";
}
}
- android系统联系人分组特效实现(1)---分组导航和挤压动画
1.打开activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andr ...
- Android系统联系人全特效实现(下),字母表快速滚动
在上一篇文章中,我和大家一起实现了类似于Android系统联系人的分组导航和挤压动画功能,不过既然文章名叫做<Android系统联系人全特效实现>,那么没有快速滚动功能显然是称不上&quo ...
- Android系统联系人全特效实现(上),分组导航和挤压动画
记得在我刚接触Android的时候对系统联系人中的特效很感兴趣,它会根据手机中联系人姓氏的首字母进行分组,并在界面的最顶端始终显示一个当前的分组.如下图所示: 最让我感兴趣的是,当后一个分组和前一个分 ...
- Android系统源码学习步骤
Android系统是基于Linux内核来开发的,在分析它在运行时库层的源代码时,我们会经常碰到诸如管道(pipe).套接字(socket)和虚拟文件系统(VFS)等知识. 此外,Android系统还在 ...
- Android系统源代码学习步骤
目前,互联网行业正在朝着移动互联网方向强劲地发展,而移动互联网的发展离不开背后的移动平台的支撑.众所周知,如今在移动平台市场上,苹果的iOS.谷歌的Android和微软的Windows Phone系统 ...
- 如何学习Android系统源码(转)
一. Android系统的源代码非常庞大和复杂,我们不能贸然进入,否则很容易在里面迷入方向,进而失去研究它的信心.我们应该在分析它的源代码之前学习好一些理论知识,下面就介绍一些与Android系统相关 ...
- android 获取系统联系人 完全解析
一.代码 1.ContactsEngine.java import java.util.ArrayList; import java.util.HashMap; import java.util.Li ...
- android: 通过内容提供器读取系统联系人
读取系统联系人 由于我们之前一直使用的都是模拟器,电话簿里面并没有联系人存在,所以现在需要自 己手动添加几个,以便稍后进行读取.打开电话簿程序,界面如图 7.1 所示. 图 7.1 可以看到,目前 ...
- Android(java)学习笔记250:ContentProvider使用之获得系统联系人信息02(掌握)
1.重要: 系统删除一个联系人,默认情况下并不是把这个联系人直接删除掉了,只是做了一个标记,标记为被删除. 2.前面一讲说过了如何获取系统联系人信息(通过ContentProvider),获取联系人信 ...
随机推荐
- nginx 配置路由规则转发配置记录
工作中公司要求针对经销商PC端和工厂PC端的访问地址固定访问. 经销商PC端 http://localhost/ 工厂PC端 http://localhost/fac 文件磁盘路径: /crm/n ...
- 中小学信息学奥林匹克竞赛-理论知识考点--ASCII
ASCII表说白了就是一张表. 表中记录着:字符 和 数字 的对应关系.比如:字符0对应的ASCII码是48,A对应的是65,a对应的是97. 只要记住这三个,其它的数字,大写,小写字母的ASCII码 ...
- left join后面加上where条件浅析
select a.*,b.* from table1 a left join table2 b on b.X=a.X where XXX 如上:一旦使用了left join,没有where条件时,左表 ...
- Eclipse 修改默认工作空间
第一次启动Eclipse时会弹出对话框,让你进行Workspace Launcher,也就是设置Eclipse的项目存放路径.但是,当你勾选“Use this as the default and d ...
- mysql架构和历史
存储引擎 查看: show table status like 'bigcourse'; 结果: +-----------+--------+---------+------------+------ ...
- scrapy--json(360美图)
之前开始学习scrapy,接触了AJax异步加载.一直没放到自己博客,趁现在不忙,也准备为下一个爬虫做知识储存,就分享给大家. 还是从爬取图片开始,先上图给大家看看成果,QAQ. 一.图片加载的方法 ...
- 类的特殊方法"__call__"详解
1. __call__ 当执行对象名+括号时, 会自动执行类中的"__call__"方法, 怎么用? class A: def __init__(self, name): self ...
- GMT 时间格式转换到 TDateTime (Delphi)
//GMT 时间格式转换到 TDateTime //忽略时区 function GMT2DateTime(const pSour:PAnsiChar):TDateTime; function GetM ...
- Java8 新API读取文件内容
import java.io.IOException;import java.nio.charset.Charset;import java.nio.file.Files;import java.ni ...
- ASP.NET 使用 MySQL
基本是通用的 C#与MySQL的交互, 先添加MySQL.Data.dll(位于MySQL安装目录下的Connector NET 8.0\Assemblies${version}目录下)引用, 之后代 ...