Android-联系人A~Z列表
2015-12-04 16:55
260 查看
Android-联系人A~Z列表实现旅途
将右侧A~Z显示出来
自定义一个A~Z垂直显示的View(自定义控件命名为:LetterView.java)/** * 靠右的字母控件 */ public class LetterView extends View { /**纵向显示的所有字符*/ public static final String letters = "*ABCDEFGHIJKLMNOPQRSTUVWXYZ#"; private int width;//控件宽度 private int height;//控件高度 private int abcHeight;//每个字母的高度 private Paint paint; public LetterView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { paint = new Paint(); //抗锯齿 paint.setAntiAlias(true); //加粗 paint.setFakeBoldText(true); //字体大小 paint.setTextSize(Float.parseFloat(getResources().getString(R.string.text_size))); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (width == 0 || height == 0)//判断是否是第一次绘画,给宽高赋值 { width = getWidth(); height = getHeight(); abcHeight = height / letters.length(); } for (int i = 0, length = letters.length(); i < length; i++) { //计算字母绘制的xy坐标,paint.measureText(letters.charAt(i)+"") 得到字母的宽 float x = (width - paint.measureText(letters.charAt(i) + "")) / 2; float y = abcHeight * i + abcHeight - paint.measureText(letters.charAt(i) + "")/2; canvas.drawText(letters.charAt(i) + "", x, y, paint); } } }
在xml布局文件中添加(布局命名为:letters_layout.xml)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.view.LetterView android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true"/> </RelativeLayout>
效果图
为A~Z设置点击监听
1、 自定义控件LetterView.java(#开头的注释表示新添加的内容)/** * 靠右的字母 */ public class LetterView extends View { /** * #>触碰时候的背景颜色 */ public static final int COLOR_BG = 0x17000000; /** * #>没有触碰时的背景颜色 */ public static final int COLOR_NO_BG = 0x07000000; /** * #>触碰状态下所有字母的颜色 */ public static final int TEXT_COLOR_NORMAL = 0xff545454; /** * #>选中的字母颜色 */ public static final int TEXT_COLOR_SELECTED = 0xffff5e00; /** * 没有触碰状态下的字母颜色 */ public static final int TEXT_COLOR_UNTOUCH = 0xffa3a3a3; public static final String letters = "☆ABCDEFGHIJKLMNOPQRSTUVWXYZ#"; private int width; private int height; //每个字母的高度 private int abcHeight; private Paint paint; private int selectedIndex = 1;//#>被选中字母的下标 private boolean isTouch = false;//#>是否处于触碰的状态 public LetterView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { paint = new Paint(); paint.setAntiAlias(true); //加粗 paint.setFakeBoldText(true); paint.setTextSize(Float.parseFloat(getResources().getString(R.string.text_size))); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //#>根据触碰状态改变控件的背景颜色 if (isTouch) { setBackgroundColor(COLOR_BG); } else { setBackgroundColor(COLOR_NO_BG); } if (width == 0 || height == 0) { width = getWidth(); height = getHeight(); abcHeight = height / letters.length(); } for (int i = 0, length = letters.length(); i < length; i++) { if (selectedIndex == i)//#>设置被选中的字母颜色 { paint.setColor(TEXT_COLOR_SELECTED); } else { if (isTouch)//#>设置触碰状态下的所有的字母颜色 { paint.setColor(TEXT_COLOR_NORMAL); } else//#>设置非点击状态下的所有的字母颜色 { paint.setColor(TEXT_COLOR_UNTOUCH); } } //计算字母绘制的xy坐标,paint.measureText(letters.charAt(i)+"") 得到字母的宽 float x = (width - paint.measureText(letters.charAt(i) + "")) / 2; float y = abcHeight * i + abcHeight - paint.measureText(letters.charAt(i) + "") / 2; canvas.drawText(letters.charAt(i) + "", x, y, paint); } } private float y;//#>点击的y坐标 private int lastSelectedIndex = -1;//#>记录上一次的位置 @Override public boolean onTouchEvent(MotionEvent event) { y = event.getY();//#>获取当前触摸时的y坐标 selectedIndex = (int) (y / abcHeight);//#>计算出当前触碰到的字母下标 if (selectedIndex <= 0) selectedIndex = 1;//#>如果下标处于0将,下标改为1,不让下标为0的☆产生监听 if (selectedIndex >= letters.length() - 1) selectedIndex = letters.length() - 2;//#>如果下标处于最后将,下标改为letters.length() - 2,不让最下面的#产生监听 if (selectedIndex != lastSelectedIndex) {//#>如果触摸的地方不是上一次的y轴位置,重绘,调用回调中间显示字母 invalidate(); if (letterChangeListener != null) { letterChangeListener.onLetterChange(selectedIndex); } } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isTouch = true; break; case MotionEvent.ACTION_UP: letterChangeListener.onClickUp();// isTouch = false; break; } invalidate(); return true; } /** * #>回调接口,处理字母的点击事件 */ //#>回调接口(当前是哪一栏,则字母就显示哪一个) public interface OnLetterChangeListener { void onLetterChange(int selectedIndex);//#>当位置发生改变时调用 void onClickUp();//#>当触摸后,放开时调用 } private OnLetterChangeListener letterChangeListener; public void setOnLetterChangeListener(OnLetterChangeListener letterChangeListener) { this.letterChangeListener = letterChangeListener; } //设置当前那个字母被选中 public void setSelected(int section) { this.selectedIndex = section; invalidate(); } }
2、 在xml布局文件letters_layout.xml中
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.view.LetterView android:id="@+id/letterView" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true"/> <!--中间显示被点到的字母--> <TextView android:id="@+id/show_now_abc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="A" android:layout_centerInParent="true" android:visibility="gone" android:textSize="60sp"/> </RelativeLayout>
3、在Activity中(MainActivity.java)
public class MainActivityextends Activity{ private LetterView letterView; private TextView tvToast; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.letters_layout); letterView = (LetterView) findViewById(R.id.letterView); tvToast = (TextView) findViewById(R.id.show_now_abc); letterView.setOnLetterChangeListener(new LetterView.OnLetterChangeListener() { @Override public void onLetterChange(int selectedIndex) { tvToast.setText(LetterView.letters.charAt(selectedIndex) + "");//设置中间显示的字母 tvToast.setVisibility(View.VISIBLE);//设置为可见 } @Override public void onClickUp() { tvToast.setVisibility(View.GONE);//当放开时,设置为不可见 } }); }
当前效果图
最终要实现效果,在下面解析
加上一个ListView列表布局
1、主布局letters_layout.xml<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/listView_express" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scrollbars="none"/> <!-- 引用另一个布局文件 --> <include layout="@layout/express_overlay" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <com.view.LetterView android:id="@+id/letterView" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true" /> <TextView android:id="@+id/show_now_abc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="A" android:textColor="#ff5e00" android:textSize="60sp" android:visibility="gone" /> </RelativeLayout>
2、顶部布局express_overlay.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/viewOverlay_express" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/tvOverlay_express" android:layout_width="fill_parent" android:layout_height="45dp" android:background="@android:color/holo_orange_light" android:gravity="center_vertical" android:paddingLeft="15dp" android:text="A" android:textColor="@android:color/holo_green_dark" android:textSize="20sp" android:textStyle="bold" /> </FrameLayout>
3、item子布局item_express.xml
<?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/tvLetter_item_express" android:layout_width="fill_parent" android:layout_height="45dp" android:background="#17000000" android:clickable="true" android:gravity="center_vertical" android:paddingLeft="15dp" android:text="A" android:textSize="20sp" android:textStyle="bold" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="60dp"> <TextView android:id="@+id/tvCompanyName_item_express" android:layout_width="fill_parent" android:layout_height="60dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:singleLine="true" android:text="申通快递" android:textColor="#000000" android:textSize="20sp" /> </RelativeLayout> </LinearLayout>
为ListView设置适配器
1、这里使用了一个数据库,表结构如下图
2、这里使用了AlphabetIndexer字母索引辅助类。去了解AlphabetIndexer
3、下面是适配器代码
public class OrderAdapter extends BaseAdapter { private Cursor cursor;//接收根据字母排序好了的cursor private LayoutInflater inflater;//布局填充器,加载ListView子布局 private AlphabetIndexer indexer;//AlphabetIndexer字母索引辅助类 public OrderAdapter(Context context, Cursor cursor, AlphabetIndexer indexer) { this.cursor = cursor; inflater = LayoutInflater.from(context); this.indexer = indexer; } @Override public int getCount() { return cursor.getCount(); } @Override public Cursor getItem(int position) { cursor.moveToPosition(position); return cursor; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = inflater.inflate(R.layout.item_express, parent, false); holder = new ViewHolder(); holder.tvLetter = (TextView) convertView.findViewById(R.id.tvLetter_item_express); holder.tvCompanyName = (TextView) convertView.findViewById(R.id.tvCompanyName_item_express); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } cursor.moveToPosition(position); holder.tvCompanyName.setText(cursor.getString(cursor.getColumnIndex(ExpressDbHelper.TABLE_COMPANY_COMPANY_NAME))); //获取这个位置代表的字符在字符表中的位置 int section = indexer.getSectionForPosition(position); //判断当前位置是否是第一个出现这个字符,indexer.getPositionForSection(section)获取第一次出现的位置 if (position == indexer.getPositionForSection(section)) { holder.tvLetter.setVisibility(View.VISIBLE); holder.tvLetter.setText(cursor.getString(cursor.getColumnIndex(ExpressDbHelper.TABLE_COMPANY_COMPANY_INITIAL))); } else { holder.tvLetter.setVisibility(View.GONE); } return convertView; } private class ViewHolder { TextView tvLetter; TextView tvCompanyName; } }
4、数据库帮助类(ExpressDbHelper.java)
public class ExpressDbHelper extends SQLiteOpenHelper{ public static final String DB_NAME = "express.db"; public static final int VERSION = 1; /**快递公司名字**/ public static final String TABLE_COMPANY_COMPANY_NAME = "company_name"; /**快递公司对应code**/ public static final String TABLE_COMPANY_COMPANY_CODE = "company_code"; /**公司名字对应的首字母**/ public static final String TABLE_COMPANY_COMPANY_INITIAL = "initial"; public ExpressDbHelper(Context context) { super(context, DB_NAME, null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
5、在MainActivity.java中
public class MainActivity extends Activity { private ExpressDbHelper helper; private LetterView letterView; private TextView tvToast; private ListView lv; /*顶部的view*/ private View viewTop; /*顶部显示的字母*/ private TextView tvTop; private RelativeLayout.LayoutParams params; /*字母索引辅助类*/ private AlphabetIndexer indexer; /*字母索引*/ private String alphabet = "☆ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); copyDatabase();//将assets目录下的数据库拷贝到程序的包中 initView(); afterInit();//主要处理部分 } protected void initView() { letterView = (LetterView) findViewById(R.id.letterView); tvToast = (TextView) findViewById(R.id.show_now_abc); lv = (ListView) findViewById(R.id.listView_express); viewTop = findViewById(R.id.viewOverlay_express); tvTop = (TextView) findViewById(R.id.tvOverlay_express); params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); } protected void afterInit() { helper = new ExpressDbHelper(this);//获得一个数据库帮助类 /*从数据库获取所有数据并根据公司对应的首字母排序*/ Cursor cursor = helper.getReadableDatabase().rawQuery("select * from company order by initial", null); /* * 参数1:包含数据的Cursor对象 * 参数2:进行索引排序的列号 * 参数3:字母表(空格将会作为第一个字符。字母要大写,并且按ascii/unicode排序。) */ indexer = new AlphabetIndexer(cursor, cursor.getColumnIndex("initial"), alphabet); lv.setAdapter(new OrderAdapter(this, cursor, indexer)); letterView.setOnLetterChangeListener(new LetterView.OnLetterChangeListener() { @Override public void onLetterChange(int selectedIndex) { lv.setSelection(indexer.getPositionForSection(selectedIndex));//跳到以这个字母为索引的第一个名字位置 tvToast.setText(LetterView.letters.charAt(selectedIndex) + "");//设置中间显示的字母 tvToast.setVisibility(View.VISIBLE);//设置为可见 } @Override public void onClickUp() { tvToast.setVisibility(View.GONE);//当放开时,设置为不可见 } }); //给ListView设置滑动的监听,动态的改变顶部标题的显示 lv.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } /* * firstVisibleItem表示在现时屏幕第一个ListItem(部分显示的ListItem也算) * totalItemCount表示ListView的ListItem总数 * view 表示整个ListView */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //按指定数据项的位置,返回匹配的索引项。 int section = indexer.getSectionForPosition(firstVisibleItem); //得到下一个索引项 int nextSection = section+1; //按指定索引查找,返回匹配的第一行数据项位置或比较接近的数据项的位置 int nextPosition = indexer.getPositionForSection(nextSection); //#=>判断ListView第二排的item的位置是否是下一个索引项的开始 if (firstVisibleItem + 1 != nextPosition) {//标题显示不改变的状态下 params.topMargin = 0; viewTop.setLayoutParams(params); tvTop.setText(alphabet.charAt(section) + ""); } else {//标题显示改变的状态下 View v = view.getChildAt(0); if (v == null) { return; } int dex = v.getBottom() - tvTop.getHeight(); if (dex <= 0) { params.topMargin = dex; } else { params.topMargin = 0; } tvTop.setText(alphabet.charAt(section) + ""); viewTop.setLayoutParams(params); } letterView.setSelected(section); } }); } /** * 拷贝express.db到数据库 */ private void copyDatabase(){ try { InputStream in = getAssets().open("express.db"); //得到数据库文件的路径 File file = getDatabasePath("express.db"); if(!file.exists()){ if(!file.getParentFile().exists()){ file.getParentFile().mkdir(); } file.createNewFile(); } else { return; } FileOutputStream fos = new FileOutputStream(file); byte[] buffer = new byte[1024]; int lenght; while((lenght = in.read(buffer))!=-1){ fos.write(buffer, 0, lenght); } fos.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } }
项目代码地址
http://download.csdn.net/detail/u014314614/9326443我的感受
把这玩意理解完一下子就觉得简单多了,开始老是看代码去理解并不能真的理清思路,下次要看代码最好先知道整体的结构,然后从一个突破点一步步做下去,避免东想西想浪费时间。记住一切复杂都是从简单的小结构出发这里的主要使用的新知识点就是AlphabetIndexer字母索引辅助类,让右侧字母索引和ListView的item显示联系起来
通过这个例子,以后若要用,我只要提供了一个数据库,数据库里面包含名字和名字的开头字母,我就能改下数据库就能方便的使用这个案例了
想说的话
博客坚持写,今后学习了新的东西就在这里记录一下,以便今后回顾,也希望小小笔记能帮助你们若内容有什么地方不对、不清楚,还望吐槽,希望大家能一起成长
来一个: 阅读源码是一个学好编程的重要途径,不仅学习到编程思路、实现技巧和风格,更是能形成自己独特思路和风格。其中一定有很多无法理解的代码、英文看不懂等问阻扰着,坚持下去,突破重重困难。了解全局理清细节
相关文章推荐
- Android之DDMS无法查看data目录, 并查看数据库的方法
- 老猪带你玩转android自定义控件一——打造最简单viewpagerindicator
- 短信加密机制的设计
- Android MediaPlayer使用方法简单介绍
- android(13)(sqlite的CRUD使用SQL和API两种方式实现以及Linearlayout的列表展示)
- Android Messenger简单的跨进程通信
- xml转bean
- Android中<meta-data>的使用
- Android自定义View----1. 自定义自绘控件
- Android 中实现5.0按钮水波纹反馈效果
- Android  draw canvas save restore saveLayer 学习
- android屏幕适配
- Android平台Camera实时滤镜实现方法探讨(六)--创建帧缓存对象(FBO)加速实时滤镜处理
- Android应用 中英文切换
- Android:用Handler实现异步处理功能
- android mediaplayer 实现歌曲边播放边下载
- android学习笔记(七):Handler消息传递机制
- android开发之软键盘控制
- Android之View基础总结(View的事件体系一)
- Android开发周报:弹幕源码开放送