版权声明:本文为博主原创文章,未经博主允许不得转载。
这几天在做IM模块,设计图要求做一个类似下图所示的自定义控件。
我百度了一下,发现类似的Ddmo有很多,
但是还不能完全满足设计图的需求。
参考了几个比较有价值的demo琢磨了一天总算做出来了,现在发出来和大家分享。
分析一下这个需求的难点。
1、右边侧滑栏(SideBar)控件绘制。
2、将列表中的中文昵称转化为拼音列表。(这个问题用jpinyin解决。)
3、滑动侧滑栏
(SideBar)
的过程中如何与ListView列表建立对应的联系?
关于jpinyin的相关介绍https://github.com/stuxuhai/jpinyin
如果能够解决这三个问题那么这个需求也就迎刃而解了。
接下来看看整个Demo的结构图
一、SideBar控件的绘制
1、首先创建一个名为SideBar的类并且继承View。在这个类中分别重写onMeasure,onDraw,onTouchEvent三个方法。
(1)onMeasure顾名思义这是一个用来测量SideBar视图的大小的方法。
MeasureSpec.getMode是用来获取测量模式类型。测量模式类型有三种UNSPECIFIED,EXACTLY,AT_MOST。
其中
UNSPECIFIED很少会用到,我们忽略。我们主要理解
EXACTLY和
AT_MOST
当我们在XML文件里面设置SideBar类的layout_width属性的时候,
如果设置为match_parent或者一个精确值(例如30dp)那么
MeasureSpec.getMode得到的测量模式类型就是EXACTLY。
如果设置为warp_content那么MeasureSpec.getMode得到的测量模式类型就是AT_MOST。
(layout_height属性同理)
根据这些获取的类型值,就会进入下面的if条件中计算该视图的大小。在该方法的最后会调用setMeasureDimension方法设置最终的宽度和高度。
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec. getMode(heightMeasureSpec) ;
- int finalWidth = 0, finalHeight = 0;
- // 测量单个字符的宽度和高度
- float charWidthAndHeight = paint.measureText( letter.get(0 )) + letterSpace;
- if (widthMode == MeasureSpec. EXACTLY) {
- finalWidth = MeasureSpec.getSize (widthMeasureSpec);
- } else if (widthMode == MeasureSpec.AT_MOST) {
- finalWidth = (int ) charWidthAndHeight + getPaddingLeft() + getPaddingRight();
- }
- if (heightMode == MeasureSpec.EXACTLY) {
- finalHeight = MeasureSpec.getSize (heightMeasureSpec);
- } else if (heightMode == MeasureSpec.AT_MOST) {
- // 注意measureText的值与 paint.setTextSize的值有关
- finalHeight = ( int) charWidthAndHeight * letter .size() + getPaddingBottom() + getPaddingTop();
- }
- setMeasuredDimension(finalWidth, finalHeight);
- }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec. getMode(heightMeasureSpec) ;
int finalWidth = 0, finalHeight = 0;
// 测量单个字符的宽度和高度
float charWidthAndHeight = paint.measureText( letter.get(0 )) + letterSpace;
if (widthMode == MeasureSpec. EXACTLY) {
finalWidth = MeasureSpec.getSize (widthMeasureSpec);
} else if (widthMode == MeasureSpec.AT_MOST) {
finalWidth = (int ) charWidthAndHeight + getPaddingLeft() + getPaddingRight();
}
if (heightMode == MeasureSpec.EXACTLY) {
finalHeight = MeasureSpec.getSize (heightMeasureSpec);
} else if (heightMode == MeasureSpec.AT_MOST) {
// 注意measureText的值与 paint.setTextSize的值有关
finalHeight = ( int) charWidthAndHeight * letter .size() + getPaddingBottom() + getPaddingTop();
}
setMeasuredDimension(finalWidth, finalHeight);
}
(2)在测量好这个视图的大小之后,接下来就是在onDraw方法中绘制自己想要的视图。
- @Override
- protected void onDraw(Canvas canvas) {
- //27 个字符(包含 #)均分整个视图的高度,例如视图高度为 270,270/27 均分之后,每个字符的y坐标为10。
- y = getHeight() / letter.size() ;
- for ( int i = 0 ; i < letter .size(); i++) {
- Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
- // 计算绘制字符 X的坐标,整个视图宽度的一半减去字符宽度的一半
- x = getWidth() / 2 - ( int) paint.measureText(letter .get(i)) / 2;
- // int correctY=y*i+y;
- / int correctY = y * i + y / 2;
- int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2;
- String tempString = isLetterUpper ? letter.get(i).toUpperCase() : letter .get(i).toLowerCase();
- canvas.drawText(tempString , x, correctY, paint );
- / canvas.drawLine(0, y * i + y, 100, y * i + y, paint);
- }
- }
@Override
protected void onDraw(Canvas canvas) {
//27 个字符(包含 #)均分整个视图的高度,例如视图高度为 270,270/27 均分之后,每个字符的y坐标为10。
y = getHeight() / letter.size() ;
for ( int i = 0 ; i < letter .size(); i++) {
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
// 计算绘制字符 X的坐标,整个视图宽度的一半减去字符宽度的一半
x = getWidth() / 2 - ( int) paint.measureText(letter .get(i)) / 2;
// int correctY=y*i+y;
// int correctY = y * i + y / 2;
int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2;
String tempString = isLetterUpper ? letter.get(i).toUpperCase() : letter .get(i).toLowerCase();
canvas.drawText(tempString , x, correctY, paint );
// canvas.drawLine(0, y * i + y, 100, y * i + y, paint);
}
}
关于绘制字符X坐标的求法比较简单,注释已经解释得很清楚就不再分析。
主要分析y坐标的求法。我百度了一些demo总结发现有两种关于字符y坐标的求法。
(为了便于观察,我暂时将SideBar的背景改为了浅蓝色,并且每个字符的都用横线分隔)
第一种是int correctY=y*i+y。运行一下程序发现字符的绘制明显向下偏移(如下图所示)
第二种是int correctY=y*i+y/2。再运行程序,发现还是有点偏移。我是处女座有强迫症,果断不能忍啊。
于是在网上搜索了不少资料,研究了一番。发现这位前辈的资料解决了我的问题http://blog.csdn.net/hursing/article/details/18703599
我把计算公式改为int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2; 运行程序,完美解决问题。
刚看到这个式子可能不是很理解。我给大家解释一下,假设我们要绘制D这个字符。如图所示
这时包含D字符的这个矩形上下边的纵坐标分别是y*i,y*i+y。那么这个矩形的中间纵坐标怎么算呢?答案就是((y*i)+(y*i+y))/2
得到这个中间值之后,我们直接调用绘制方法canvas.drawText(tempString, x,
((y*i)+(y*i+y))/2
, paint);发现还是出问题了
字符还是画在矩形偏上位置(如下图)
为什么会这样?这是因为canvas.drawText方法在绘制文本的时候并不是从文本的中间(下图的黄线)开始
,而且是从文本的中间偏下位置,即图中绿色的baseLine这条线开始绘制。
明白了这些知识点之后就好处理了。(FontMetricInt.top+FontMetricInt.bottom)/2就是这边黄线的纵坐标(注意
FontMetricInt.top
是负值)。
综合起来:矩形纵坐标的值减去黄线纵坐标的值除以2整理得到
((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2
再调用Canvas.drawText方法绘制字符。
(3)整个视图都
测量,
绘制好之后。我们还需要添加控件的触摸事件。所以还需要重写onTouchEvent方法。
首先设置回调接口,向用户提供两个方法,分别是showCurrentLetter和hideCurrentLetter。
- /**
- * 回调接口。
- */
- public interface OnCurrentLetterListener {
- void showCurrentLetter(String currentLetter);
- void hideCurrentLetter ();
- }
/**
* 回调接口。
*/
public interface OnCurrentLetterListener {
void showCurrentLetter(String currentLetter);
void hideCurrentLetter ();
}
重写onTouchEvent方法,首先获取用户在点击,移动SideBar视图时的y坐标。
y/getHeight()*letter.size()表示y坐标占整个视图高度的比例(例如50%)
乘以字符数组(长度为27)的长度等于下标(约等于13,)
switch(event.getAction())是用来判断用户触摸手机屏幕的手势。
当用户在手机屏幕按
压,移动时
MotionEvent.ACTION_DOWN(按压手势),
MotionEvent.ACTION_MOVE(移动手势)
回调上面回调接口的showCurrentLetter方法。
当用户在手机屏幕抬起时MotionEvent.ACTION_UP(抬起手势)回调接口中的hideCurrentLetter方法。
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float y = event.getY();
- // 获取当前侧滑栏字母的下标
- float currentLetterIndex = y / getHeight() * letter.size();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE:
- if (onCurrentLetterListener != null) {
- //对上下边界对限制
- if (currentLetterIndex >= letter.size()) {
- currentLetterIndex = letter.size() - 1 ;
- } else if (currentLetterIndex < 0) {
- currentLetterIndex = 0;
- }
- onCurrentLetterListener .showCurrentLetter(letter.get(( int) currentLetterIndex));
- }
- return true;
- case MotionEvent. ACTION_UP:
- if (onCurrentLetterListener != null) {
- onCurrentLetterListener.hideCurrentLetter() ;
- }
- return true;
- default:
- return true;
- }
- }
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
// 获取当前侧滑栏字母的下标
float currentLetterIndex = y / getHeight() * letter.size();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (onCurrentLetterListener != null) {
//对上下边界对限制
if (currentLetterIndex >= letter.size()) {
currentLetterIndex = letter.size() - 1 ;
} else if (currentLetterIndex < 0) {
currentLetterIndex = 0;
}
onCurrentLetterListener .showCurrentLetter(letter.get(( int) currentLetterIndex));
}
return true;
case MotionEvent. ACTION_UP:
if (onCurrentLetterListener != null) {
onCurrentLetterListener.hideCurrentLetter() ;
}
return true;
default:
return true;
}
}
最后整个方法return true;表示这个触摸事件被SideBar视图消费了,不再向其它地方传递事件。
2、最后贴个SideBar类的代码
- package per.edward.ui;
- import android.content.Context ;
- import android.content.res.TypedArray ;
- import android.graphics.Canvas ;
- import android.graphics.Color ;
- import android.graphics.Paint ;
- import android.util.AttributeSet ;
- import android.view.MotionEvent ;
- import android.view.View ;
- import java.util.ArrayList ;
- import java.util.Arrays ;
- import java.util.List ;
- /**
- * 侧滑栏视图
- * Created by Edward on 2016/4/27.
- */
- public class SideBar extends View {
- private String[] letterStrings = { "#", "A" , "B", "C", "D" , "E", "F" , "G", "H", "I" , "J", "K", "L" , "M", "N", "O" , "P", "Q", "R", "S" , "T", "U", "V" , "W", "S", "Y" , "Z"} ;
- private Paint paint;
- // 字母列表
- private List<String> letter;
- // 绘制字母的 x,y坐标
- private int x, y ;
- // 字母的间距
- private int letterSpace = 0 ;
- // 字母是否大写
- private boolean isLetterUpper = true;
- private OnCurrentLetterListener onCurrentLetterListener;
- public void setLetter (List<String> letter) {
- this .letter = letter ;
- }
- // 设置回调接口
- public void setOnCurrentLetterListener(OnCurrentLetterListener onCurrentLetterListener) {
- this .onCurrentLetterListener = onCurrentLetterListener ;
- }
- public SideBar(Context context) {
- this (context, null) ;
- }
- public SideBar(Context context, AttributeSet attrs) {
- super (context, attrs) ;
- if ( letter == null ) {
- letter = new ArrayList<>() ;
- letter = Arrays.asList(letterStrings );
- }
- TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SideBar) ;
- letterSpace = typedArray.getInt(R.styleable.SideBar_SB_Letter_Space, 0);
- isLetterUpper = typedArray.getBoolean(R.styleable.SideBar_SB_Is_Letter_Upper, true);
- typedArray.recycle() ;
- paint = new Paint() ;
- paint .setColor(Color.BLACK) ;
- paint .setAntiAlias(true) ;
- paint .setTextSize(30) ;//3CAC48
- paint .setColor(Color.parseColor("#ffffff" ));
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec. getMode(heightMeasureSpec) ;
- int finalWidth = 0, finalHeight = 0;
- // 测量单个字符的宽度和高度
- float charWidthAndHeight = paint.measureText( letter.get(0 )) + letterSpace;
- if (widthMode == MeasureSpec. EXACTLY) {
- finalWidth = MeasureSpec.getSize(widthMeasureSpec);
- } else if (widthMode == MeasureSpec.AT_MOST) {
- finalWidth = (int) charWidthAndHeight + getPaddingLeft() + getPaddingRight();
- }
- if (heightMode == MeasureSpec.EXACTLY) {
- finalHeight = MeasureSpec.getSize(heightMeasureSpec) ;
- } else if (heightMode == MeasureSpec.AT_MOST) {
- // 注意measureText的值与 paint.setTextSize的值有关
- finalHeight = ( int) charWidthAndHeight * letter .size() + getPaddingBottom() + getPaddingTop();
- }
- // Log.e("--------------->", MeasureSpec.getSize(widthMeasureSpec) + " " + MeasureSpec.getSize(heightMeasureSpec));
- setMeasuredDimension(finalWidth , finalHeight);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- setBackgroundColor(Color.parseColor ("#31b2f7" ));
- //27 个字符(包含 #)均分整个视图的高度,例如视图高度为 270,270/27 均分之后,每个字符的 y坐标为10
- y = getHeight() / letter.size() ;
- Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
- for ( int i = 0 ; i < letter .size(); i++) {
- // 计算绘制字符 X的坐标,整个视图宽度的一半减去字符宽度的一半
- x = getWidth() / 2 - ( int) paint.measureText(letter .get(i)) / 2;
- // int correctY=y*i+y;
- // int correctY = y * i + y / 2;
- int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2;
- String tempString = isLetterUpper ? letter.get(i).toUpperCase() : letter .get(i).toLowerCase();
- canvas.drawText(tempString , x, ((y * i) + ( y * i + y)) / 2 , paint) ;
- canvas.drawLine( 0, y * i + y, 100, y * i + y, paint);
- }
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- float y = event.getY();
- // 获取当前侧滑栏字母的下标
- float currentLetterIndex = y / getHeight() * letter.size();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE:
- if ( onCurrentLetterListener != null ) {
- //对上下边界对限制
- if (currentLetterIndex >= letter.size()) {
- currentLetterIndex = letter.size() - 1 ;
- } else if (currentLetterIndex < 0) {
- currentLetterIndex = 0;
- }
- onCurrentLetterListener .showCurrentLetter(letter.get(( int) currentLetterIndex));
- }
- return true;
- case MotionEvent. ACTION_UP:
- if ( onCurrentLetterListener != null ) {
- onCurrentLetterListener.hideCurrentLetter() ;
- }
- return true;
- default:
- return true;
- }
- }
- /**
- * 回调接口。
- */
- public interface OnCurrentLetterListener {
- void showCurrentLetter(String currentLetter);
- void hideCurrentLetter ();
- }
- }
package per.edward.ui;
import android.content.Context ;
import android.content.res.TypedArray ;
import android.graphics.Canvas ;
import android.graphics.Color ;
import android.graphics.Paint ;
import android.util.AttributeSet ;
import android.view.MotionEvent ;
import android.view.View ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.List ;
/**
* 侧滑栏视图
* Created by Edward on 2016/4/27.
*/
public class SideBar extends View {
private String[] letterStrings = { "#", "A" , "B", "C", "D" , "E", "F" , "G", "H", "I" , "J", "K", "L" , "M", "N", "O" , "P", "Q", "R", "S" , "T", "U", "V" , "W", "S", "Y" , "Z"} ;
private Paint paint;
// 字母列表
private List<String> letter;
// 绘制字母的 x,y坐标
private int x, y ;
// 字母的间距
private int letterSpace = 0 ;
// 字母是否大写
private boolean isLetterUpper = true;
private OnCurrentLetterListener onCurrentLetterListener;
public void setLetter (List<String> letter) {
this .letter = letter ;
}
// 设置回调接口
public void setOnCurrentLetterListener(OnCurrentLetterListener onCurrentLetterListener) {
this .onCurrentLetterListener = onCurrentLetterListener ;
}
public SideBar(Context context) {
this (context, null) ;
}
public SideBar(Context context, AttributeSet attrs) {
super (context, attrs) ;
if ( letter == null ) {
letter = new ArrayList<>() ;
letter = Arrays.asList(letterStrings );
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SideBar) ;
letterSpace = typedArray.getInt(R.styleable.SideBar_SB_Letter_Space, 0);
isLetterUpper = typedArray.getBoolean(R.styleable.SideBar_SB_Is_Letter_Upper, true);
typedArray.recycle() ;
paint = new Paint() ;
paint .setColor(Color.BLACK) ;
paint .setAntiAlias(true) ;
paint .setTextSize(30) ;//3CAC48
paint .setColor(Color.parseColor("#ffffff" ));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec. getMode(heightMeasureSpec) ;
int finalWidth = 0, finalHeight = 0;
// 测量单个字符的宽度和高度
float charWidthAndHeight = paint.measureText( letter.get(0 )) + letterSpace;
if (widthMode == MeasureSpec. EXACTLY) {
finalWidth = MeasureSpec.getSize(widthMeasureSpec);
} else if (widthMode == MeasureSpec.AT_MOST) {
finalWidth = (int) charWidthAndHeight + getPaddingLeft() + getPaddingRight();
}
if (heightMode == MeasureSpec.EXACTLY) {
finalHeight = MeasureSpec.getSize(heightMeasureSpec) ;
} else if (heightMode == MeasureSpec.AT_MOST) {
// 注意measureText的值与 paint.setTextSize的值有关
finalHeight = ( int) charWidthAndHeight * letter .size() + getPaddingBottom() + getPaddingTop();
}
// Log.e("--------------->", MeasureSpec.getSize(widthMeasureSpec) + " " + MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension(finalWidth , finalHeight);
}
@Override
protected void onDraw(Canvas canvas) {
setBackgroundColor(Color.parseColor ("#31b2f7" ));
//27 个字符(包含 #)均分整个视图的高度,例如视图高度为 270,270/27 均分之后,每个字符的 y坐标为10
y = getHeight() / letter.size() ;
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
for ( int i = 0 ; i < letter .size(); i++) {
// 计算绘制字符 X的坐标,整个视图宽度的一半减去字符宽度的一半
x = getWidth() / 2 - ( int) paint.measureText(letter .get(i)) / 2;
// int correctY=y*i+y;
// int correctY = y * i + y / 2;
int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2;
String tempString = isLetterUpper ? letter.get(i).toUpperCase() : letter .get(i).toLowerCase();
canvas.drawText(tempString , x, ((y * i) + ( y * i + y)) / 2 , paint) ;
canvas.drawLine( 0, y * i + y, 100, y * i + y, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
// 获取当前侧滑栏字母的下标
float currentLetterIndex = y / getHeight() * letter.size();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if ( onCurrentLetterListener != null ) {
//对上下边界对限制
if (currentLetterIndex >= letter.size()) {
currentLetterIndex = letter.size() - 1 ;
} else if (currentLetterIndex < 0) {
currentLetterIndex = 0;
}
onCurrentLetterListener .showCurrentLetter(letter.get(( int) currentLetterIndex));
}
return true;
case MotionEvent. ACTION_UP:
if ( onCurrentLetterListener != null ) {
onCurrentLetterListener.hideCurrentLetter() ;
}
return true;
default:
return true;
}
}
/**
* 回调接口。
*/
public interface OnCurrentLetterListener {
void showCurrentLetter(String currentLetter);
void hideCurrentLetter ();
}
}
3、SideBar类写完之后,我们就可以创建一个XML文件命名为activity_main,
在里面用上我们刚才写的自定义控件。整个XML代码布局很简单。
最外层是一个RelativeLayout容器。
中间放了一个TextView控件,默认为隐藏状态。
将SideBar右置垂直居中。
- <? xml version="1.0" encoding= "utf-8"?>
- <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
- xmlns: app="http://schemas.android.com/apk/res-auto"
- android :layout_width="match_parent"
- android :layout_height="match_parent">
- <TextView
- android :id="@+id/txt_show_current_letter"
- android :layout_width="100dp"
- android :layout_height="wrap_content"
- android :layout_centerInParent="true"
- android :background="#3CAC48"
- android :gravity="center_vertical|center_horizontal"
- android :text="A"
- android :textColor="#ffffff"
- android :textSize="50dp"
- android :visibility="gone" />
- <per.edward.ui.SideBar
- android :id="@+id/side_bar"
- android :layout_width="30dp"
- android :layout_height="150dp"
- android :layout_alignParentRight="true"
- android :layout_centerVertical="true" />
- </RelativeLayout>
<? xml version="1.0" encoding= "utf-8"?>
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
android :layout_width="match_parent"
android :layout_height="match_parent">
<TextView
android :id="@+id/txt_show_current_letter"
android :layout_width="100dp"
android :layout_height="wrap_content"
android :layout_centerInParent="true"
android :background="#3CAC48"
android :gravity="center_vertical|center_horizontal"
android :text="A"
android :textColor="#ffffff"
android :textSize="50dp"
android :visibility="gone" />
<per.edward.ui.SideBar
android :id="@+id/side_bar"
android :layout_width="30dp"
android :layout_height="150dp"
android :layout_alignParentRight="true"
android :layout_centerVertical="true" />
</RelativeLayout>
4、最后再创建一个主体类,命名为MainActivity并且继承Activity。
首先实例化TextView和SideBar两个控件。
之后用SideBar的实例。设置setOnCurrentLetterListener的回调接口,在这个接口需要实现两个方法分别是showCurrentLetter,hideCurrentLetter。前者用来显示TextView,并且设置当前字母值。后者用来隐藏TextView。具体操作看下面代码。简单吧?
- package per.edward.ui;
- import android.app.Activity ;
- import android.os.Bundle ;
- import android.view.View ;
- import android.widget.TextView ;
- /**
- * author:Edward
- */
- public class MainActivity extends Activity {
- private TextView txtShowCurrentLetter;
- private SideBar sideBar;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState) ;
- setContentView(R.layout. activity_main);
- txtShowCurrentLetter = (TextView) findViewById(R.id.txt_show_current_letter );
- sideBar = (SideBar) findViewById(R.id. side_bar);
- setCallbackInterface() ;
- }
- /**
- * 设置回调接口
- */
- public void setCallbackInterface() {
- // 回调接口
- sideBar .setOnCurrentLetterListener(new SideBar.OnCurrentLetterListener() {
- @Override
- public void showCurrentLetter(String currentLetter) {
- txtShowCurrentLetter .setVisibility(View.VISIBLE) ;
- txtShowCurrentLetter.setText(currentLetter) ;
- }
- @Override
- public void hideCurrentLetter() {
- txtShowCurrentLetter.setVisibility(View. GONE);
- }
- });
- }
- }
package per.edward.ui;
import android.app.Activity ;
import android.os.Bundle ;
import android.view.View ;
import android.widget.TextView ;
/**
* author:Edward
*/
public class MainActivity extends Activity {
private TextView txtShowCurrentLetter;
private SideBar sideBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState) ;
setContentView(R.layout. activity_main);
txtShowCurrentLetter = (TextView) findViewById(R.id.txt_show_current_letter );
sideBar = (SideBar) findViewById(R.id. side_bar);
setCallbackInterface() ;
}
/**
* 设置回调接口
*/
public void setCallbackInterface() {
// 回调接口
sideBar .setOnCurrentLetterListener(new SideBar.OnCurrentLetterListener() {
@Override
public void showCurrentLetter(String currentLetter) {
txtShowCurrentLetter .setVisibility(View.VISIBLE) ;
txtShowCurrentLetter.setText(currentLetter) ;
}
@Override
public void hideCurrentLetter() {
txtShowCurrentLetter.setVisibility(View. GONE);
}
});
}
}
5、最后实现的效果图如下。
分享到这里整个demo的流程已经走到一半了。
下面会讲讲ListView与SideBar控件的联动处理。
二、建立侧滑栏SideBar与ListView的对应关系
1、首先创建一个联系人的实体类,在这个实体类中,定义了两个变量firstLetter(用来存储联系人拼音的第一个字母)和name(联系人的名字)。
- package per.edward.ui;
- /**
- * 联系人列表实体类
- * Created by Edward on 2016/4/26.
- */
- public class ContactsModel {
- private String firstLetter;
- private String name;
- public String getFirstLetter() {
- return firstLetter;
- }
- public void setFirstLetter(String firstLetter) {
- this .firstLetter = firstLetter ;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this .name = name ;
- }
- }
package per.edward.ui;
/**
* 联系人列表实体类
* Created by Edward on 2016/4/26.
*/
public class ContactsModel {
private String firstLetter;
private String name;
public String getFirstLetter() {
return firstLetter;
}
public void setFirstLetter(String firstLetter) {
this .firstLetter = firstLetter ;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name ;
}
}
2、创建一个名为adapter_side_bar的XML布局。
这个布局将在MainActivity类中通过实例化
myAdapter = new SideBarAdapter(this, list, R.layout.adapter_side_bar);
的方法传递给SideBarAdapter类。
- <? xml version="1.0" encoding= "utf-8"?>
- <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
- android :layout_width="match_parent"
- android :layout_height="match_parent"
- android :orientation="vertical">
- <TextView
- android :id="@+id/txt_letter_category"
- android :layout_width="match_parent"
- android :layout_height="30dp"
- android :background="#d7d6d6"
- android :padding="5dp"
- android :text="A"
- android :textColor="#000000"
- android :textSize="16dp" />
- <LinearLayout
- android :layout_width="match_parent"
- android :layout_height="wrap_content"
- android :background="#ffffff"
- android :orientation="horizontal">
- <ImageView
- android :id="@+id/image"
- android :layout_width="50dp"
- android :layout_height="50dp"
- android :padding="5dp"
- android :src="@mipmap/ic_launcher" />
- <TextView
- android :id="@+id/txt_name"
- android :layout_width="wrap_content"
- android :layout_height="wrap_content"
- android :layout_gravity="center_vertical"
- android :text="姓名 "
- android :textSize="14dp" />
- </LinearLayout>
- </LinearLayout>
<? xml version="1.0" encoding= "utf-8"?>
<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
android :layout_width="match_parent"
android :layout_height="match_parent"
android :orientation="vertical">
<TextView
android :id="@+id/txt_letter_category"
android :layout_width="match_parent"
android :layout_height="30dp"
android :background="#d7d6d6"
android :padding="5dp"
android :text="A"
android :textColor="#000000"
android :textSize="16dp" />
<LinearLayout
android :layout_width="match_parent"
android :layout_height="wrap_content"
android :background="#ffffff"
android :orientation="horizontal">
<ImageView
android :id="@+id/image"
android :layout_width="50dp"
android :layout_height="50dp"
android :padding="5dp"
android :src="@mipmap/ic_launcher" />
<TextView
android :id="@+id/txt_name"
android :layout_width="wrap_content"
android :layout_height="wrap_content"
android :layout_gravity="center_vertical"
android :text="姓名 "
android :textSize="14dp" />
</LinearLayout>
</LinearLayout>
这个布局的预览图是这样滴。
SideBarAdapter接收到此布局之后,会通过convertView = LayoutInflater.from(context).inflate(mItemLayoutId, null);加载布局。
并且在getView方法中动态在控制字母标题的显示或隐藏。
3、接着创建一个名为
SideBarAdapter类的
适配器
并且继承BaseAdapter。
(1)构造方法中调用traverseList方法。这个方法的算法是这样的。此时mDatas列表所有的getFirstLetter
字段已经排序成
#
#
#
ABCDEFLLLLMMMQTTZZ。#有三个,for循环。
第一次currentd的值是#把current的值作为键
并且记录它的下标,即map.put(current, i)。
第二次current的值是#不记录,第三次
current的值还是#
不记录。
到第四次此时current更新为A,把current作为键并且记录下标。
第五次此时current更新为B,
把current作为键并且记录下标....一直到for循环结束为止。
这时map的集合已经记录了整个mDatas列表的字母标题和下标。我们只需要在getView方法(这个方法会传进来一个position)中判断当前的position是否等于map的下标值。如果相等就将字母标题显示出来。否则隐藏
- package per.edward.ui;
- 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.HashMap ;
- import java.util.List ;
- import java.util.Map ;
- /**
- * 侧栏适配器
- * Created by Edward on 2016/4/27.
- */
- public class SideBarAdapter extends BaseAdapter {
- // 是否第一个 Item的
- private boolean isFirstItemLetter = true;
- // 记录是否显示字母标题,键为字母,值为下标
- private Map<String, Integer> map;
- private List<ContactsModel> mDatas;
- private int mItemLayoutId ;
- private Context context;
- public SideBarAdapter(Context mContext , List<ContactsModel> mDatas, int mItemLayoutId) {
- this .mDatas = mDatas ;
- map = new HashMap<>() ;
- this. mItemLayoutId = mItemLayoutId;
- this. context = mContext;
- traverseList() ;
- }
- /**
- * 遍历列表
- * 由于传进来的 mDatas是一个已排好序的列表,遍历整个列表,每遇到分类的第一个字母就把下标记录下来
- */
- public void traverseList() {
- // 获取初始值
- String current = mDatas.get(0 ).getFirstLetter();
- for ( int i = 0 ; i < mDatas .size(); i++) {
- char tempChar = mDatas.get(i).getFirstLetter().charAt(0) ;
- String tempFirstLetter = mDatas.get(i).getFirstLetter();
- if (tempFirstLetter.equals(current) || (tempChar < 'A' || tempChar > 'Z' )) {
- if ( isFirstItemLetter) {
- map.put(current , i);
- }
- } else {
- //更新初始值
- current = mDatas .get(i).getFirstLetter();
- map.put(current , i);
- }
- isFirstItemLetter = false;
- }
- }
- /**
- * 获取当前字母的下标
- *
- * @return
- */
- public int getCurrentLetterPosition(String currentLetter) {
- if (map.get(currentLetter) != null) {
- return map.get(currentLetter);
- } else
- return - 1;
- }
- @Override
- public int getCount() {
- return mDatas.size();
- }
- @Override
- public Object getItem( int position) {
- return mDatas.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public View getView( int position, View convertView, ViewGroup parent) {
- ViewHolder viewHolder;
- if (convertView == null) {
- viewHolder = new ViewHolder() ;
- convertView = LayoutInflater.from( context).inflate(mItemLayoutId , null);
- viewHolder. txtFirstLetter = (TextView) convertView.findViewById(R.id.txt_letter_category) ;
- viewHolder. txtName = (TextView) convertView.findViewById(R.id.txt_name) ;
- convertView.setTag(viewHolder) ;
- } else {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- // 判断是否显示字母标题
- if (map.get( mDatas.get(position).getFirstLetter()) != null && map.get(mDatas .get(position).getFirstLetter()).equals(position)) {
- viewHolder.txtFirstLetter .setVisibility(View.VISIBLE) ;
- viewHolder.txtFirstLetter.setText( mDatas.get(position).getFirstLetter());
- } else {
- viewHolder.txtFirstLetter .setVisibility(View.GONE) ;
- }
- viewHolder.txtName .setText(mDatas.get(position).getName()) ;
- return convertView ;
- }
- public class ViewHolder {
- TextView txtFirstLetter , txtName;
- }
- }
package per.edward.ui;
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.HashMap ;
import java.util.List ;
import java.util.Map ;
/**
* 侧栏适配器
* Created by Edward on 2016/4/27.
*/
public class SideBarAdapter extends BaseAdapter {
// 是否第一个 Item的
private boolean isFirstItemLetter = true;
// 记录是否显示字母标题,键为字母,值为下标
private Map<String, Integer> map;
private List<ContactsModel> mDatas;
private int mItemLayoutId ;
private Context context;
public SideBarAdapter(Context mContext , List<ContactsModel> mDatas, int mItemLayoutId) {
this .mDatas = mDatas ;
map = new HashMap<>() ;
this. mItemLayoutId = mItemLayoutId;
this. context = mContext;
traverseList() ;
}
/**
* 遍历列表
* 由于传进来的 mDatas是一个已排好序的列表,遍历整个列表,每遇到分类的第一个字母就把下标记录下来
*/
public void traverseList() {
// 获取初始值
String current = mDatas.get(0 ).getFirstLetter();
for ( int i = 0 ; i < mDatas .size(); i++) {
char tempChar = mDatas.get(i).getFirstLetter().charAt(0) ;
String tempFirstLetter = mDatas.get(i).getFirstLetter();
if (tempFirstLetter.equals(current) || (tempChar < 'A' || tempChar > 'Z' )) {
if ( isFirstItemLetter) {
map.put(current , i);
}
} else {
//更新初始值
current = mDatas .get(i).getFirstLetter();
map.put(current , i);
}
isFirstItemLetter = false;
}
}
/**
* 获取当前字母的下标
*
* @return
*/
public int getCurrentLetterPosition(String currentLetter) {
if (map.get(currentLetter) != null) {
return map.get(currentLetter);
} else
return - 1;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem( int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView( int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder() ;
convertView = LayoutInflater.from( context).inflate(mItemLayoutId , null);
viewHolder. txtFirstLetter = (TextView) convertView.findViewById(R.id.txt_letter_category) ;
viewHolder. txtName = (TextView) convertView.findViewById(R.id.txt_name) ;
convertView.setTag(viewHolder) ;
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// 判断是否显示字母标题
if (map.get( mDatas.get(position).getFirstLetter()) != null && map.get(mDatas .get(position).getFirstLetter()).equals(position)) {
viewHolder.txtFirstLetter .setVisibility(View.VISIBLE) ;
viewHolder.txtFirstLetter.setText( mDatas.get(position).getFirstLetter());
} else {
viewHolder.txtFirstLetter .setVisibility(View.GONE) ;
}
viewHolder.txtName .setText(mDatas.get(position).getName()) ;
return convertView ;
}
public class ViewHolder {
TextView txtFirstLetter , txtName;
}
}
4、记得在倒回刚才的activity_main的XML文件添加ListView控件
- <? xml version="1.0" encoding= "utf-8"?>
- <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
- xmlns: app="http://schemas.android.com/apk/res-auto"
- android :layout_width="match_parent"
- android :layout_height="match_parent"
- android :background="#ffffff">
- <ListView
- android :id="@+id/list_view"
- android :layout_width="match_parent"
- android :layout_height="match_parent" />
- <TextView
- android :id="@+id/txt_show_current_letter"
- android :layout_width="100dp"
- android :layout_height="wrap_content"
- android :layout_centerInParent="true"
- android :background="#3CAC48"
- android :gravity="center_vertical|center_horizontal"
- android :text="A"
- android :textColor="#ffffff"
- android :textSize="50dp"
- android :visibility="gone" />
- <per.edward.ui.SideBar
- android :id="@+id/side_bar"
- android :layout_width="30dp"
- android :layout_height="match_parent"
- android :layout_alignParentRight="true"
- android :layout_centerVertical="true" />
- </RelativeLayout>
<? xml version="1.0" encoding= "utf-8"?>
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
android :layout_width="match_parent"
android :layout_height="match_parent"
android :background="#ffffff">
<ListView
android :id="@+id/list_view"
android :layout_width="match_parent"
android :layout_height="match_parent" />
<TextView
android :id="@+id/txt_show_current_letter"
android :layout_width="100dp"
android :layout_height="wrap_content"
android :layout_centerInParent="true"
android :background="#3CAC48"
android :gravity="center_vertical|center_horizontal"
android :text="A"
android :textColor="#ffffff"
android :textSize="50dp"
android :visibility="gone" />
<per.edward.ui.SideBar
android :id="@+id/side_bar"
android :layout_width="30dp"
android :layout_height="match_parent"
android :layout_alignParentRight="true"
android :layout_centerVertical="true" />
</RelativeLayout>
5、写完了SideBarAdapter,我们再回到刚才写的MainActivity类进行相应的设置,包括初始化值联系人列表,
联系人
列表排序,联系人昵称转化为拼音等。具体代码如下所示,比较简单。就不一一分析了。
- package per.edward.ui;
- import android.os.Bundle ;
- import android.support.v7.app.AppCompatActivity ;
- import android.view.View ;
- import android.widget.ListView ;
- import android.widget.TextView ;
- import java.util.ArrayList ;
- import java.util.Collections ;
- import java.util.Comparator ;
- import java.util.HashSet ;
- import java.util.List ;
- import java.util.Set ;
- import opensource.jpinyin.PinyinHelper ;
- /**
- * author:Edward
- * 此 demo的博客地址:http://blog.csdn.net/u012814441
- */
- public class MainActivity extends AppCompatActivity {
- private ListView listView;
- private TextView txtShowCurrentLetter;
- private SideBar sideBar;
- private SideBarAdapter myAdapter;
- // private List<ContactsModel> list;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super .onCreate(savedInstanceState) ;
- setContentView(R.layout. activity_main);
- txtShowCurrentLetter = (TextView) findViewById(R.id.txt_show_current_letter );
- sideBar = (SideBar) findViewById(R.id. side_bar);
- listView = (ListView) findViewById(R.id. list_view);
- setCallbackInterface() ;
- List<ContactsModel> list = initData() ;
- chineseToPinyin(list) ;
- // 将联系人列表的标题字母排序
- Collections. sort(list , new Comparator<ContactsModel>() {
- @Override
- public int compare(ContactsModel lhs, ContactsModel rhs) {
- return lhs.getFirstLetter().compareTo(rhs.getFirstLetter()) ;
- }
- });
- // 将联系人列表的标题字母放到 List<String>列表中,准备数据去重
- List<String> getLetter = new ArrayList<>();
- for ( int i = 0 ; i < list.size(); i++) {
- getLetter.add(list.get(i).getFirstLetter());
- }
- // 数据去重
- getLetter = removeDuplicate(getLetter) ;
- // 将联系人列表的字母标题排序
- Collections. sort(getLetter , new Comparator<String>() {
- @Override
- public int compare(String lhs, String rhs) {
- return lhs.compareTo(rhs) ;
- }
- });
- // 设置已排序好的标题
- sideBar .setLetter(getLetter);
- myAdapter = new SideBarAdapter( this, list, R.layout.adapter_side_bar) ;
- listView .setAdapter(myAdapter) ;
- }
- /**
- * 将中文转化为拼音
- */
- public void chineseToPinyin(List<ContactsModel> list) {
- for (int i = 0; i < list.size() ; i++) {
- ContactsModel contactsModel1 = list.get(i);
- // 将汉字转换为拼音
- String pinyinString = PinyinHelper.getShortPinyin(list.get(i).getName()) ;
- // 将拼音字符串转换为大写拼音
- String upperCasePinyinString = String.valueOf(pinyinString.charAt( 0)).toUpperCase();
- // 获取大写拼音字符串的第一个字符
- char tempChar = upperCasePinyinString.charAt( 0);
- if (tempChar < 'A' || tempChar > 'Z' ) {
- contactsModel1.setFirstLetter( "#");
- } else {
- contactsModel1.setFirstLetter(String.valueOf(tempChar)) ;
- }
- }
- }
- /**
- * 设置回调接口
- */
- public void setCallbackInterface() {
- // 回调接口
- sideBar .setOnCurrentLetterListener(new SideBar.OnCurrentLetterListener() {
- @Override
- public void showCurrentLetter(String currentLetter) {
- txtShowCurrentLetter .setVisibility(View.VISIBLE) ;
- txtShowCurrentLetter.setText(currentLetter) ;
- int position = myAdapter.getCurrentItemPosition(currentLetter);
- if (position != -1 )
- listView.setSelection(position) ;
- }
- @Override
- public void hideCurrentLetter() {
- txtShowCurrentLetter.setVisibility(View. GONE);
- }
- });
- }
- /**
- * 初始化数据
- */
- public List<ContactsModel> initData() {
- List<ContactsModel> list = new ArrayList<>();
- ContactsModel contactsModel ;
- String[] nameStrings = { "覃" , "岑 ", "$ 来啊,来互相伤害啊 ", "疍姬" , "梵蒂冈 ", " 亳州", "佟" , "郄 ", " 张三", "Edward", " 李四", "萌萌哒" , "霾耷 ", " 离散", "赵信" , "啦啦 ", " 辣妹子", "嗷嗷" , "妹妹 ", "']asd" , "%Hello"} ;
- for ( int i = 0 ; i < nameStrings.length ; i++) {
- contactsModel = new ContactsModel() ;
- contactsModel.setName(nameStrings[i]) ;
- list.add(contactsModel) ;
- }
- return list;
- }
- /**
- * 去重数据
- *
- * @param list
- * @param <T>
- * @return
- */
- public <T> List< T> removeDuplicate (List<T> list) {
- Set<T > h = new HashSet<>(list) ;
- list.clear() ;
- list.addAll(h) ;
- return list ;
- }
- }
package per.edward.ui;
import android.os.Bundle ;
import android.support.v7.app.AppCompatActivity ;
import android.view.View ;
import android.widget.ListView ;
import android.widget.TextView ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Comparator ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Set ;
import opensource.jpinyin.PinyinHelper ;
/**
* author:Edward
* 此 demo的博客地址:http://blog.csdn.net/u012814441
*/
public class MainActivity extends AppCompatActivity {
private ListView listView;
private TextView txtShowCurrentLetter;
private SideBar sideBar;
private SideBarAdapter myAdapter;
// private List<ContactsModel> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState) ;
setContentView(R.layout. activity_main);
txtShowCurrentLetter = (TextView) findViewById(R.id.txt_show_current_letter );
sideBar = (SideBar) findViewById(R.id. side_bar);
listView = (ListView) findViewById(R.id. list_view);
setCallbackInterface() ;
List<ContactsModel> list = initData() ;
chineseToPinyin(list) ;
// 将联系人列表的标题字母排序
Collections. sort(list , new Comparator<ContactsModel>() {
@Override
public int compare(ContactsModel lhs, ContactsModel rhs) {
return lhs.getFirstLetter().compareTo(rhs.getFirstLetter()) ;
}
});
// 将联系人列表的标题字母放到 List<String>列表中,准备数据去重
List<String> getLetter = new ArrayList<>();
for ( int i = 0 ; i < list.size(); i++) {
getLetter.add(list.get(i).getFirstLetter());
}
// 数据去重
getLetter = removeDuplicate(getLetter) ;
// 将联系人列表的字母标题排序
Collections. sort(getLetter , new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
return lhs.compareTo(rhs) ;
}
});
// 设置已排序好的标题
sideBar .setLetter(getLetter);
myAdapter = new SideBarAdapter( this, list, R.layout.adapter_side_bar) ;
listView .setAdapter(myAdapter) ;
}
/**
* 将中文转化为拼音
*/
public void chineseToPinyin(List<ContactsModel> list) {
for (int i = 0; i < list.size() ; i++) {
ContactsModel contactsModel1 = list.get(i);
// 将汉字转换为拼音
String pinyinString = PinyinHelper.getShortPinyin(list.get(i).getName()) ;
// 将拼音字符串转换为大写拼音
String upperCasePinyinString = String.valueOf(pinyinString.charAt( 0)).toUpperCase();
// 获取大写拼音字符串的第一个字符
char tempChar = upperCasePinyinString.charAt( 0);
if (tempChar < 'A' || tempChar > 'Z' ) {
contactsModel1.setFirstLetter( "#");
} else {
contactsModel1.setFirstLetter(String.valueOf(tempChar)) ;
}
}
}
/**
* 设置回调接口
*/
public void setCallbackInterface() {
// 回调接口
sideBar .setOnCurrentLetterListener(new SideBar.OnCurrentLetterListener() {
@Override
public void showCurrentLetter(String currentLetter) {
txtShowCurrentLetter .setVisibility(View.VISIBLE) ;
txtShowCurrentLetter.setText(currentLetter) ;
int position = myAdapter.getCurrentItemPosition(currentLetter);
if (position != -1 )
listView.setSelection(position) ;
}
@Override
public void hideCurrentLetter() {
txtShowCurrentLetter.setVisibility(View. GONE);
}
});
}
/**
* 初始化数据
*/
public List<ContactsModel> initData() {
List<ContactsModel> list = new ArrayList<>();
ContactsModel contactsModel ;
String[] nameStrings = { "覃" , "岑 ", "$ 来啊,来互相伤害啊 ", "疍姬" , "梵蒂冈 ", " 亳州", "佟" , "郄 ", " 张三", "Edward", " 李四", "萌萌哒" , "霾耷 ", " 离散", "赵信" , "啦啦 ", " 辣妹子", "嗷嗷" , "妹妹 ", "']asd" , "%Hello"} ;
for ( int i = 0 ; i < nameStrings.length ; i++) {
contactsModel = new ContactsModel() ;
contactsModel.setName(nameStrings[i]) ;
list.add(contactsModel) ;
}
return list;
}
/**
* 去重数据
*
* @param list
* @param <T>
* @return
*/
public <T> List< T> removeDuplicate (List<T> list) {
Set<T > h = new HashSet<>(list) ;
list.clear() ;
list.addAll(h) ;
return list ;
}
}
6、最后特别注意的是。在项目工程的libs文件夹里面需要添加jpinyin.jar包(这个jar包可以在下面的demo源码中copy出来)。
7、 最终效果图
三、结束
好累啊,终于写完了。这是我目前写过最详细的博客了。鼓励一下呗。如果有不足的地方,欢迎指出。谢谢。