自定义View学习之12/4(仿IOS联系人列表)

来源:互联网 发布:下载商城软件 编辑:程序博客网 时间:2024/05/20 05:26

今天我们来仿一个IOS联系人列表,首先得支持字母行置顶(有阴影和没阴影置顶)。支持右边字母视图点击和滑动到置顶的字母行。搜索栏支持中英文搜索。有了这个需求,我们现在就得开始动手做。

动手前我们得理清思路:
1、需要重写一个有置顶功能的列表控件;
2、需要写一个右边字母控件竖排视图;
3、支持中英文就得把中文转成拼音,这里我用了google系统用的HanziToPinyin这个类来做。
4、然后我们得有一个全部联系人的集合。一个显示UI联系人的集合和一个每个联系人的首字母第一次出现的Map集合来保存下标;
5、先贴下动态图,马上开始动手做;

这里写图片描述

首先我们先写右边的字母视图代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):

package com.example.phonedemo;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.view.Gravity;import android.view.MotionEvent;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;public class LetterIndexView extends LinearLayout {    /**     * 上下文环境     */    private Context context;    /**     * 字母控件     */    private TextView[] lettersTxt = new TextView[28];    /**     * 触碰字母索引接口     */    private OnTouchLetterIndex touchLetterIndex;    public LetterIndexView(Context context) {        super(context);        this.context = context;    }    public LetterIndexView(Context context, AttributeSet attrs) {        super(context, attrs);        this.context = context;    }    /**     * 初始化控件.     */    public void init(OnTouchLetterIndex touchLetterIndex) {        this.touchLetterIndex = touchLetterIndex;        this.setBackgroundColor(getResources().getColor(R.color.transparent));        this.setOrientation(LinearLayout.VERTICAL);        this.setGravity(Gravity.CENTER);        //创建字母控件实例        for (int i = 0; i < 28; i++) {            lettersTxt[i] = new TextView(context);            lettersTxt[i].setGravity(Gravity.CENTER);            char tab = (char) (i + 63);            if (i == 0)                lettersTxt[i].setText("!");            else if (i == 1)                lettersTxt[i].setText("#");            else                lettersTxt[i].setText("" + tab);            lettersTxt[i].setPadding(10, 0,10, 0);            lettersTxt[i].setBackgroundColor(0xFF0000);            lettersTxt[i].setTextSize(12);            lettersTxt[i].setTextColor(Color.BLACK);            LayoutParams letterParam = new LayoutParams(LayoutParams.WRAP_CONTENT, 0);            letterParam.weight = 1;            lettersTxt[i].setLayoutParams(letterParam);            this.addView(lettersTxt[i]);        }        this.setOnTouchListener(new OnTouchListener() {            //移动y轴的距离            private int y;            //控件的高度            private int height;            //按到了哪个字母            private String tab;            @Override            public boolean onTouch(View v, MotionEvent event) {                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        //按下时改变背景和字体颜色                        setTextColor(Color.WHITE);                        LetterIndexView.this.setBackgroundColor(getResources().getColor(R.color.ff7c7c7c));                    case MotionEvent.ACTION_MOVE:                        // 获取触发事件点的纵坐标                        y = (int) event.getY();                         height = LetterIndexView.this.getHeight();                        int location = (int) (y / (height / 28) + 0.5f);                        if (location == 0) {                            tab = "!";                        } else if (location == 1) {                            tab = "#";                        } else if (location > 0 && location <= 27) {                            tab = String.valueOf((char) (location + 63));                        }                        if (LetterIndexView.this.touchLetterIndex!=null) {                            //调用接口                            LetterIndexView.this.touchLetterIndex.touchLetterWitch(tab);                        }                        break;                    case MotionEvent.ACTION_UP:                        LetterIndexView.this.setBackgroundColor(getResources().getColor(R.color.transparent));                        if (LetterIndexView.this.touchLetterIndex!=null) {                            //调用接口                            LetterIndexView.this.touchLetterIndex.touchFinish();                        }                        setTextColor(Color.BLACK);                        break;                }                return true;            }        });    }    /**     * 设置字体颜色     */    private void setTextColor(int color) {        for (int i = 0; i < 28; i++) {            lettersTxt[i].setTextColor(color);        }    }    /**     * 触碰字母索引接口     */    public interface OnTouchLetterIndex {        /**         * 触摸字母空间接口.         */        void touchLetterWitch(String letter);        /**         * 结束查询         */        void touchFinish();    }}

由于时间原因自己也没研究置顶功能的listview,于是我拿的网上开源控件这里就不贴出来了。主界面代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):

package com.example.phonedemo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.text.Editable;import android.text.TextUtils;import android.text.TextWatcher;import android.view.View;import android.widget.AdapterView;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import java.text.Collator;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;public class MainActivity extends Activity {    /**     * 搜索栏     */    EditText edit_search;    /**     * 列表     */    PinnedSectionListView listView;    /**     * 右边字母列表     */    LetterIndexView letterIndexView;    /**     * 中间显示右边按的字母     */    TextView txt_center;    /**     * 所有名字集合     */    private ArrayList<PhoneBean> list_all;    /**     * 显示名字集合     */    private ArrayList<PhoneBean> list_show;    /**     * 列表适配器     */    private PhoneAdapter adapter;    /**     * 保存名字首字母     */    public HashMap<String, Integer> map_IsHead;    /**     * item标识为0     */    public static final int ITEM = 0;    /**     * item标题标识为1     */    public static final int TITLE = 1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        edit_search = (EditText) findViewById(R.id.edit_search);        listView = (PinnedSectionListView) findViewById(R.id.phone_listview);        letterIndexView = (LetterIndexView) findViewById(R.id.phone_LetterIndexView);        txt_center = (TextView) findViewById(R.id.phone_txt_center);        initView();        initData();    }    private void initView() {        // 输入监听        edit_search.addTextChangedListener(new TextWatcher() {            @Override            public void beforeTextChanged(CharSequence charSequence, int i,                    int i1, int i2) {            }            @Override            public void onTextChanged(CharSequence charSequence, int i, int i1,                    int i2) {            }            @Override            public void afterTextChanged(Editable editable) {                list_show.clear();                map_IsHead.clear();                //把输入的字符改成大写                String search = editable.toString().trim().toUpperCase();                if (TextUtils.isEmpty(search)) {                    for (int i = 0; i < list_all.size(); i++) {                        PhoneBean bean = list_all.get(i);                        //中文字符匹配首字母和英文字符匹配首字母                            if (!map_IsHead.containsKey(bean.getHeadChar())) {// 如果不包含就添加一个标题                                PhoneBean bean1 = new PhoneBean();                                // 设置名字                                bean1.setName(bean.getName());                                // 设置标题type                                bean1.setType(MainActivity.TITLE);                                list_show.add(bean1);                                // map的值为标题的下标                                map_IsHead.put(bean1.getHeadChar(),                                        list_show.size() - 1);                            }                            // 设置Item type                            bean.setType(MainActivity.ITEM);                            list_show.add(bean);                    }                } else {                    for (int i = 0; i < list_all.size(); i++) {                        PhoneBean bean = list_all.get(i);                        //中文字符匹配首字母和英文字符匹配首字母                        if (bean.getName().indexOf(search) != -1|| bean.getName_en().indexOf(search) != -1) {                            if (!map_IsHead.containsKey(bean.getHeadChar())) {// 如果不包含就添加一个标题                                PhoneBean bean1 = new PhoneBean();                                // 设置名字                                bean1.setName(bean.getName());                                // 设置标题type                                bean1.setType(MainActivity.TITLE);                                list_show.add(bean1);                                // map的值为标题的下标                                map_IsHead.put(bean1.getHeadChar(),                                        list_show.size() - 1);                            }                            // 设置Item type                            bean.setType(MainActivity.ITEM);                            list_show.add(bean);                        }                    }                }                adapter.notifyDataSetChanged();            }        });        // 右边字母竖排的初始化以及监听        letterIndexView.init(new LetterIndexView.OnTouchLetterIndex() {            //实现移动接口            @Override            public void touchLetterWitch(String letter) {                // 中间显示的首字母                txt_center.setVisibility(View.VISIBLE);                txt_center.setText(letter);                // 首字母是否被包含                if (adapter.map_IsHead.containsKey(letter)) {                    // 设置首字母的位置                    listView.setSelection(adapter.map_IsHead.get(letter));                }            }            //实现抬起接口            @Override            public void touchFinish() {                txt_center.setVisibility(View.GONE);            }        });        /** listview点击事件 */        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> adapterView, View view,int i, long l) {                if (list_show.get(i).getType() == MainActivity.ITEM) {// 标题点击不给操作                    Toast.makeText(MainActivity.this,list_show.get(i).getName(), Toast.LENGTH_LONG).show();                }            }        });        // 设置标题部分有阴影        // listView.setShadowVisible(true);    }    protected void initData() {        list_all = new ArrayList<PhoneBean>();        list_show = new ArrayList<PhoneBean>();        map_IsHead = new HashMap<String, Integer>();        adapter = new PhoneAdapter(MainActivity.this, list_show, map_IsHead);        listView.setAdapter(adapter);        // 开启异步加载数据        new Thread(runnable).start();    }    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            adapter.notifyDataSetChanged();        }    };    private Runnable runnable = new Runnable() {        @Override        public void run() {            String[] str = getResources().getStringArray(R.array.phone_all);            for (int i = 0; i < str.length; i++) {                PhoneBean cityBean = new PhoneBean();                cityBean.setName(str[i]);                list_all.add(cityBean);            }            //按拼音排序            MemberSortUtil sortUtil = new MemberSortUtil();            Collections.sort(list_all, sortUtil);            // 初始化数据,顺便放入把标题放入map集合            for (int i = 0; i < list_all.size(); i++) {                PhoneBean cityBean = list_all.get(i);                if (!map_IsHead.containsKey(cityBean.getHeadChar())) {// 如果不包含就添加一个标题                    PhoneBean cityBean1 = new PhoneBean();                    // 设置名字                    cityBean1.setName(cityBean.getName());                    // 设置标题type                    cityBean1.setType(MainActivity.TITLE);                    list_show.add(cityBean1);                    // map的值为标题的下标                    map_IsHead.put(cityBean1.getHeadChar(),list_show.size() - 1);                }                list_show.add(cityBean);            }            handler.sendMessage(handler.obtainMessage());        }    };    public class MemberSortUtil implements Comparator<PhoneBean> {        /**         * 按拼音排序         */        @Override        public int compare(PhoneBean lhs, PhoneBean rhs) {            Comparator<Object> cmp = Collator                    .getInstance(java.util.Locale.CHINA);            return cmp.compare(lhs.getName_en(), rhs.getName_en());        }    }}

然而适配器则是用的getViewTypeCount()这种方式,缓存2个不同的view来实现,个人认为是已经不能再优化的方案了。如有更好方案欢迎提出来,我加以改正,代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):

package com.example.phonedemo;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import java.util.ArrayList;import java.util.HashMap;import com.example.phonedemo.PinnedSectionListView.PinnedSectionListAdapter;public class PhoneAdapter extends BaseAdapter implements PinnedSectionListAdapter {    private LayoutInflater layoutInflater;    /**     * 数据集     */    private ArrayList<PhoneBean> list;    /**     * 首字母     */    public HashMap<String, Integer> map_IsHead;    public PhoneAdapter(Context context, ArrayList<PhoneBean> list, HashMap<String, Integer> map_IsHead) {        this.list = list;        this.map_IsHead = map_IsHead;        layoutInflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return list.size();    }    @Override    public Object getItem(int i) {        return list.get(i);    }    @Override    public long getItemId(int i) {        return i;    }    @Override    public int getViewTypeCount() {        return 2;    }    @Override    public int getItemViewType(int position) {        return list.get(position).getType();    }    //实现自定义listview的接口    @Override    public boolean isItemViewTypePinned(int viewType) {        return viewType == MainActivity.TITLE;    }    @Override    public View getView(int i, View view, ViewGroup viewGroup) {        final ViewHolder viewHolder;        switch (getItemViewType(i)) {            case MainActivity.ITEM:                if (view == null) {                    viewHolder = new ViewHolder();                    view = layoutInflater.inflate(R.layout.item_phone_item, null);                    viewHolder.txt = (TextView) view.findViewById(R.id.item_phone_txt_name);                    view.setTag(viewHolder);                } else {                    viewHolder = (ViewHolder) view.getTag();                }                //设置名字                viewHolder.txt.setText(list.get(i).getName());                break;            case MainActivity.TITLE:                if (view == null) {                    viewHolder = new ViewHolder();                    view = layoutInflater.inflate(R.layout.item_phone_title, null);                    viewHolder.txt = (TextView) view.findViewById(R.id.item_phone_txt_head);                    view.setTag(viewHolder);                } else {                    viewHolder = (ViewHolder) view.getTag();                }                //设置标题                viewHolder.txt.setText(list.get(i).getHeadChar());                break;        }        return view;    }    private class ViewHolder {        private TextView txt;    }}

而中文切成拼音,我则是直接放到bean里面来做,方便点代码如下(由于个人习惯写代码一般喜欢加上注释,这里不加以详细描述):

package com.example.phonedemo;import java.util.ArrayList;import android.text.TextUtils;public class PhoneBean {    /**     * 城市名字首字母     */    private String headChar;    /**     * 城市id     */    private String city_id;    /**     * 省id     */    private String pro_id;    /**     * 城市名字     */    private String name;    /**     * 城市字母名字     */    private String name_en;    /**     * 是否是标题     */    private int type;    public String getHeadChar() {        return headChar;    }    public String getName() {        return name;    }    public String getName_en() {        return name_en;    }    public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }    public void setName(String name) {        this.name = name;        name_en = getPinYin(name);//获取字母名称        name_en = name_en.toUpperCase();//把小写字母换成大写字母        if (!TextUtils.isEmpty(name_en)) {            char head = name_en.charAt(0);            if (head < 'A' || head > 'Z') {                head = '#';            }            headChar = head + "";        }    }    /**     * 汉字转换拼音,字母原样返回,都转换为小写     */    public String getPinYin(String input) {        ArrayList<HanziToPinyin.Token> tokens = HanziToPinyin.getInstance().get(input);        StringBuilder sb = new StringBuilder();        if (tokens != null && tokens.size() > 0) {            for (HanziToPinyin.Token token : tokens) {                if (token.type == HanziToPinyin.Token.PINYIN) {                    sb.append(token.target);                } else {                    sb.append(token.source);                }            }        }        return sb.toString().toLowerCase();    }    public String getCity_id() {        return city_id;    }    public void setCity_id(String city_id) {        this.city_id = city_id;    }    public String getPro_id() {        return pro_id;    }    public void setPro_id(String pro_id) {        this.pro_id = pro_id;    }}

最后贴下xml界面布局如下:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <EditText        android:id="@+id/edit_search"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@android:color/white"        android:drawableLeft="@drawable/edit_search"        android:drawablePadding="10dp"        android:hint="请输入名字或者拼音"        android:padding="15dp"        android:singleLine="true"        android:textColor="@android:color/black"        android:textColorHint="#ff333333"        android:textSize="16sp" />    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_below="@id/edit_search"        android:background="@android:color/white" >        <com.example.phonedemo.PinnedSectionListView            android:id="@+id/phone_listview"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:divider="@null" />        <com.example.phonedemo.LetterIndexView            android:id="@+id/phone_LetterIndexView"            android:layout_width="wrap_content"            android:layout_height="match_parent"            android:layout_alignParentRight="true" />    </RelativeLayout>    <TextView        android:id="@+id/phone_txt_center"        android:layout_width="80dp"        android:layout_height="80dp"        android:layout_centerInParent="true"        android:background="#30000000"        android:gravity="center"        android:textColor="#ff7c7c7c"        android:visibility="gone" /></RelativeLayout>

好了,只有部分代码没有贴出来,我觉得是无关紧要的,所以就没贴。其他的基本上都贴出来了。我写代码的习惯就是边写代码边注释,所以在博客上就不浪费时间做重复的事情了。望见谅;

希望大家多多关注我的博客,多多支持我。
如有好意见或更好的方式欢迎留言谈论。

尊重原创转载请注明:(http://blog.csdn.net/u013895206) !

下面是地址传送门:
http://download.csdn.net/detail/u013895206/9273575

0 0
原创粉丝点击