Android从零开始之一步一步教你实现联系人功能(一)

在最近的项目中有这样的一个需求,就是要实现类似联系人的列表,包含模糊查询、按照A到Z拼音首字母分组排序、和收藏功能。参考了一下网上的例子,我觉得还是自己亲自操刀来实现所有的功能。今天带领大家先实现联系人右边的侧边栏【A~Z】。先上一张图:
这里写图片描述

可以看到,右边是一个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

  • 10
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值