本篇博客将讲述搜索通讯录,就是微信常见的通讯录
通过点击右边的字母案件实现和左边条目联动:
首先要自定义一个控件,看代码:
public class IndexSiderBar extends View {
private ChoceInterFace choceInterFace;
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 Paint textPaint;
private ArrayList<String> singLin=new ArrayList<>();
private Paint circlePaint;
private int width;
private int height;
private int textHeight;
private int mChoose;
private TextView textInfo;
public void setChoceInterFace(ChoceInterFace choceInterFace) {
this.choceInterFace = choceInterFace;
}
public IndexSiderBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public IndexSiderBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
textInfo=new TextView(context);
}
private void init() {
for(String line:b){
singLin.add(line);
}
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
textPaint.setStrokeWidth(22);
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setStyle(Paint.Style.FILL);
circlePaint.setStrokeWidth(10);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth();
height = getHeight();
textHeight = height / (singLin.size() + 1);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mChoose!=-1){
circlePaint.setColor(Color.parseColor("#FF4081"));
}else {
circlePaint.setColor(Color.parseColor("#4a4949"));
}
canvas.drawCircle(textHeight/2.0f,textHeight/2.0f,textHeight/4.0f,circlePaint);
for (int i = 0; i < singLin.size(); i++) {
if(mChoose==i){
textPaint.setColor(Color.parseColor("#FF4081"));
}else {
textPaint.setColor(Color.parseColor("#4a4949"));
}
//文字的x坐标轴
float xPos = width / 2- textPaint.measureText(singLin.get(i)) / 2;
Paint.FontMetricsInt fm = textPaint.getFontMetricsInt();
int centerLine = textHeight * (i + 1) + textHeight/ 2;
int baseLine = centerLine + (fm.bottom - fm.top) / 2 - fm.bottom;
canvas.drawText(singLin.get(i),xPos,baseLine,textPaint);
invalidate();
}
}
}
上面的代码绘制了右边的条目列表,接下来要实现的是onTouch事件,可以获取触摸位置:
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
float posY = y / height * (singLin.size() + 1);
mChoose = (int) (posY - 1);
String text = singLin.get(mChoose);
if(null!=textInfo){
Log.i("TAG------------>", "是否设置了");
textInfo.setVisibility(VISIBLE);
textInfo.setText(text);
choceInterFace.onChose(mChoose,text);
}
switch (event.getAction()){
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_UP:
mChoose=-1;
if(null!=textInfo){
textInfo.setVisibility(GONE);
}
break;
}
invalidate();
return true;
}
第三步,要实现触摸条目,得到放大的控件:
效果图
主布局中如下:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.longyue.mycustview.MainActivity">
<ListView
android:id="@+id/main_listView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
<IndexSider.IndexSiderBar
android:id="@+id/main_index_barview"
android:layout_width="25dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="@mipmap/line_cycle_an"/>
<Button
android:id="@+id/index_info"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_toLeftOf="@id/main_index_barview"
android:layout_margin="10dp"
android:background="@mipmap/android_xuankuang"
android:textSize="15sp"
android:textColor="@color/colorBlue"
android:visibility="invisible"
android:text="文字"/>
</RelativeLayout>
mainactivity中代码:
public class MainActivity extends AppCompatActivity {
private Button index_info;
private IndexSiderBar siderBar;
private ListView mListView;
private List<String> datas=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
index_info= (Button) findViewById(R.id.index_info);
siderBar= (IndexSiderBar) findViewById(R.id.main_index_barview);
mListView= (ListView) findViewById(R.id.main_listView);
for (int i = 0; i < siderBar.b.length; i++) {
datas.add(siderBar.b[i]+((i+1)%2));
datas.add(siderBar.b[i]+((i+2)%2));
}
final CustIndex custIndex = new CustIndex(IndexSiderBar.b, datas);
mListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,datas));
siderBar.setTextInfo(index_info);
siderBar.setChoceInterFace(new IndexSiderBar.ChoceInterFace() {
@Override
public void onChose(int chose, String info) {
Log.i("TAG------------>", "chose:"+chose+",info:"+info);
int selection = custIndex.getPositionForSection(chose);
Log.i("TAG------------>", "获取的位置:"+selection);
mListView.setSelection(selection);
}
});
}
}
上面有一个类CustIndex实现了SectionIndexer接口,该接口提供三个方法:
Object[] getSections(); int getPositionForSection(int sectionIndex); int getSectionForPosition(int position);
第一个函数用于返回数据数组,第二个函数传入点击的位置返回listView对应的位置,第三个函数用于listView位置对应的导航栏的位置:
看代码:
public class CustIndex implements SectionIndexer {
private String[] mIndexString;
private List<String> mDatas=new ArrayList<>();
private SparseIntArray mAlphaMap;
public CustIndex(String[] mIndexString, List<String> data) {
this.mIndexString = mIndexString;
mDatas.addAll(data);
mAlphaMap = new SparseIntArray(this.mIndexString.length);
}
@Override
public String[] getSections() {
return mIndexString;
}
@Override
public int getPositionForSection(int sectionIndex) {
final SparseIntArray alphaMap = mAlphaMap;
final List<String> items = mDatas;
if (items == null || mIndexString == null) {
return 0;
}
if (sectionIndex <= 0) {
return 0;
}
if (sectionIndex >= mIndexString.length) {
sectionIndex = mIndexString.length - 1;
}
int count = items.size();
int start = 0;
int end = count;
int pos;
char letter = mIndexString[sectionIndex].charAt(0);
String targetLetter = Character.toString(letter);
int key = letter;
// 检查map是否已经保存了key为letter的对应索引,如果有的话直接返回,如果没有则进行查找
if (Integer.MIN_VALUE != (pos = alphaMap.get(key, Integer.MIN_VALUE))) {
if (pos < 0) {
//如果没有,则将end设为一个极大值,以保证利用二分查找时,肯定能包含listview中所有item的索引
pos = -pos;
end = pos;
} else {
// Not approximate, this is the confirmed start of section, return it
return pos;
}
}
if (sectionIndex > 0) {
int prevLetter = mIndexString[sectionIndex-1].charAt(0);
int prevLetterPos = alphaMap.get(prevLetter, Integer.MIN_VALUE);
if (prevLetterPos != Integer.MIN_VALUE) {
start = Math.abs(prevLetterPos);
}
}
pos = (end + start) / 2;
while (pos < end) {
// Get letter at pos
String item = items.get(pos);
String curName = "";
if (item != null) {
curName = item.charAt(0)+"";
}
if (curName == null) {
if (pos == 0) {
break;
} else {
pos--;
continue;
}
}
int diff = compare(curName, targetLetter);
if (diff != 0) {
if (diff < 0) {
//如果已经到了Listview的末尾,但仍没有索引字符对应的item,就将最末Item的索引返回.
start = pos + 1;
if (start >= count) {
pos = count;
break;
}
} else {
end = pos;
}
} else {
// They're the same, but that doesn't mean it's the start
if (start == pos) {
// This is it
break;
} else {
// Need to go further lower to find the starting row
end = pos;
}
}
pos = (start + end) / 2;
}
alphaMap.put(key, pos);
return pos;
}
@Override
public int getSectionForPosition(int position) {
return 0;
}
protected int compare(String firstLetter, String secondLetter) {
if (firstLetter == null||firstLetter.length() == 0) {
firstLetter = " ";
} else {
firstLetter = firstLetter.substring(0, 1);
}
if (secondLetter == null||secondLetter.length() == 0) {
secondLetter = " ";
} else {
secondLetter = secondLetter.substring(0, 1);
}
return firstLetter.compareTo(secondLetter);
}
}
上面方法用到了二分法来查询数据,有兴趣的童鞋可以下载源码查看具体信息,奉上:
github地址
欢迎关注我的博客。