Android系统联系人全特效实现(上),分组导航和挤压动画(1)

本文介绍了如何在Android应用中创建一个带有分组和字母表排序的联系人列表,包括定义Contact实体类、使用ContactAdapter和SectionIndexer实现分组逻辑以及在ListView中实现挤压动画效果的过程。
摘要由CSDN通过智能技术生成

android:layout_marginRight=“10dip”
android:src=“@drawable/icon” />


在这个布局文件中,首先是放入了一个和前面完成一样的分组布局,因为不仅界面头部需要展示分组,在每个分组内的第一个无素之前都需要展示分组布局。然后是加入一个简单的 LinearLayout,里面包含了一个 ImageView 用于显示联系人头像,还包含一个 TextView 用于显示联系人姓名。

这样我们的布局文件就全部写完了,下面开始来真正地实现功能。

先从简单的开始,新建一个 Contact 实体类:

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;
}

}

这个实体类很简单,只包含了联系人姓名和排序键。

接下来完成联系人列表适配器的编写,新建一个 ContactAdapter 类继承自 ArrayAdapter,加入如下代码:

public class ContactAdapter extends ArrayAdapter {

/**

  • 需要渲染的item布局文件
    */
    private int resource;

/**

  • 字母表分组工具
    */
    private SectionIndexer mIndexer;

public ContactAdapter(Context context, int textViewResourceId, List 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 sortKey = (TextView) layout.findViewById(R.id.sort_key);
name.setText(contact.getName());
int section = mIndexer.getSectionForPosition(position);
if (position == mIndexer.getPositionForSection(section)) {
sortKey.setText(contact.getSortKey());
sortKeyLayout.setVisibility(View.VISIBLE);
} else {
sortKeyLayout.setVisibility(View.GONE);
}
return layout;
}

/**

  • 给当前适配器传入一个分组工具。
  • @param indexer
    */
    public void setIndexer(SectionIndexer indexer) {
    mIndexer = indexer;
    }

}

上面的代码中,最重要的就是 getView 方法,在这个方法中,我们使用 SectionIndexer 的 getSectionForPosition 方法,通过当前的 position 值拿到了对应的 section 值,然后再反向通过刚刚拿到的 section 值,调用 getPositionForSection 方法,取回新的 position 值。如果当前的 position 值和新的 position 值是相等的,那么我们就可以认为当前 position 的项是某个分组下的第一个元素,我们应该将分组布局显示出来,而其它的情况就应该将分组布局隐藏。

最后我们来编写程序的主界面,打开或新建 MainActivity 作为程序的主界面,代码如下所示:

public class MainActivity extends Activity {

/**

  • 分组的布局
    */
    private LinearLayout titleLayout;

/**

  • 分组上显示的字母
    */
    private TextView title;

/**

  • 联系人ListView
    */
    private ListView contactsListView;

/**

  • 联系人列表适配器
    */
    private ContactAdapter adapter;

/**

  • 用于进行字母表分组
    */
    private AlphabetIndexer indexer;

/**

  • 存储所有手机中的联系人
    */
    private List contacts = new ArrayList();

/**

  • 定义字母表的排序规则
    */
    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.contact_item, contacts);
titleLayout = (LinearLayout) findViewById(R.id.title_layout);
title = (TextView) findViewById(R.id.title);
contactsListView = (ListView) findViewById(R.id.contacts_list_view);
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);
contacts.add(contact);
} while (cursor.moveToNext());
}
startManagingCursor(cursor);
indexer = new AlphabetIndexer(cursor, 1, alphabet);
adapter.setIndexer(indexer);
if (contacts.size() > 0) {
setupContactsListView();
}
}

/**

  • 为联系人ListView设置监听事件,根据当前的滑动状态来改变分组的显示位置,从而实现挤压动画的效果。
    */
    private void setupContactsListView() {
    contactsListView.setAdapter(adapter);
    contactsListView.setOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
    }

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
int section = indexer.getSectionForPosition(firstVisibleItem);
int nextSecPosition = 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 (nextSecPosition == 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;
}
});

}

/**

  • 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。
  • @param sortKeyString
  •        数据库中读取出的sort key
    
  • @return 英文字母或者#
    */
    private String getSortKey(String sortKeyString) {
    String key = sortKeyString.substring(0, 1).toUpperCase();
    if (key.matches(“[A-Z]”)) {
    return key;
    }
    return “#”;
    }

}

可以看到,在 onCreate 方法中,我们从系统联系人数据库中去查询联系人的姓名和排序键,之后将查询返回的 cursor 直接传入 AlphabetIndexer 作为第一个参数。由于我们一共就查了两列,排序键在第二列,所以我们第二个 sortedColumnIndex 参数传入 1。第三个 alphabet 参数这里传入了 “#ABCDEFGHIJKLMNOPQRSTUVWXYZ” 字符串,因为可能有些联系人的姓名不在字母表范围内,我们统一用 #来表示这部分联系人。

然后我们在 setupContactsListView 方法中监听了 ListView 的滚动,在 onScroll 方法中通过 getSectionForPosition 方法获取第一个可见元素的分组值,然后给该分组值加 1,再通过 getPositionForSection 方法或者到下一个分组中的第一个元素,如果下个分组的第一个元素值等于第一个可见元素的值加 1,那就说明下个分组的布局要和界面顶部分组布局相碰了。之后再通过 ListView 的 getChildAt(0) 方法,获取到界面上显示的第一个子 View,再用 view.getBottom 获取底部距离父窗口的位置,对比分组布局的高度来对顶部分组布局进行纵向偏移,就可以实现挤压动画的效果了。

最后给出 AndroidManifest.xml 的代码,由于要读取手机联系人,因此需要加上 android.permission.READ_CONTACTS 的声明:

小福利:

在当下这个碎片化信息环境的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021大厂最新Android面试真题解析

Android大厂面试真题解析

各个模块学习视频:如数据结构与算法

算法与数据结构资料图

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
一线互联网架构师

这份体系学习笔记,适应人群:**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!点赞+评论即可获得!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
D8CtOg9-1714835933497)]

这份体系学习笔记,适应人群:**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!点赞+评论即可获得!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值