Android之联系人PinnedHeaderListView使用介绍

Android联系人中的ListView是做得比较独特的,但是源码写得比较复制,当我们想使用他的时候再从源码中提取,实属不易啊,而且容易出错,这几天,我把他提取出来了,写成一个简单的例子,一是给自己备忘,而是跟大家分享一下,好了,先来看看效果图:
 
首先是封装好的带头部的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,我就不贴出来了,有兴趣的朋友可以下载源码

(0)

相关推荐

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

    记得在我刚接触Android的时候对系统联系人中的特效很感兴趣,它会根据手机中联系人姓氏的首字母进行分组,并在界面的最顶端始终显示一个当前的分组.如下图所示:  最让我感兴趣的是,当后一个分组和前一个分组相碰时,会产生一个上顶的挤压动画.那个时候我思考了各种方法想去实现这种特效,可是限于功夫不到家,都未能成功.如今两年多过去了,自己也成长了很多,再回头去想想这个功能,突然发现已经有了思路,于是立刻记录下来与大家分享. 首先讲一下需要提前了解的知识点,这里我们最需要用到的就是SectionInde

  • Android手机联系人快速索引(手机通讯录)

    最近需要实现一个手机通讯录的快速索引功能.根据姓名首字母快速索引功能.下面是一个手机联系人快速索引的效果,总体来说代码不算难,拼音转换的地方略有复杂.下面上源码:源码中有注释. 下面是效果图: MainActivity: import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.os.Bundle; imp

  • Android根据电话号码获得联系人头像实例代码

    在日常Android手机的使用过程中,根据电话号码获得联系人头像,是经常会碰到的问题.本文即以实例形式讲述了Android根据电话号码获得联系人头像是实现代码.分享给大家供大家参考之用.具体方法如下: 首先,通过ContentProvider,可以访问Android中的联系人等数据.常用的Uri有: 联系人信息Uri:content://com.android.contacts/contacts 联系人电话Uri:content://com.android.contacts/data/phone

  • Android编程实现通讯录中联系人的读取,查询,添加功能示例

    本文实例讲述了Android编程实现通讯录中联系人的读取,查询,添加功能.分享给大家供大家参考,具体如下: 先加二个读和写权限: <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> 具体代码: package com.ebo

  • Android仿微信联系人按字母排序

    App只要涉及到联系人的界面,几乎都是按照字母排序以及导航栏的方式.既然这个需求这么火,于是开始学习相关内容,此篇文章是我通过参考网上资料独立编写和总结的,希望多多少少对大家有所帮助,写的不好,还请各位朋友指教. 效果图如下: 实现这个效果,需要三个知识点 : 1:将字符串 进行拼音分类 2:ExpandableListView 二级扩展列表 3:右边字母分类View 我们先一个一个来了解解决方案,再上代码. 实现字母分类: 字母分类又分为三个小要点:一个是将中文转化为拼音,一个是实现按照字母的

  • android 加载本地联系人实现方法

    首先先建布局文件,界面很简单,就是一个搜索框和下面的联系人列表:   复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layou

  • 使用adb命令向Android模拟器中导入通讯录联系人的方法

    本文实例讲述了使用adb命令向Android模拟器中导入通讯录联系人的方法.分享给大家供大家参考.具体实现方法如下: 使用adb提供的命令, 可以非常方便地从PC中将通讯录导入android模拟器中. 首先要先准备好固定格式的contacts.vcf文件, 该文件即android中的通讯录存储文件. 格式如下: 复制代码 代码如下: BEGIN:VCARD  VERSION:3.0  N:15200000000;;;;  TEL;TYPE=cell:15200000000  END:VCARD 

  • Android获取手机通讯录、sim卡联系人及调用拨号界面方法

    android获取手机通讯录联系人信息 复制代码 代码如下: private void getPhoneContacts() {        ContentResolver resolver = this.getContentResolver();                // 获取手机联系人       Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,                  new String[] { Phone

  • Android保存联系人到通讯录的方法

    上一篇文章讲了如何获取所有联系人,这篇文章就讲下怎么保存联系人数据到本机通讯录.这里我就假设你已经拿到了要保存的联系人数据. 因为是一个工具类,所以我这里就只给一个方法了,也是很简单,但是写的没有读取联系人的数据那么多,要保存更多其实看下如何读取的就会了. 直接上源码: /** * 添加联系人到本机 * * @param context * @param contact * @return */ public static boolean addContact(Context context,

  • Android之联系人PinnedHeaderListView使用介绍

    Android联系人中的ListView是做得比较独特的,但是源码写得比较复制,当我们想使用他的时候再从源码中提取,实属不易啊,而且容易出错,这几天,我把他提取出来了,写成一个简单的例子,一是给自己备忘,而是跟大家分享一下,好了,先来看看效果图:  首先是封装好的带头部的PinnedHeaderListView: 复制代码 代码如下: public class PinnedHeaderListView extends ListView { public interface PinnedHeade

  • 浅谈Android手机联系人开发之增删查改功能

    最近在做手机联系人的功能模块的时候,遇到了很多的坑,在网上搜索的有一些所谓的最全的手机联系人开发的介绍还存在一些bug,所以我把我最近的项目心得和方法写下来,既能帮助大家减少了解android开发手机联系人的门槛,好,废话少说,接下来直奔主题. 一.深入浅出手机联系人的前奏(小米手机的data表跟模拟器的data表不一样) 1.手机联系人主要是对contacts2.db数据库表的操纵,这个数据库中有三个表是比较重要的,分别是data,raw_contacts,mimetyps这三个表.在下面的增

  • Android手机信号强度检测详细介绍

    最近到处在跑着找工作,难免在面试过程中遇到这样那样的问题,记得最清楚一次在面试过程中被问到,当手机处于弱网状态下,如何处理,如何监听网络信号强度变化.但是真是蒙了,回答的乱七八糟,思路一点都不明确.今天小编在这里带领大家了解下关于手机信号强度的相关几个概念. Android手机信号强度介绍 android定义了2种信号单位:dBm和asu.它们之间的关系是:dBm =-113+2asu,这是google给android手机定义的特有信号单位.例如,我的信号强度为-53dBm,则对应30asu,因

  • Android获取联系人头像的方法

    本文实例讲述了Android获取联系人头像的方法.分享给大家供大家参考,具体如下: public byte[] getPhoto(String people_id) { String photo_id = null; String selection1 = ContactsContract.Contacts._ID + " = " + people_id; Cursor cur1 = getContentResolver().query( ContactsContract.Contac

  • Android init.rc文件简单介绍

    Android init.rc文件简单介绍 init.rc脚本是由Android中linux的第一个用户级进程init进行解析的. init.rc 文件并不是普通的配置文件,而是由一种被称为"Android初始化语言"(Android Init Language,这里简称为AIL)的脚本写成的文件. 该文件在ROM中是只读的,即使有了root权限,可以修改该文件也没有.因为我们在根目录看到的文件只是内存文件的镜像.也就是说,android启动后,会将init.rc文件装载到内存.而修改

  • Android与H5互调详细介绍

    Android与H5互调详细介绍 微信,微博,微商,QQ空间,大量的软件使用内嵌了H5,这个时候就需要了解Android如何更H5交互的了:有些外包公司,为了节约成本,采用Android内嵌H5模式开发,便于在iOS上直接复用页面,最终解决成本. 为什么学android也要学h5? Android很多软件都有内嵌H5的,有什么用处.优势?节约成本,提高开发效率. 实现的原理是什么? 本质是:Java代码和JavaScript调用 案例一:Java与Js简单互调 首先,在Android代码中加载H

  • Android Studio debug.keystore位置介绍

    如果你在网上搜debug.keystore的位置,大部分文章都会告诉你在 C:\Users\XXX\.android 目录下,但我电脑的该目录下确实没有这个文件,放入同事的debug.keystore也不会生效. 后来在Android SDK的子目录.android下搜到了debug.keystore文件,猜测的原因是IDE里做了配置,但也不知道是哪里修改的. 最后在 StackOverflow 上找到一个靠谱答案,链接:http://stackoverflow.com/a/30908688 意

  • Android添加联系人到通讯录的方法

    字段 联系人名字 名字不知道为什么,值设置了之后传过去没有,于是自己通过 Intent 最后又单独传了一次 // 联系人名字 ContentValues row1 = new ContentValues();String name = lastName + middleName + firstName;row1.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_I

  • android Retrofit2网络请求封装介绍

    目录 1. Retrofit使用 2. Retrofit封装 3. RetrofitUtil使用 最后 1. Retrofit使用 Retrofit是一个现在网络请求框架,先来说一下怎么使用 网络权限(添加到AndroidManifest.xml) <uses-permission android:name="android.permission.INTERNET" /> gradle依赖(添加到build.gradle) implementation("com.

随机推荐