您的位置:首页 > 移动开发 > Android开发

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

2017-02-22 10:57 393 查看
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();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐