【Android】安卓开发实战之仿iPhone通讯录demo的移植和优化

demo下载地址:http://download.csdn.net/detail/u013895206/9273575

首先来看一下demo和我移植优化后的对比效果

demo:


优化后:


可以看到,针对demo,我做了移植后,主要做了一下优化:

1、优化键盘设置。

2、将顶部固定的搜索框折叠到了搜索图标中。

3、将Activity中的demo,移到了Fragment碎片中。

4、将右侧过长的字母列表向中间缩进。


1、优化键盘设置:

原demo弹出的键盘点击空白处不会收回,功能键是“下一步”,而不是“完成”、“搜索”这样的功能键,用户输入后,只能点击返回键收回软键盘,体验不好。至于软键盘的设置,请参考我之前的博客。

2、搜索框的优化:

顶部固定搜索框是比较省事,但是会占用屏幕空间,使得显示列表减少,用户体验不好。搜索框弹出效果,请参考我之前的博客。

3、将Activity中的demo,移到了Fragment碎片中。移植代码如下:

/**
 * 这个类将Fragment和包含ListView和标题栏的布局进行绑定
 */
public class FragmentOfLinkMan extends Fragment implements View.OnClickListener {
    private LinearLayout ll_Linkman;//“联系人”标题栏布局,现在可见,点击搜索图标后不可见
    private LinearLayout ll_searchman;//“搜索框”布局,现在不可见,点击搜索图标后可见。
    private Dialog dialogOfGroup;//该对话框用显式点击分组图标后的列表项
    private EditText searchEdit;

    EditText edit_search;//搜索栏
    PinnedSectionListView listView;//列表
    LetterIndexView letterIndexView;//右边字母列表
    TextView txt_center;//中间显示右边按的字母
    private ArrayList<PhoneBean> list_all;//所有名字集合
    private ArrayList<PhoneBean> list_show;//名字集合+英文首字母栏的集合
    private AdapterOfLinkManListView adapter;//列表适配器
    public HashMap<String, Integer> map_IsHead;//保存名字首字母
    public static final int ITEM = 0;//item标识为0
    public static final int TITLE = 1;//item标题标识为1

    /**
     * 这个方法可以进行适配器和布局的绑定,初始化Item点击监听
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup contain, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.listview_of_lingkman, contain, false);

        ll_Linkman = (LinearLayout) view.findViewById(R.id.ll_of_LinkMan);
        ll_searchman = (LinearLayout) view.findViewById(R.id.ll_of_search);

        ImageView searchImage = (ImageView) view.findViewById(R.id.m_searchImageId);
        ImageView sorteImage = (ImageView) view.findViewById(R.id.m_sortImageId);
        TextView cancelTextView = (TextView) view.findViewById(R.id.textView_search_cancel);
        searchEdit = (EditText) view.findViewById(R.id.edit_of_search);
        searchImage.setOnClickListener(this);
        sorteImage.setOnClickListener(this);
        cancelTextView.setOnClickListener(this);
        searchEdit.setOnClickListener(this);

        //----------------------------在碎片中添加对话框--------------------------------------------
        UsingMain activity = (UsingMain) getActivity();//不在Fragment中,这行去掉
        dialogOfGroup = new Dialog(activity);//不在Fragment中,activity设置为需要显示对话框的活动
        dialogOfGroup.setContentView(R.layout.dailog_group_layout);
        Window dialogWindow = dialogOfGroup.getWindow();
        WindowManager.LayoutParams lp = dialogWindow.getAttributes();
        dialogWindow.setGravity(Gravity.LEFT | Gravity.TOP);
        lp.x = 40; // 新位置X坐标
        lp.y = 120; // 新位置Y坐标
        lp.width = 280; // 宽度
        lp.height = 650; // 高度
        lp.alpha = 1.0f; // 透明度
        dialogWindow.setBackgroundDrawable(getResources().getDrawable(R.drawable.round_dialog_box));
        dialogWindow.setAttributes(lp);
        //------------------------------------------------------------------------------------------

        //----------------------------在碎片中引入PhoneDemo的相关布局-------------------------------
        edit_search = (EditText) view.findViewById(R.id.edit_of_search);//获取搜索栏组件的对象
        listView = (PinnedSectionListView) view.findViewById(R.id.listViewOfLinkManId);//获取列表组件的对象
        letterIndexView = (LetterIndexView) view.findViewById(R.id.phone_LetterIndexView);//获取右侧字母栏的对象
        txt_center = (TextView) view.findViewById(R.id.phone_txt_center);//获取点击右侧,中间显示的文本组件的对象

        initView();//初始化布局中的组件
        initData();//数据初始化
        //------------------------------------------------------------------------------------------
        return view;
    }

    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();//清空ListView组件中的内容
                map_IsHead.clear();//清空保存名字和首字母的HashMap
                //把输入的字符改成大写
                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(FragmentOfLinkMan.TITLE);
                            list_show.add(bean1);
                            // map的值为标题的下标
                            map_IsHead.put(bean1.getHeadChar(), list_show.size() - 1);
                        }
                        // 设置Item type
                        bean.setType(FragmentOfLinkMan.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(FragmentOfLinkMan.TITLE);
                                list_show.add(bean1);
                                // map的值为标题的下标
                                map_IsHead.put(bean1.getHeadChar(),
                                        list_show.size() - 1);
                            }
                            // 设置Item type
                            bean.setType(FragmentOfLinkMan.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() == FragmentOfLinkMan.ITEM) {// 标题点击不给操作
                    UsingMain usingMain = (UsingMain)getActivity();
                    Toast.makeText(usingMain,list_show.get(i).getName(), Toast.LENGTH_LONG).show();
                }
            }
        });
        // 设置标题部分有阴影
        listView.setShadowVisible(true);
    }

    protected void initData() {
        list_all = new ArrayList<PhoneBean>();//list集合,元素只能是PhoneBean的对象
        list_show = new ArrayList<PhoneBean>();//list集合,元素只能是PhoneBean的对象
        map_IsHead = new HashMap<String, Integer>();//HashMap的集合,key对是String-Integer
        UsingMain usingMain = (UsingMain) getActivity();
        adapter = new AdapterOfLinkManListView(usingMain, list_show, map_IsHead);//ListView的适配器
        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);//把资源文件array里的数据加载到str数组里
            /**
             * 把str数组里的名字添加到PhoneBean类里的String name
             * 如果name是汉字就转化为拼音,然后把name存到en_name里
             * 同时把首字母添加到String headchar里
             * for里可以反复创建同名对象的原因在于
             * cityBean引用的作用域在单次循环里,下次循环时该引用即失效
             * 而它指向的对象在堆内存中失去了强引用,也面临着被回收的境地
             * 然而ArrayList集合添加了它,List集合跟数组差不多,所以集合的下标
             * 应该具备引用一般的强引用作用,所以被添加的PhoneBean对象不会被回收
             */
            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的list集合
                PhoneBean cityBean = list_all.get(i);
                //检查HashMap集合里的key,是否有和添加到了list集合的PhoneBean元素的headchar一致
                if (!map_IsHead.containsKey(cityBean.getHeadChar())) {// 如果不包含就添加一个标题
                    PhoneBean cityBean1 = new PhoneBean();
                    // 设置名字
                    cityBean1.setName(cityBean.getName());
                    // 设置标题type
                    cityBean1.setType(FragmentOfLinkMan.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());
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.m_searchImageId://点击“搜索”图标触发的事件
                ll_Linkman.setVisibility(View.GONE);
                ll_searchman.setVisibility(View.VISIBLE);
                //---------------------------设置引入图片的尺寸---------------------------------------------
                Drawable searchPic = getResources().getDrawable(R.drawable.redsearch);
                searchPic.setBounds(0, 0, 40, 40);
                searchEdit.setCompoundDrawables(searchPic, null, null, null);
                //------------------------------------------------------------------------------------------
                break;
            case R.id.m_sortImageId://点击“组别”图标触发的事件
                dialogOfGroup.show();//显式组别列表框
                break;
            case R.id.textView_search_cancel://点击“取消”字样触发的事件
                ll_Linkman.setVisibility(View.VISIBLE);
                ll_searchman.setVisibility(View.GONE);
                searchEdit.setText("");//清空输入框的内容
                //----------------------------------收起软键盘--------------------------------------------------------
                UsingMain usingMain = (UsingMain)getActivity();
                InputMethodManager imm = (InputMethodManager)usingMain.getSystemService(Context.INPUT_METHOD_SERVICE);
                if (imm != null) {
                    imm.hideSoftInputFromWindow(usingMain.getWindow().getDecorView().getWindowToken(),0);
                }
                //-----------------------------------------------------------------------------------------------------
                break;
            default:
                break;
        }
    }
}
至于怎么在Activity中加载Fragment,请参考我之前的博客。


4、将右侧过长的字母列表向中间缩进。

public class LetterIndexView extends LinearLayout {

    /**
     * 上下文环境
     */
    private Context context;
    /**
     * 字母控件
     */
    private TextView[] lettersTxt = new TextView[28];
    /**
     * 触碰字母索引接口
     */
    private OnTouchLetterIndex touchLetterIndex;

    private float textHight=0;

    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);
            //----------------这里我对demo进行了修改------------------------------------------------
            lettersTxt[i].setPadding(0, 0,0, 0);
            lettersTxt[i].setTextSize(10);
            //--------------------------------------------------------------------------------------
            lettersTxt[i].setTextColor(Color.BLACK);
            this.addView(lettersTxt[i]);
        }
        this.setOnTouchListener(new OnTouchListener() {
        	//移动y轴的距离
            private float 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:
                    	// 获取触发事件点的纵坐标
                        //----------------这里我对demo进行了修改------------------------------------
                        /**
                         * 先获取包含字母列表的线性布局的总高度height和单个字母的高度TextHight
                         * M是线性布局的顶端(坐标0)到第一个字符“!”的举例
                         * y是线性布局顶端到点击位置的距离
                         * x = y - M相当于将原点在线性布局的顶端重置为第一个字符“!”的顶端
                         * (y>=M)&&(y<=height-M)意思是只有在字符列表处点击,才会响应
                         * location =  (int)(x / textHight)可以定位出现在点击的是第几个字符
                         */
                        textHight = lettersTxt[0].getHeight();
                        height = LetterIndexView.this.getHeight();
                        float M = (height-textHight*28)/2;
                        y = event.getY();
                        float x = y - M;
                        if((y>=M)&&(y<=height-M)){
                            int location =  (int)(x / textHight);
                            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();

    }


}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值