欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android之联系人PinnedHeaderListView使用介绍

程序员文章站 2023-10-19 14:04:11
android联系人中的listview是做得比较独特的,但是源码写得比较复制,当我们想使用他的时候再从源码中提取,实属不易啊,而且容易出错,这几天,我把他提取出来了,写成...
android联系人中的listview是做得比较独特的,但是源码写得比较复制,当我们想使用他的时候再从源码中提取,实属不易啊,而且容易出错,这几天,我把他提取出来了,写成一个简单的例子,一是给自己备忘,而是跟大家分享一下,好了,先来看看效果图:
Android之联系人PinnedHeaderListView使用介绍 
首先是封装好的带头部的pinnedheaderlistview:
复制代码 代码如下:

public class pinnedheaderlistview extends listview {
public interface pinnedheaderadapter {
public static final int pinned_header_gone = 0;
public static final int pinned_header_visible = 1;
public static final int pinned_header_pushed_up = 2;
int getpinnedheaderstate(int position);
void configurepinnedheader(view header, int position, int alpha);
}
private static final int max_alpha = 255;
private pinnedheaderadapter madapter;
private view mheaderview;
private boolean mheaderviewvisible;
private int mheaderviewwidth;
private int mheaderviewheight;
public pinnedheaderlistview(context context) {
super(context);
}
public pinnedheaderlistview(context context, attributeset attrs) {
super(context, attrs);
}
public pinnedheaderlistview(context context, attributeset attrs,
int defstyle) {
super(context, attrs, defstyle);
}
protected void onlayout(boolean changed, int left, int top, int right, int bottom) {
super.onlayout(changed, left, top, right, bottom);
if (mheaderview != null) {
mheaderview.layout(0, 0, mheaderviewwidth, mheaderviewheight);
configureheaderview(getfirstvisibleposition());
}
}
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
super.onmeasure(widthmeasurespec, heightmeasurespec);
if (mheaderview != null) {
measurechild(mheaderview, widthmeasurespec, heightmeasurespec);
mheaderviewwidth = mheaderview.getmeasuredwidth();
mheaderviewheight = mheaderview.getmeasuredheight();
}
}
public void setpinnedheaderview(view view) {
mheaderview = view;
if (mheaderview != null) {
setfadingedgelength(0);
}
requestlayout();
}
public void setadapter(listadapter adapter) {
super.setadapter(adapter);
madapter = (pinnedheaderadapter)adapter;
}
public void configureheaderview(int position) {
if (mheaderview == null) {
return;
}
int state = madapter.getpinnedheaderstate(position);
switch (state) {
case pinnedheaderadapter.pinned_header_gone: {
mheaderviewvisible = false;
break;
}
case pinnedheaderadapter.pinned_header_visible: {
madapter.configurepinnedheader(mheaderview, position, max_alpha);
if (mheaderview.gettop() != 0) {
mheaderview.layout(0, 0, mheaderviewwidth, mheaderviewheight);
}
mheaderviewvisible = true;
break;
}
case pinnedheaderadapter.pinned_header_pushed_up: {
view firstview = getchildat(0);
int bottom = firstview.getbottom();
int headerheight = mheaderview.getheight();
int y;
int alpha;
if (bottom < headerheight) {
y = (bottom - headerheight);
alpha = max_alpha * (headerheight + y) / headerheight;
} else {
y = 0;
alpha = max_alpha;
}
madapter.configurepinnedheader(mheaderview, position, alpha);
if (mheaderview.gettop() != y) {
mheaderview.layout(0, y, mheaderviewwidth, mheaderviewheight
+ y);
}
mheaderviewvisible = true;
break;
}
}
}
protected void dispatchdraw(canvas canvas) {
super.dispatchdraw(canvas);
if (mheaderviewvisible) {
drawchild(canvas, mheaderview, getdrawingtime());
}
}
}

然后是旁边那个快速导航bladeview(刀锋):
复制代码 代码如下:

public class bladeview extends view {
private onitemclicklistener monitemclicklistener;
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();
boolean showbkg = false;
private popupwindow mpopupwindow;
private textview mpopuptext;
private handler handler = new handler();
public bladeview(context context, attributeset attrs, int defstyle) {
super(context, attrs, defstyle);
}
public bladeview(context context, attributeset attrs) {
super(context, attrs);
}
public bladeview(context context) {
super(context);
}
@override
protected void ondraw(canvas canvas) {
super.ondraw(canvas);
if (showbkg) {
canvas.drawcolor(color.parsecolor("#00000000"));
}
int height = getheight();
int width = getwidth();
int singleheight = height / b.length;
for (int i = 0; i < b.length; i++) {
paint.setcolor(color.black);
paint.settypeface(typeface.default_bold);
paint.setfakeboldtext(true);
paint.setantialias(true);
if (i == choose) {
paint.setcolor(color.parsecolor("#3399ff"));
}
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) {
final int action = event.getaction();
final float y = event.gety();
final int oldchoose = choose;
final int c = (int) (y / getheight() * b.length);
switch (action) {
case motionevent.action_down:
showbkg = true;
if (oldchoose != c) {
if (c > 0 && c < b.length) {
performitemclicked(c);
choose = c;
invalidate();
}
}
break;
case motionevent.action_move:
if (oldchoose != c) {
if (c > 0 && c < b.length) {
performitemclicked(c);
choose = c;
invalidate();
}
}
break;
case motionevent.action_up:
showbkg = false;
choose = -1;
dismisspopup();
invalidate();
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() {
// todo auto-generated method stub
if (mpopupwindow != null) {
mpopupwindow.dismiss();
}
}
};
public boolean ontouchevent(motionevent event) {
return super.ontouchevent(event);
}
public void setonitemclicklistener(onitemclicklistener listener) {
monitemclicklistener = listener;
}
private void performitemclicked(int item) {
if (monitemclicklistener != null) {
monitemclicklistener.onitemclick(b[item]);
showpopup(item);
}
}
public interface onitemclicklistener {
void onitemclick(string s);
}
}

接下来就是使用了,先在布局文件中声明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"
tools:context=".mainactivity" >
<com.way.view.pinnedheaderlistview
android:id="@+id/friends_display"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cachecolorhint="#00000000"
android:divider="@null"
android:footerdividersenabled="false"
android:headerdividersenabled="false" />
<com.way.view.bladeview
android:id="@+id/friends_myletterlistview"
android:layout_width="30dip"
android:layout_height="fill_parent"
android:layout_alignparentright="true"
android:background="#00000000" />
</relativelayout>

然后是一个独立adapter,这次我没有作为内部类放在mainactivity中:
复制代码 代码如下:

public class friendsadapter extends baseadapter implements sectionindexer,
pinnedheaderadapter, onscrolllistener {
private int mlocationposition = -1;
private string[] mdatas;
// 首字母集
private list<string> mfriendssections;
private list<integer> mfriendspositions;
private layoutinflater inflater;
public friendsadapter(context context,string[] datas, list<string> friendssections,
list<integer> friendspositions) {
// todo auto-generated constructor stub
inflater = layoutinflater.from(context);
mdatas = datas;
mfriendssections = friendssections;
mfriendspositions = friendspositions;
}
@override
public int getcount() {
// todo auto-generated method stub
return mdatas.length;
}
@override
public object getitem(int position) {
// todo auto-generated method stub
return mdatas[position];
}
@override
public long getitemid(int position) {
// todo auto-generated method stub
return position;
}
@override
public view getview(int position, view convertview, viewgroup parent) {
// todo auto-generated method stub
int section = getsectionforposition(position);
if (convertview == null) {
convertview = inflater.inflate(r.layout.listview_item, null);
}
linearlayout mheaderparent = (linearlayout) convertview
.findviewbyid(r.id.friends_item_header_parent);
textview mheadertext = (textview) convertview
.findviewbyid(r.id.friends_item_header_text);
if (getpositionforsection(section) == position) {
mheaderparent.setvisibility(view.visible);
mheadertext.settext(mfriendssections.get(section));
} else {
mheaderparent.setvisibility(view.gone);
}
textview textview = (textview) convertview
.findviewbyid(r.id.friends_item);
textview.settext(mdatas[position]);
return convertview;
}
@override
public void onscrollstatechanged(abslistview view, int scrollstate) {
// todo auto-generated method stub
}
@override
public void onscroll(abslistview view, int firstvisibleitem,
int visibleitemcount, int totalitemcount) {
// todo auto-generated method stub
if (view instanceof pinnedheaderlistview) {
((pinnedheaderlistview) view).configureheaderview(firstvisibleitem);
}
}
@override
public int getpinnedheaderstate(int position) {
int realposition = position;
if (realposition < 0
|| (mlocationposition != -1 && mlocationposition == realposition)) {
return pinned_header_gone;
}
mlocationposition = -1;
int section = getsectionforposition(realposition);
int nextsectionposition = getpositionforsection(section + 1);
if (nextsectionposition != -1
&& realposition == nextsectionposition - 1) {
return pinned_header_pushed_up;
}
return pinned_header_visible;
}
@override
public void configurepinnedheader(view header, int position, int alpha) {
// todo auto-generated method stub
int realposition = position;
int section = getsectionforposition(realposition);
string title = (string) getsections()[section];
((textview) header.findviewbyid(r.id.friends_list_header_text))
.settext(title);
}
@override
public object[] getsections() {
// todo auto-generated method stub
return mfriendssections.toarray();
}
@override
public int getpositionforsection(int section) {
if (section < 0 || section >= mfriendssections.size()) {
return -1;
}
return mfriendspositions.get(section);
}
@override
public int getsectionforposition(int position) {
// todo auto-generated method stub
if (position < 0 || position >= getcount()) {
return -1;
}
int index = arrays.binarysearch(mfriendspositions.toarray(), position);
return index >= 0 ? index : -index - 2;
}
}

最后就是mainactivity中的处理了:
复制代码 代码如下:

public class mainactivity extends activity {
private static final string format = "^[a-z,a-z].*$";
private pinnedheaderlistview mlistview;
private bladeview mletter;
private friendsadapter madapter;
private string[] datas;
// 首字母集
private list<string> msections;
// 根据首字母存放数据
private map<string, list<string>> mmap;
// 首字母位置集
private list<integer> mpositions;
// 首字母对应的位置
private map<string, integer> mindexer;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
initdata();
initview();
}
private void initdata() {
datas = getresources().getstringarray(r.array.countries);
msections = new arraylist<string>();
mmap = new hashmap<string, list<string>>();
mpositions = new arraylist<integer>();
mindexer = new hashmap<string, integer>();
for (int i = 0; i < datas.length; i++) {
string firstname = datas[i].substring(0, 1);
if (firstname.matches(format)) {
if (msections.contains(firstname)) {
mmap.get(firstname).add(datas[i]);
} else {
msections.add(firstname);
list<string> list = new arraylist<string>();
list.add(datas[i]);
mmap.put(firstname, list);
}
} else {
if (msections.contains("#")) {
mmap.get("#").add(datas[i]);
} else {
msections.add("#");
list<string> list = new arraylist<string>();
list.add(datas[i]);
mmap.put("#", list);
}
}
}
collections.sort(msections);
int position = 0;
for (int i = 0; i < msections.size(); i++) {
mindexer.put(msections.get(i), position);// 存入map中,key为首字母字符串,value为首字母在listview中位置
mpositions.add(position);// 首字母在listview中位置,存入list中
position += mmap.get(msections.get(i)).size();// 计算下一个首字母在listview的位置
}
}
private void initview() {
// todo auto-generated method stub
mlistview = (pinnedheaderlistview) findviewbyid(r.id.friends_display);
mletter = (bladeview) findviewbyid(r.id.friends_myletterlistview);
mletter.setonitemclicklistener(new onitemclicklistener() {
@override
public void onitemclick(string s) {
if (mindexer.get(s) != null) {
mlistview.setselection(mindexer.get(s));
}
}
});
madapter = new friendsadapter(this, datas, msections, mpositions);
mlistview.setadapter(madapter);
mlistview.setonscrolllistener(madapter);
mlistview.setpinnedheaderview(layoutinflater.from(this).inflate(
r.layout.listview_head, mlistview, false));
}
}

还有一个数据arrays.xml,我就不贴出来了,有兴趣的朋友可以下载源码