Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能
Posted WYH_Healer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能相关的知识,希望对你有一定的参考价值。
android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能
本文中阐述如何自定义EditText实现搜索框自定义的样式以及挤压字母的思路等
- 自定义EditText
- 相关的drawable文件
- 主界面以及相关的适配器
- 结果展示
定义要呈现的EditText的样式
public class ClearEditText extends EditText implements
OnFocusChangeListener, TextWatcher
/**
* 定义删除按钮的引用
* */
private Drawable mClearDrawable;
public ClearEditText(Context context)
this(context, null);
public ClearEditText(Context context, AttributeSet attrs)
//这个构造方法很重要,不加这个很多属性不能再XML中定义
this(context, attrs, android.R.attr.editTextStyle);
public ClearEditText(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
init();
private void init()
//获取EidtText的DrawableRight,加入没有设置我们就使用自己的默认图片,getCompoundDrawables返回的是四个方向的图片信息
mClearDrawable = getCompoundDrawables()[2];
if (mClearDrawable == null)
mClearDrawable = getResources()
.getDrawable(R.mipmap.emotionstore_progresscancelbtn);
//设置图片的宽高,前两个是组件左上角在容器中的坐标 后两个是组件的宽度和高度
mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight());
//默认初始化情况清空当前的图片信息
setClearIconVisible(false);
//类似于文本框里面hint文字在初始化的时候显示或者隐藏的操作,就要用到setOnFocusChangeListener,同时监听有字符出现时就显示删除按钮
setOnFocusChangeListener(this);
addTextChangedListener(this);
/**
* 判断是否有删除按钮
* 设置删除按钮的点击事件:制空
* */
@Override
public boolean onTouchEvent(MotionEvent event)
if (getCompoundDrawables()[2] != null)
if (event.getAction() == MotionEvent.ACTION_UP)
boolean touchable = event.getX() > (getWidth()
- getPaddingRight() - mClearDrawable.getIntrinsicWidth())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (touchable)
this.setText("");
return super.onTouchEvent(event);
/**
* 当焦点发生变化的情况设置删除按钮
* */
@Override
public void onFocusChange(View v, boolean hasFocus)
if (hasFocus)
setClearIconVisible(getText().length() > 0);
else
setClearIconVisible(false);
/**
* 设置删除按钮的显示或隐藏场景
* */
protected void setClearIconVisible(boolean visible)
Drawable right = visible ? mClearDrawable : null;
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
/**
* 判断是否有字符,如果有存在字符添加删除按钮
* */
@Override
public void onTextChanged(CharSequence s, int start, int count,
int after)
setClearIconVisible(s.length() > 0);
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after)
@Override
public void afterTextChanged(Editable s)
public void setShakeAnimation()
this.setAnimation(shakeAnimation(5));
public static Animation shakeAnimation(int counts)
Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0);
translateAnimation.setInterpolator(new CycleInterpolator(counts));
translateAnimation.setDuration(1000);
return translateAnimation;
ClearEditText设置获取焦点时的正在输入的样式以及背景样式相关的drawable文件
line_vertiacl文件
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/black"/>
<size android:width="1dp"/>
</shape>
edit_background文件
stroke是外边框的边框宽度以及颜色
solid是填充的内部色彩
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white"/>
<stroke android:width="1dp"
android:color="@color/green_light"/>
<corners android:radius="30dp"/>
<padding
android:bottom="6dp"
android:top="6dp"
android:left="12dp"
android:right="6dp"
/>
</shape>
自定义SideBar实现右边的26个字母显示快捷选取目标
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++)
//设置每一个字母的样式
paint.setColor(Color.rgb(33, 65, 98));
// paint.setColor(Color.WHITE);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
paint.setTextSize(30);
//选中状态
if (i == choose)
paint.setColor(Color.parseColor("#3399ff"));
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();// 重置画笔
/**
* 点击的时候的状态
* */
@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:
setBackgroundResource(R.drawable.sidebar_background);
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);
主界面的布局展示
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:focusable="true"
android:focusableInTouchMode="true"
tools:context="demo.test.com.myself.sortlistview.SortActivity">
<demo.test.com.myself.sortlistview.ClearEditText
android:layout_margin="6dp"
android:textCursorDrawable="@drawable/line_vertiacl"
android:id="@+id/filter_edit"
android:layout_marginTop="5dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/edit_background"
android:drawableLeft="@mipmap/search_bar_icon_normal"
android:hint="请输入关键字"
android:singleLine="true"
android:textSize="15.0dip" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="@+id/country_lvcountry"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:divider="@null" />
<!--设置就是设置顶部的字母排序-->
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="#E0E0E0" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="#454545"
android:paddingLeft="5dip"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:text="A"/>
</LinearLayout>
<TextView
android:id="@+id/dialog"
android:layout_width="80.0dip"
android:layout_height="80.0dip"
android:layout_gravity="center"
android:background="@drawable/toast_bg"
android:gravity="center"
android:textColor="#ffffffff"
android:textSize="30.0dip"
android:visibility="invisible" />
<demo.test.com.myself.sortlistview.SideBar
android:id="@+id/sidrbar"
android:layout_width="30.0dip"
android:layout_height="fill_parent"
android:layout_gravity="right|center" />
</FrameLayout>
</LinearLayout>
自定义的显示的数据Bean
public class SortModel
private String name; //显示的数据
private String sortLetters; //显示数据拼音的首字母
public String getName()
return name;
public void setName(String name)
this.name = name;
public String getSortLetters()
return sortLetters;
public void setSortLetters(String sortLetters)
this.sortLetters = sortLetters;
按字母大小排序比较器实现
public class PinyinComparator implements Comparator<SortModel>
public int compare(SortModel o1, SortModel o2)
if (o1.getSortLetters().equals("@")
|| o2.getSortLetters().equals("#"))
return -1;
else if (o1.getSortLetters().equals("#")
|| o2.getSortLetters().equals("@"))
return 1;
else
return o1.getSortLetters().compareTo(o2.getSortLetters());
最重要的一步是要中文转拼音的操作,需要在网络中下载pinyin4j-2.5.0.jar或更高版本作为依赖添加到程序中,添加后实现转拼音方法
public static String getPingYin(String inputString)
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
format.setVCharType(HanyuPinyinVCharType.WITH_V);
char[] input = inputString.trim().toCharArray();
String output = "";
try
for (char curchar : input)
if (java.lang.Character.toString(curchar).matches("[\\u4e00-\\u9fa5]+"))
String[] temp = PinyinHelper.toHanyuPinyinStringArray(curchar, format);
output += temp[0];
else
output += java.lang.Character.toString(curchar);
catch (BadHanyuPinyinOutputFormatCombination e)
e.printStackTrace();
return output;
主界面数据展示
public class SortActivity extends Activity
private ListView sortListView;
private SideBar sideBar;
private TextView dialog;
private SortAdapter adapter;
private ClearEditText mClearEditText;
/**
* 上次第一个可见元素,用于滚动时记录标识。
*/
private int lastFirstVisibleItem = -1;
private List<SortModel> SourceDateList;
/**
* 根据拼音来排列ListView里面的数据类
*/
private PinyinComparator pinyinComparator;
/**
* 分组的布局
*/
private LinearLayout titleLayout;
/**
* 分组上显示的字母
*/
private TextView title;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sort);
initViews();
private void initViews()
titleLayout = (LinearLayout) findViewById(R.id.title_layout);
title = (TextView) findViewById(R.id.title);
pinyinComparator = new PinyinComparator();
sideBar = (SideBar) findViewById(R.id.sidrbar);
dialog = (TextView) findViewById(R.id.dialog);
sideBar.setTextView(dialog);//设置相应的字体背景样式
//设置右侧触摸监听
sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener()
@Override
public void onTouchingLetterChanged(String s)
//该字母首次出现的位置
int position = adapter.getPositionForSection(s.charAt(0));
if (position != -1)
sortListView.setSelection(position);
);
sortListView = (ListView) findViewById(R.id.country_lvcountry);
sortListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
//这里要利用adapter.getItem(position)来获取当前position所对应的对象
Toast.makeText(getApplication(), ((SortModel) adapter.getItem(position)).getName()+"="+position, Toast.LENGTH_SHORT).show();
);
SourceDateList = getData(getResources().getStringArray(R.array.date));
// 根据a-z进行排序源数据
Collections.sort(SourceDateList, pinyinComparator);
adapter = new SortAdapter(this, SourceDateList, this);
sortListView.setAdapter(adapter);
sortListView.setOnScrollListener(new AbsListView.OnScrollListener()
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount)
// LogUtils.i(+visibleItemCount+"=当前对呀的Item是="+firstVisibleItem);
//字母连续断层使不能置顶,例如 D (空) F使D到F阶段不存在置顶
int section;
try
section = adapter.getSectionForPosition(firstVisibleItem);
catch (Exception e)
return ;
int nextSecPosition = adapter.getPositionForSection(section + 1);
//解决断层置顶
for (int i = 1; i < 30; i++)
//26个英文字母充分循环
if (nextSecPosition == -1)
//继续累加
int data = section + 1 + i;
nextSecPosition = adapter.getPositionForSection(data);
else
break;
if (firstVisibleItem != lastFirstVisibleItem)
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) titleLayout.getLayoutParams();
params.topMargin = 0;
titleLayout.setLayoutParams(params);
title.setText(String.valueOf((char) section));
if (nextSecPosition == firstVisibleItem + 1)
View childView = view.getChildAt(0);
if (childView != null)
int titleHeight = titleLayout.getHeight();
int bottom = childView.getBottom();
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) titleLayout
.getLayoutParams();
if (bottom < titleHeight)
float pushedDistance = bottom - titleHeight;
params.topMargin = (int) pushedDistance;
titleLayout.setLayoutParams(params);
else
if (params.topMargin != 0)
params.topMargin = 0;
titleLayout.setLayoutParams(params);
lastFirstVisibleItem = firstVisibleItem;
);
mClearEditText = (ClearEditText) findViewById(R.id.filter_edit);
//根据输入框输入值的改变来过滤搜索
mClearEditText.addTextChangedListener(new TextWatcher()
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
filterData(s.toString());
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after)
@Override
public void afterTextChanged(Editable s)
);
private List<SortModel> getData(String[] data)
List<SortModel> listarray = new ArrayList<SortModel>();
for (int i = 0; i < data.length; i++)
String pinyin = CharacterParser.getPingYin(data[i]);
String Fpinyin = pinyin.substring(0, 1).toUpperCase();
SortModel person = new SortModel();
person.setName(data[i]);
//person.setPinYin(pinyin);
// 正则表达式,判断首字母是否是英文字母
if (Fpinyin.matches("[A-Z]"))
person.setSortLetters(Fpinyin);
else
person.setSortLetters("#");
listarray.add(person);
return listarray;
/**
* 根据输入框中的值来过滤数据并更新ListView
*
* @param filterStr
*/
private void filterData(String filterStr)
List<SortModel> filterDateList = new ArrayList<SortModel>();
if (TextUtils.isEmpty(filterStr))
filterDateList = SourceDateList;
titleLayout.setVisibility(View.VISIBLE);
title.setText("A");
else
titleLayout.setVisibility(View.GONE);
filterDateList.clear();
for (SortModel sortModel : SourceDateList)
String name = sortModel.getName();
if (name.indexOf(filterStr.toString()) != -1 || CharacterParser.getPingYin(name).startsWith(filterStr.toString()))
filterDateList.add(sortModel);
// 根据a-z进行排序
Collections.sort(filterDateList, pinyinComparator);
adapter.updateListView(filterDateList);
适配器adapter展示
public class SortAdapter extends BaseAdapter implements SectionIndexer
private List<SortModel> list = null;
private Context mContext;
public SortAdapter(Context context, List<SortModel> list )
this.mContext = context;
this.list = list;
public void updateListView(List<SortModel> list)
this.list = list;
notifyDataSetChanged();
public int getCount()
return this.list.size();
public SortModel getItem(int position)
return list.get(position);
public long getItemId(int position)
return position;
public View getView(final int position, View view, ViewGroup arg2)
ViewHolder viewHolder = null;
final SortModel mContent = list.get(position);
if (view == null)
viewHolder = new ViewHolder();
view = LayoutInflater.from(mContext).inflate(R.layout.item, null);
viewHolder.tvTitle = (TextView) view.findViewById(R.id.title);
viewHolder.tvLetter = (TextView) view.findViewById(R.id.catalog);
view.setTag(viewHolder);
else
viewHolder = (ViewHolder) view.getTag();
获取首字母的assii值
int section = getSectionForPosition(position);
//通过首字母的assii值来判断是否显示字母
int positionForSelection = getPositionForSection(section);
viewHolder.tvLetter.setOnClickListener(null);
if(position == getPositionForSection(section))
viewHolder.tvLetter.setVisibility(View.VISIBLE);
viewHolder.tvLetter.setText(mContent.getSortLetters());
else
viewHolder.tvLetter.setVisibility(View.GONE);
viewHolder.tvTitle.setText(this.list.get(position).getName());
return view;
final static class ViewHolder
TextView tvLetter;
TextView tvTitle;
public int getSectionForPosition(int position)
return list.get(position).getSortLetters().charAt(0);
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;
结果展示
源码下载(请记得给好评哦,有什么问题可以留言提问,作者会尽快给予解决,O(∩_∩)O谢谢)
http://download.csdn.net/detail/wyh_healer/9566682
以上是关于Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能的主要内容,如果未能解决你的问题,请参考以下文章
Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能
Android Studio 仿首页美团切换城市(ListView)+数据库帮助类SQLiteOpenHelper+LetterView(字母排序)