gpt4 book ai didi

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

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 55 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章Android系统联系人全特效实现(上)分组导航和挤压动画(附源码)由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

记得在我刚接触android的时候对系统联系人中的特效很感兴趣,它会根据手机中联系人姓氏的首字母进行分组,并在界面的最顶端始终显示一个当前的分组。如下图所示:   最让我感兴趣的是,当后一个分组和前一个分组相碰时,会产生一个上顶的挤压动画。那个时候我思考了各种方法想去实现这种特效,可是限于功夫不到家,都未能成功。如今两年多过去了,自己也成长了很多,再回头去想想这个功能,突然发现已经有了思路,于是立刻记录下来与大家分享。 首先讲一下需要提前了解的知识点,这里我们最需要用到的就是sectionindexer,它能够有效地帮助我们对分组进行控制。由于sectionindexer是一个接口,你可以自定义一个子类来实现sectionindexer,不过自己再写一个sectionindexer的实现太麻烦了,这里我们直接使用android提供好的实现alphabetindexer,用它来实现联系人分组功能已经足够了。 alphabetindexer的构造函数需要传入三个参数,第一个参数是cursor,第二个参数是sortedcolumnindex整型,第三个参数是alphabet字符串。其中cursor就是把我们从数据库中查出的游标传进去,sortedcolumnindex就是指明我们是使用哪一列进行排序的,而alphabet则是指定字母表排序规则,比如:"abcdefghijklmnopqrstuvwxyz"。有了alphabetindexer,我们就可以通过它的getpositionforsection和getsectionforposition方法,找出当前位置所在的分组,和当前分组所在的位置,从而实现类似于系统联系人的分组导航和挤压动画效果,关于alphabetindexer更详细的详解,请参考官方文档。 那么我们应该怎样对联系人进行排序呢?前面也提到过,有一个sortedcolumnindex参数,这个sortedcolumn到底在哪里呢?我们来看一下系统联系人的raw_contacts这张表(/data/data/com.android.providers.contacts/databases/contacts2.db),这个表结构比较复杂,里面有二十多个列,其中有一列名叫sort_key,这就是我们要找的了!如下图所示:   可以看到,这一列非常人性化地帮我们记录了汉字所对应的拼音,这样我们就可以通过这一列的值轻松为联系人进行排序了。 下面我们就来开始实现,新建一个android项目,命名为contactsdemo。首先我们还是先来完成布局文件,打开或新建activity_main.xml作为程序的主布局文件,在里面加入如下代码:

复制代码 代码如下

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <listview android:id="@+id/contacts_list_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignparenttop="true" android:fadingedge="none" > </listview> <linearlayout android:id="@+id/title_layout" android:layout_width="fill_parent" android:layout_height="18dip" android:layout_alignparenttop="true" 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="10dip" android:textcolor="#ffffff" android:textsize="13sp" /> </linearlayout> </relativelayout> 。

布局文件很简单,里面放入了一个listview,用于展示联系人信息。另外还在头部放了一个linearlayout,里面包含了一个textview,它的作用是在界面头部始终显示一个当前分组。 然后新建一个contact_item.xml的布局,这个布局用于在listview中的每一行进行填充,代码如下:

复制代码 代码如下

<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="18dip" 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="10dip" android:textcolor="#ffffff" android:textsize="13sp" /> </linearlayout> <linearlayout android:id="@+id/name_layout" android:layout_width="fill_parent" android:layout_height="50dip" > <imageview android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginleft="10dip" android:layout_marginright="10dip" 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> 。

在这个布局文件中,首先是放入了一个和前面完成一样的分组布局,因为不仅界面头部需要展示分组,在每个分组内的第一个无素之前都需要展示分组布局。然后是加入一个简单的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<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 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<contact> contacts = 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.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的声明:

复制代码 代码如下

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.contactsdemo" android:versioncode="1" android:versionname="1.0" > <uses-sdk android:minsdkversion="8" android:targetsdkversion="8" /> <uses-permission android:name="android.permission.read_contacts"></uses-permission> <application android:allowbackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/theme.notitlebar" > <activity android:name="com.example.contactsdemo.mainactivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> </application> </manifest> 。

现在我们来运行一下程序,效果如下图所示:   目前的话,分组导航和挤压动画效果都已经完成了,看起来感觉还是挺不错的,下一篇文章我会带领大家继续完善这个程序,加入字母表快速滚动功能。 好了,今天的讲解到此结束,有疑问的朋友请在下面留言。 源码下载,请点击这里 。

最后此篇关于Android系统联系人全特效实现(上)分组导航和挤压动画(附源码)的文章就讲到这里了,如果你想了解更多关于Android系统联系人全特效实现(上)分组导航和挤压动画(附源码)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

55 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com