类似于微信通讯录的界面Demo

原创 2014年11月27日 17:00:42

这几天遇到一个项目需要向微信通讯录那样展示联系人,这里我做了一个简单的例子,希望可以帮助大家

先来看下界面的实现效果

          

     

上面的是在搜索框搜索时的结果,和点击后边的字母跳转到相应 的字母对应名字地方,并且显示你选择的字母

好了,现在我们来整理一下思路,怎么实现这中效果呢。

第一步,我们肯定是要有一个这样的界面

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/yellow_page_search_bg"
        android:paddingBottom="5dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="5dp" >

        <com.mulu.widget.CustomEditText
            android:id="@+id/school_friend_member_search_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:background="@drawable/bg_search_input"
            android:drawableLeft="@drawable/ic_search"
            android:drawablePadding="5dp"
            android:drawableRight="@drawable/ic_edit_text_delbtn"
            android:freezesText="true"
            android:gravity="center_vertical"
            android:hint="@string/school_friend_search_hint"
            android:imeOptions="actionSearch"
            android:singleLine="true"
            android:textColor="@color/black"
            android:textColorHint="@color/login_form_hint"
            android:textSize="14sp" />
    </RelativeLayout>

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="1px"
        android:background="@drawable/bg_cut_line" />

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <ListView
            android:id="@+id/school_friend_member"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:cacheColorHint="@color/transparent"
            android:divider="@drawable/bg_cut_line"
            android:dividerHeight="1px"
            android:fadingEdge="none"
            android:listSelector="@color/transparent"
            android:scrollbars="none" >
        </ListView>

        <TextView
            android:id="@+id/school_friend_dialog"
            android:layout_width="80.0dip"
            android:layout_height="80.0dip"
            android:layout_gravity="center"
            android:background="@drawable/bg_show_head_toast"
            android:gravity="center"
            android:textColor="#ffffffff"
            android:textSize="30.0dip"
            android:visibility="invisible" />

        <com.mulu.widget.SideBar
            android:id="@+id/school_friend_sidrbar"
            android:layout_width="30dp"
            android:layout_height="fill_parent"
            android:layout_gravity="right|center" />
    </FrameLayout>

</LinearLayout>

这个界面引用了自定义的一些空间CustomEditText,SideBar,一个是最上面的搜索输入框,一个是要显示上图对应绿色的x那个布局,还有一些资源文件,我都放在相应的res下drawable-hdip这个包中,等会我会把相应的全部代码打包上传上来的,有需要的可以下载看看

下面我们来自定义我们需要的控件

package com.mulu.widget;


import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.EditText;
/**
 * 自定义文本输入框,增加清空按钮
 * 
 * @author  shaozc
 * 
 */
public class CustomEditText extends EditText {

private Drawable mLeft, mTop, mRight, mBottom;
private Rect mBounds;

public CustomEditText(Context context) {
super(context);
init();
}
public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
setDrawable();
// 增加文本监听器.
addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void afterTextChanged(Editable s) {
setDrawable();
}
});
}

// 输入框右边的图标显示控制
private void setDrawable() {
if (length() == 0) {
setCompoundDrawables(mLeft, mTop, null, mBottom);
} else {
setCompoundDrawables(mLeft, mTop, mRight, mBottom);
}
}

@Override
public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
if (mLeft == null) {
this.mLeft = left;
}
if (mTop == null) {
this.mTop = top;
}
if (mRight == null) {
this.mRight = right;
}
if (mBottom == null) {
this.mBottom = bottom;
}
super.setCompoundDrawables(left, top, right, bottom);
}

// 输入事件处理
@Override
public boolean onTouchEvent(MotionEvent event) {


if (mRight != null && event.getAction() == MotionEvent.ACTION_UP) {
this.mBounds = mRight.getBounds();
mRight.getIntrinsicWidth();
int eventX = (int) event.getX();
int width = mBounds.width();
int right = getRight();
int left = getLeft();
if (eventX > (right - width * 2 - left)) {
setText("");
event.setAction(MotionEvent.ACTION_CANCEL);
}
}
return super.onTouchEvent(event);
}

@Override
protected void finalize() throws Throwable {
super.finalize();
this.mLeft = null;
this.mTop = null;
this.mRight = null;
this.mBottom = null;
this.mBounds = null;
}

}

接下来是右边的字母布局控件

package com.mulu.widget;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.mulu.activity.R;

public class SideBar extends View {
// 触摸事件
private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
// 26个字母
public static 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" };
private int choose = -1;// 选中
private Paint paint = new Paint();
private TextView mTextDialog;

public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}


public SideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}


public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
}


public SideBar(Context context) {
super(context);
}


/**
* 重写这个方法
*/
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++) {
if (!isInEditMode()) {
paint.setColor(Color.parseColor("#838383"));
}
paint.setAntiAlias(true);
paint.setTextSize(20);
// 选中的状态
if (i == choose) {
paint.setColor(getResources().getColor(R.color.yellow_light));
paint.setFakeBoldText(true);
}
// x坐标等于中间-字符串宽度的一半.
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();// 重置画笔
}
}

@SuppressWarnings("deprecation")
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
final int c = (int) (y / getHeight() * b.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
switch (action) {
case MotionEvent.ACTION_UP:
setBackgroundDrawable(new ColorDrawable(0x00000000));
choose = -1;//
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default:
if (oldChoose != c) {
if (c >= 0 && c < b.length) {
if (listener != null) {
listener.onTouchingLetterChanged(b[c]);
}
if (mTextDialog != null) {
mTextDialog.setText(b[c]);
mTextDialog.setVisibility(View.VISIBLE);
}

choose = c;
invalidate();
}
}

break;
}
return true;
}


/**
* 向外公开的方法

* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}

/**
* 接口

* @author coder

*/
public interface OnTouchingLetterChangedListener {
public void onTouchingLetterChanged(String s);
}

}

再次说明一边,需要的资源文件都是放在res下drawable -hdp里面的

下面我们就来实现功能,那么这个时候我们还需要什么呢,我们还需要一个dto,还有一个拼音库的jar包,jar包我也放在我的项目源代码里了,需要的一会自己下载完成的代码,这是我的项目文件存放位置



dto

package com.mulu.dto;

public class PersonDto {
    private Integer userId;// 用户ID
    private String name;// 姓名
    private String head;// 头像
    private String utype;// 用户类型
    private String sortLetters; // 显示数据拼音的首字母
    private String suoxie;// 姓名缩写
    private String signature;// 个性签名

    public final Integer getUserId() {
        return userId;
    }

    public final void setUserId(Integer userId) {
        this.userId = userId;
    }

    public final String getName() {
        return name;
    }


    public final void setName(String name) {
        this.name = name;
    }

    public final String getHead() {
        return head;
    }
    public final void setHead(String head) {
        this.head = head;
    }
    public final String getUtype() {
        return utype;
    }
    public final void setUtype(String utype) {
        this.utype = utype;
    }

    public final String getSortLetters() {
        return sortLetters;
    }
    public final void setSortLetters(String sortLetters) {
        this.sortLetters = sortLetters;
    }

    public final String getSuoxie() {
        return suoxie;
    }

    public final void setSuoxie(String suoxie) {
        this.suoxie = suoxie;
    }

    public final String getSignature() {
        return signature;
    }

    public final void setSignature(String signature) {
        this.signature = signature;
    }

}

下面来看一下主文件中的代码吧

package com.mulu.activity;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import com.mulu.adapter.SchoolFriendMemberListAdapter;
import com.mulu.dto.PersonDto;
import com.mulu.widget.CharacterParser;
import com.mulu.widget.PinyinComparator;
import com.mulu.widget.SideBar;




public class MainActivity extends Activity implements SideBar.OnTouchingLetterChangedListener, TextWatcher {
private SideBar mSideBar;
private TextView mDialog;
private ListView mListView;
private EditText mSearchInput;
private CharacterParser characterParser;// 汉字转拼音
private PinyinComparator pinyinComparator;// 根据拼音来排列ListView里面的数据类
private List<PersonDto> sortDataList = new ArrayList<PersonDto>();
private SchoolFriendMemberListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.school_friend_member);
mSideBar = (SideBar) findViewById(R.id.school_friend_sidrbar);
mDialog = (TextView) findViewById(R.id.school_friend_dialog);
mSearchInput = (EditText) findViewById(R.id.school_friend_member_search_input);

mSideBar.setTextView(mDialog);
mSideBar.setOnTouchingLetterChangedListener(this);
initData();

}

/**
* 初始化数据
*/
private void initData() {
// 实例化汉字转拼音类
characterParser = CharacterParser.getInstance();
pinyinComparator = new PinyinComparator();

PersonDto cuser1 = new PersonDto();
cuser1.setName("阿东");
cuser1.setSortLetters("a");

PersonDto cuser2 = new PersonDto();
cuser2.setName("贝贝");
cuser2.setSortLetters("b");

PersonDto cuser3 = new PersonDto();
cuser3.setName("西西");
cuser3.setSortLetters("x");

PersonDto cuser4 = new PersonDto();
cuser4.setName("楠楠");
cuser4.setSortLetters("n");

PersonDto cuser5 = new PersonDto();
cuser5.setName("君君");
cuser5.setSortLetters("j");

PersonDto cuser6 = new PersonDto();
cuser6.setName("陈诚");
cuser6.setSortLetters("c");

PersonDto cuser7 = new PersonDto();
cuser7.setName("美美");
cuser7.setSortLetters("m");

PersonDto cuser8 = new PersonDto();
cuser8.setName("花园");
cuser8.setSortLetters("h");

PersonDto cuser9 = new PersonDto();
cuser9.setName("天天");
cuser9.setSortLetters("t");

                PersonDto cuser10 = new PersonDto();
                cuser10.setName("八戒");
                cuser10.setSortLetters("b");

sortDataList.add(cuser1);
sortDataList.add(cuser2);
sortDataList.add(cuser3);
sortDataList.add(cuser4);
sortDataList.add(cuser5);
sortDataList.add(cuser6);
sortDataList.add(cuser7);
sortDataList.add(cuser8);
sortDataList.add(cuser9);
sortDataList.add(cuser10);

fillData(sortDataList);
// 根据a-z进行排序源数据
Collections.sort(sortDataList, pinyinComparator);
mAdapter = new SchoolFriendMemberListAdapter(this, sortDataList);
mListView.setAdapter(mAdapter);
mSearchInput.addTextChangedListener(this);

}

@Override
public void onTouchingLetterChanged(String s) {
int position = 0;
// 该字母首次出现的位置
if(mAdapter != null){
position = mAdapter.getPositionForSection(s.charAt(0));
}
if (position != -1) {
mListView.setSelection(position);
}
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// 当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
filterData(s.toString(), sortDataList);
}

@Override
public void afterTextChanged(Editable s) {

}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

/**
* 根据输入框中的值来过滤数据并更新ListView

* @param filterStr
*/
private void filterData(String filterStr, List<PersonDto> list) {
List<PersonDto> filterDateList = new ArrayList<PersonDto>();


if (TextUtils.isEmpty(filterStr)) {
filterDateList = list;
} else {
filterDateList.clear();
for (PersonDto sortModel : list) {
String name = sortModel.getName();
String suoxie = sortModel.getSuoxie();
if (name.indexOf(filterStr.toString()) != -1 || suoxie.indexOf(filterStr.toString()) != -1 || characterParser.getSelling(name).startsWith(filterStr.toString())) {
filterDateList.add(sortModel);
}
}
}

// 根据a-z进行排序
Collections.sort(filterDateList, pinyinComparator);
mAdapter.updateListView(filterDateList);
}

/**
* 填充数据

* @param list
*/
private void fillData(List<PersonDto> list) {
for (PersonDto cUserInfoDto : list) {
if (cUserInfoDto != null && cUserInfoDto.getName() != null) {
String pinyin = characterParser.getSelling(cUserInfoDto.getName());
String suoxie = CharacterParser.getFirstSpell(cUserInfoDto.getName());


cUserInfoDto.setSuoxie(suoxie);
String sortString = pinyin.substring(0, 1).toUpperCase();


if ("1".equals(cUserInfoDto.getUtype())) {// 判断是否是管理员
cUserInfoDto.setSortLetters("☆");
} else if (sortString.matches("[A-Z]")) {// 正则表达式,判断首字母是否是英文字母
cUserInfoDto.setSortLetters(sortString);
} else {
cUserInfoDto.setSortLetters("#");
}
}
}
}
}

这里还需要一个展示listView的一个适配器,我把它单独拿出来了,放在adapter包中的

package com.mulu.adapter;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import com.mulu.dto.PersonDto;
import com.mulu.activity.R;
import java.util.List;

/**
 * 成员列表适配器
 */
public class SchoolFriendMemberListAdapter extends BaseAdapter implements SectionIndexer {

private LayoutInflater inflater;
private Activity mActivity;
private List<PersonDto> list;
public SchoolFriendMemberListAdapter(Activity mActivity, List<PersonDto> list) {
this.mActivity = mActivity;
this.list = list;
}

/**
* 当ListView数据发生变化时,调用此方法来更新ListView

* @param list
*/
public void updateListView(List<PersonDto> list) {
this.list = list;
notifyDataSetChanged();
}

@Override
public int getCount() {
return list.size();
}

@Override
public Object getItem(int position) {
return null;
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.items_person_list, null);
holder = new ViewHolder();
holder.ivHead = (ImageView) convertView.findViewById(R.id.head);
holder.tvTitle = (TextView) convertView.findViewById(R.id.title);
holder.tvLetter = (TextView) convertView.findViewById(R.id.catalog);
holder.tvLine = (TextView) convertView.findViewById(R.id.line);
holder.tvContent = (LinearLayout) convertView.findViewById(R.id.content);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}


final PersonDto dto = list.get(position);


if (dto != null) {
// 根据position获取分类的首字母的Char ascii值
int section = getSectionForPosition(position);
// 如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现
if (position == getPositionForSection(section)) {
holder.tvLetter.setVisibility(View.VISIBLE);
holder.tvLetter.setText("☆".equals(dto.getSortLetters()) ? dto.getSortLetters() + "(管理员)" : dto.getSortLetters());
holder.tvLine.setVisibility(View.VISIBLE);
} else {
holder.tvLetter.setVisibility(View.GONE);
holder.tvLine.setVisibility(View.GONE);
}
holder.tvTitle.setText(dto.getName());
}
return convertView;
}

class ViewHolder {
ImageView ivHead;
TextView tvLetter;
TextView tvTitle;
TextView tvLine;
LinearLayout tvContent;
}

/**
* 根据ListView的当前位置获取分类的首字母的Char ascii值
*/
public int getSectionForPosition(int position) {
return list.get(position).getSortLetters().charAt(0);
}

/**
* 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置
*/
public int getPositionForSection(int section) {
for (int i = 0; i < getCount(); i++) {
String sortStr = list.get(i).getSortLetters();
char firstChar = sortStr.toUpperCase().charAt(0);
if (firstChar == section) {
return i;
}
}
return -1;
}

@Override
public Object[] getSections() {
return null;
}

}

还需要一个PinyinComparator实现Comparator的类

package com.mulu.widget;

import com.mulu.dto.PersonDto;
import java.util.Comparator;
public class PinyinComparator implements Comparator<PersonDto> {

@Override
public int compare(PersonDto o1, PersonDto o2) {
if (o1.getSortLetters().equals("☆")) {
return -1;
} else if (o2.getSortLetters().equals("☆")) {
return 1;
} else if (o1.getSortLetters().equals("#")) {
return -1;
} else if (o2.getSortLetters().equals("#")) {
return 1;
} else {
return o1.getSortLetters().compareTo(o2.getSortLetters());
}
}

}

好了基本的代码也就这些了,下面是我完整代码的地址,欢迎下载http://download.csdn.net/detail/shaozucheng/8204905



版权声明:本文为博主原创文章,未经博主允许不得转载。

私人通讯录Demo - iOS

Github地址:PersonalContacts-私人通讯录写在前面本 Demo 简单演示了一些小控件的应用。 比如导航栏的设置; 界面全部是代码写的,没有用stroyboard。 UITextFi...
  • zyq522376829
  • zyq522376829
  • 2016年09月06日 09:22
  • 1195

Android 仿微信联系人Demo(自定义View,Viewgroup)

上周在某博客发现博主分享了一篇很经典的程序---------联系人效果。感觉很神秘很强大,但在阅读和理解博主的demo的同时也发现了一些冗余和不完美。于是带着宝宝的痛一咬牙自己开工了,大约花了一周的时...
  • fesdgasdgasdg
  • fesdgasdgasdg
  • 2016年08月20日 15:57
  • 3916

自定义实现微信通讯录效果View

前言在使用App过程中,经常会有使用到联系人或城市列表的场景,其实这两种效果是一样的,都是右边有个索引列表,点击索引列表可跳转到指定字母开头的联系人或城市上去,同时向上滑动过程中头部会有个显示当前联系...
  • huyongl1989
  • huyongl1989
  • 2016年12月31日 16:34
  • 1274

android通讯录开发(粗糙实现微信通讯录的功能)

主要实现功能: 一、实现名单的中英文混合排序,按照联系人名单的首字母进行分类。 二、实现右侧的字母导航功能,根据字母定位相应的名单组,方便查找。 三、实现搜索功能(1)输入字母进行搜...
  • xu_song
  • xu_song
  • 2014年03月03日 20:59
  • 1814

android仿微信联系人 首字母分类(旧)

效果图 实现这个效果,需要三个知识点 : 1:将字符串 进行拼音分类拼音 2:ExpandableListView 二级扩展列表 3:右边字母分类View 4:中间的绿色对话框popupWindo...
  • b275518834
  • b275518834
  • 2013年07月14日 23:45
  • 22748

类似于微信的在一个Activity中界面的相互切换

今天我们讲一个例子,主要的实现是通过点击界面下方的不同按钮,然后在上面显示不同的Fragment布局 步骤: 1、在res文件夹下新建一个drawable文件,然后在里面创建4个新的Selector类...
  • kuangxiaoguo0123
  • kuangxiaoguo0123
  • 2015年09月25日 22:47
  • 1971

仿IOS通讯录效果,实现获取手机通讯录、字母排序显示、搜索联系人、拨打电话

1.使用UITableView,实现联系人字母排序、点击字母跳转显示联系人组目录; 2.使用UISearchController,实现联系搜索,动态显示符合查询的联系人; 3.点击通讯录列表项,显示联...
  • u011622479
  • u011622479
  • 2016年04月28日 17:47
  • 7928

Android中使用ExpandableListView实现微信通讯录界面(完善仿微信APP)

之前的博文《Android中使用ExpandableListView实现好友分组》我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信APP来对E...
  • panhouye
  • panhouye
  • 2016年12月28日 22:12
  • 417

Android小Demo:高仿微信5.3界面

近日看慕课讲解的一个高仿微信5.2.1主界面及消息提醒的案例,感觉也不是很难,就顺便也写了一下。 涉及知识点如下: 怎样在代码中动态设置一个控件,必然ImageView 的宽度。 怎样获取屏幕宽度。...
  • DucklikeJAVA
  • DucklikeJAVA
  • 2015年10月05日 18:11
  • 1017

Android使用ExpandableListView实现仿微信通讯录UI

这几天一直想着写一篇自己的博客,也不知道些什么好,刚好翻到微信通讯录界面,就想着何不写一下呢。话不多说,直接切入主题。先看下效果: 初始页面是这样的: 按下之后是这样的: ...
  • wangins
  • wangins
  • 2018年01月31日 16:48
  • 30
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:类似于微信通讯录的界面Demo
举报原因:
原因补充:

(最多只允许输入30个字)