Android从零开始之一步一步教你实现联系人功能(一)
2016-07-10 14:52
633 查看
在最近的项目中有这样的一个需求,就是要实现类似联系人的列表,包含模糊查询、按照A到Z拼音首字母分组排序、和收藏功能。参考了一下网上的例子,我觉得还是自己亲自操刀来实现所有的功能。今天带领大家先实现联系人右边的侧边栏【A~Z】。先上一张图:
可以看到,右边是一个A到Z的侧边菜单导航栏,当我们点击右边侧边菜单导航栏时,中间显示当前点击item。相信这个UI对大家都很熟悉。很多APP都有这样的界面。最典型的就是通讯录。
新建一个类,名字叫做SiderBarMenu并且继承View,按照自定义view的步骤。先实现它的三个构造方法。如下:
在含有三个构造方法定义一个初始化方法,用于初始化一些我们后面需要用到的变量等。这儿我们初始化Paint(画笔)的实例,以及设置画笔的颜色、字体大小、字体。在这之前我们先定义下我们需要用到的变量:
完成这些之后,我们就要开始画我们的侧边栏菜单了,首先重写View的onDraw()方法,如下:
首先,我们要获取到我们在布局文件当中的宽、高。然后算出每一个item的高度。之后在算出每一个item的X、Y坐标。之后调用canvas.drawText()方法绘制我们的侧边栏,这儿我注释了一个reset()方法,如果调用它我们就需要每次在循环里面重新设置paint的属性,因为前面我在init()方法中设置过,所以我把这个方法注释掉了。而且考虑到后面也没有拿这个画笔做其他操作。这时候我们运行程序应该可以到侧边栏已经绘制成功了。
接下来就是处理触摸事件了。我们需要重写dispatchTouchEvent()方法。来手动处理触摸事件。
这儿代码稍稍长一点,我从上到下给解释下,首先我们要通过event.getAction()方法拿到用户的触摸事件,之后在通过event.getY()方法获取用户触摸的Y坐标。拿到Y坐标之后,我们就可以判断用户当前触摸的哪一个item。计算公式:触摸的Y坐标/控件高度*侧边导航栏item总数。不知道大家能理解到这个公式不,理解不到自己用本子算吧。或者找个六年纪一下的同学帮你算。哈哈!然后定义一个变量oldClick来保存用户之前点击的位置。
完成上面的操作之后,我们就开始判断用户的触摸事件了,这儿我们将ACTION_UP事件单独处理,因为ACTION_DOWN、ACTION_MOVE的处理逻辑一样,所以我放到了default里面,
ACTION_UP:
我们需要处理当用户抬手之后隐藏TextView并且将click恢复默认值,不然当你重复点击同一个item是没有效果的。
ACTION_DOWN&ACTION_MOVE:
首先判断之前点击的item位置和现在的位置是否一样,如果不一样,再判断当前位置是否越界,之后设置要显示的内容并且将TextView设置为可见,之后将当前位置重新赋值,并且通知View树重绘。
最后我们再向外抛一个设置TextView的方法。
现在在我们布局文件中使用:
最后在MainActivity中使用:
运行程序,你就可以看到文章开头的效果了。源码地址:https://github.com/PoisonH/Contacts
可以看到,右边是一个A到Z的侧边菜单导航栏,当我们点击右边侧边菜单导航栏时,中间显示当前点击item。相信这个UI对大家都很熟悉。很多APP都有这样的界面。最典型的就是通讯录。
新建一个类,名字叫做SiderBarMenu并且继承View,按照自定义view的步骤。先实现它的三个构造方法。如下:
public SideBarMenu(Context context) { this(context, null); } public SideBarMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SideBarMenu(Context context, AttributeSet attrs, int style) { super(context, attrs, style); init(); }
private void init() { mSideBarMenuPaint = new Paint(); //设置画笔颜色 mSideBarMenuPaint.setColor(Color.RED); //设置字体大小 mSideBarMenuPaint.setTextSize(30); //设置字体 mSideBarMenuPaint.setTypeface(Typeface.DEFAULT_BOLD); }
在含有三个构造方法定义一个初始化方法,用于初始化一些我们后面需要用到的变量等。这儿我们初始化Paint(画笔)的实例,以及设置画笔的颜色、字体大小、字体。在这之前我们先定义下我们需要用到的变量:
//画笔 private Paint mSideBarMenuPaint; //定义一个用户点击item标识 private int click = -1; //侧边导航栏的文字 private String[] SIDEBAR = {"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"}; //显示tips的控件 private TextView mTvTips;
完成这些之后,我们就要开始画我们的侧边栏菜单了,首先重写View的onDraw()方法,如下:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //获取控件的宽、高度,即你在布局文件当中设置的:layout_width、layout_height两个的值; int width = getWidth(); int height = getHeight(); //每个Item高度 int SideBarItemHeight = height / SIDEBAR.length; for (int i = 0; i < SIDEBAR.length; i++) { //计算我们要画文字的X坐标,计算公式:控件宽度/2-文字宽度/2 目的:文字水平方向居中 float xPos = width / 2 - mSideBarMenuPaint.measureText(SIDEBAR[i]) / 2; //计算我们要画文字的Y坐标,计算公式:控件高度*当前项数+控件高度/2 目的:文字垂直方向居中 float yPos = SideBarItemHeight * i + SideBarItemHeight / 2; //画出侧边导航栏 canvas.drawText(SIDEBAR[i], xPos, yPos, mSideBarMenuPaint); //重置画笔,如果重置画笔,则必须重新设置画笔属性 //mSideBarMenuPaint.reset(); } }
首先,我们要获取到我们在布局文件当中的宽、高。然后算出每一个item的高度。之后在算出每一个item的X、Y坐标。之后调用canvas.drawText()方法绘制我们的侧边栏,这儿我注释了一个reset()方法,如果调用它我们就需要每次在循环里面重新设置paint的属性,因为前面我在init()方法中设置过,所以我把这个方法注释掉了。而且考虑到后面也没有拿这个画笔做其他操作。这时候我们运行程序应该可以到侧边栏已经绘制成功了。
接下来就是处理触摸事件了。我们需要重写dispatchTouchEvent()方法。来手动处理触摸事件。
/** * 手动处理触摸事件 * * @param event * @return */ @Override public boolean dispatchTouchEvent(MotionEvent event) { //获取触摸事件:ACTION_DOWN、ACTION_MOVE、ACTION_UP int action = event.getAction(); //获取触摸的Y坐标 float yPos = event.getY(); //判断点击的是那一个item。计算公式:触摸的Y坐标/控件高度*侧边导航栏item总数。 int pos = (int) (yPos / getHeight() * SIDEBAR.length); //记录之前用户点击的item int oldClick = click; //处理触摸事件,up事件单独处理,其他(ACTION_DOWN、ACTION_MOVE)在default中去处理。 switch (action) { case MotionEvent.ACTION_UP: //复位 click = -1; if (mTvTips != null) { mTvTips.setVisibility(View.GONE); } invalidate(); break; default: if (oldClick != pos) { if (pos > 0 && pos < SIDEBAR.length) { if (mTvTips != null) { mTvTips.setText(SIDEBAR[pos]); mTvTips.setVisibility(View.VISIBLE); } click = pos; //通知View树重绘,主线程用:invalidate(),非主线程用:postInvalidate(); invalidate(); } } break; } return true; }
这儿代码稍稍长一点,我从上到下给解释下,首先我们要通过event.getAction()方法拿到用户的触摸事件,之后在通过event.getY()方法获取用户触摸的Y坐标。拿到Y坐标之后,我们就可以判断用户当前触摸的哪一个item。计算公式:触摸的Y坐标/控件高度*侧边导航栏item总数。不知道大家能理解到这个公式不,理解不到自己用本子算吧。或者找个六年纪一下的同学帮你算。哈哈!然后定义一个变量oldClick来保存用户之前点击的位置。
完成上面的操作之后,我们就开始判断用户的触摸事件了,这儿我们将ACTION_UP事件单独处理,因为ACTION_DOWN、ACTION_MOVE的处理逻辑一样,所以我放到了default里面,
ACTION_UP:
我们需要处理当用户抬手之后隐藏TextView并且将click恢复默认值,不然当你重复点击同一个item是没有效果的。
ACTION_DOWN&ACTION_MOVE:
首先判断之前点击的item位置和现在的位置是否一样,如果不一样,再判断当前位置是否越界,之后设置要显示的内容并且将TextView设置为可见,之后将当前位置重新赋值,并且通知View树重绘。
最后我们再向外抛一个设置TextView的方法。
public void setTextView(TextView tips) { this.mTvTips = tips; }
现在在我们布局文件中使用:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="right"> <com.poisonh.contacts.widgets.SideBarMenu android:id="@+id/sbm_siderbarmenu" android:layout_width="40dp" android:layout_height="wrap_content" android:layout_alignParentRight="true"/> <TextView android:id="@+id/tv_tips" android:layout_width="50dp" android:layout_height="50dp" android:visibility="gone" android:layout_centerInParent="true" android:background="@drawable/shape_tips_bg" android:gravity="center" android:text="H" android:textColor="#fff"/> </RelativeLayout>
最后在MainActivity中使用:
public class MainActivity extends AppCompatActivity { private SideBarMenu mSiderBarMenu; private TextView mTvTips; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); //setClickListener(); } private void initView() { mSiderBarMenu = (SideBarMenu) findViewById(R.id.sbm_siderbarmenu); mTvTips = (TextView) findViewById(R.id.tv_tips); mSiderBarMenu.setTextView(mTvTips); } }
运行程序,你就可以看到文章开头的效果了。源码地址:https://github.com/PoisonH/Contacts
相关文章推荐
- Android学习笔记(四)View和Layout
- Android系统各种类型的service刨根解读
- 深入理解Android四大组件之一ContentProvider
- 给 Android 开发者的 RxJava 详解
- Android序列化和反序列化
- Android 推荐几款好用的开源作品(一)之ViewPager指示器
- Android adapter 中动态设置控件的方法
- Android之JSON解析
- android 自定义View (5) 音量控制
- android ant 打包,不能打出apk:Library project: do not create apk...
- Android多媒体之MediaPlayerService
- Android中关于传递对象的俩种用法,也就是Serializable和Parcelable的使用
- Android开发学习之路-RecyclerView滑动删除和拖动排序
- Android Netd
- android toolbar收缩的实现{局限性非常强}
- EventBus
- Android之Pull解析XML文件
- Android中关于系统Intent的一些应用的说明
- Imooc---Android工程师
- Android 关于android:foreground设置无效的问题