仿小米通讯录 右侧滑动条与带动画的悬停列表实现(二)

效果图

这里写图片描述

为了能一眼看出如何实现 看下图

这里写图片描述

上面的title就是用到了一个RecyclerView.ItemDecoration

从字面上翻译他就是一个recyclerview item 的装饰

如何使用?

自定义一个类(IndicatorDecoration )继承 RecyclerView.ItemDecoration
IndicatorDecoration extends RecyclerView.ItemDecoration 
构造方法进行一些初始化 别忘了传入 Context
public IndicatorDecoration(Context ctx, List<? extends ContactsBean> data) {
        super();
        mContext = ctx;
        mData = data;

        final TypedArray a = mContext.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();

        mTitleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, ctx
                .getResources().getDisplayMetrics());
        mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15, ctx
                .getResources().getDisplayMetrics());
        //抗锯齿
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setTextSize(mTextSize);
    }
设置item的边框 其实就是设置个左上右下的边距 给要Draw的东西留下位置
//设置item周围边框的补偿
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State
            state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        if (position == 0) {
            outRect.set(0, mTitleHeight, 0, 0);//左上右下
        } else if (mData.get(position).index != null && mData.get(position).index != mData.get
                (position - 1).index) {
            //如果当前条目的index 也就是 拼音首字母 和上一个条目不同 则有title
            outRect.set(0, mTitleHeight, 0, 0);
        } else {
            outRect.set(0, 0, 0, 0);
        }
    }

开始画

RecyclerView的draw方法中会先通过super.draw()调用父类也就是View的draw方法,进而继续调用RecyclerView的OnDraw方法,ItemDecorations的onDraw方法就在此时会被调用
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

        int left;
        int top;
        int right;
        int bottom;

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams childParams = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            int position = childParams.getViewLayoutPosition();

            left = parent.getPaddingLeft();
            top = child.getTop() - childParams.topMargin - mTitleHeight;
            right = parent.getWidth() - parent.getPaddingRight();
            bottom = top + mTitleHeight;

            if (position == 0) {

                drawTitle(c, left, top, right, bottom, child, childParams, position);
            } else if (mData.get(position).index != null && !mData.get(position).index .equals(mData.get
                    (position - 1).index) ) {

                drawTitle(c, left, top, right, bottom, child, childParams, position);
            }
        }
    }
private void drawTitle(Canvas c, int left, int top, int right, int bottom, View child,
                           RecyclerView.LayoutParams
                                   params, int position) {

        mPaint.setColor(mContext.getResources().getColor(R.color.md_blue_300));
        c.drawRect(left, top, right, bottom, mPaint);
        mPaint.setColor(mContext.getResources().getColor(R.color.md_white_1000));

        float textHeight = getTextHeight(mData.get(position).name);

        // 将字母绘制到 title的中间
        c.drawText(mData.get(position).index, child.getPaddingLeft(), child.getTop() - params
                .topMargin - mTitleHeight / 2 + textHeight / 2, mPaint);

    }
返回文字的高
 /**
     * @param text
     * @return 返回文字的高
     */
    private float getTextHeight(String text) {
        Rect rect = new Rect();
        mPaint.getTextBounds(text, 0, text.length(), rect);
        return rect.height();
    }

动画效果实现 onDrawOver在OnDraw 方法结束后调用

当处于这种临界值 child.getTop() + child.getHeight() = mTitleHeight
下一步 上面的title将要上移 (注:getTop此时为负)
所以就将 canvas上移。
这里写图片描述

    @Override
 public void onDrawOver(Canvas c, final RecyclerView parent, RecyclerView.State state) {
        int firstPos = ((LinearLayoutManager) parent.getLayoutManager())
                .findFirstVisibleItemPosition();

        View child = parent.findViewHolderForAdapterPosition(firstPos).itemView;
        //canvas是否移动过的
        boolean flag = false;
        if (!mData.get(firstPos).index.equals(mData.get(firstPos + 1 ).index)) {
            if (child.getTop() + child.getHeight() < mTitleHeight) {
                //在canvas 移动前先保存他的状态
                c.save();
                flag = true;
                c.translate(0, child.getTop() + child.getHeight() - mTitleHeight);
            }

        }

        //mPaint.setColor(mContext.getResources().getColor(R.color.md_blue_300));
        mPaint.setColor(mContext.getResources().getColor(R.color.md_amber_900));


        c.drawRect(parent.getPaddingLeft(), parent.getPaddingTop(), parent.getRight() - parent
                .getPaddingRight(), parent.getPaddingTop() + mTitleHeight, mPaint);
        mPaint.setColor(mContext.getResources().getColor(R.color.md_white_1000));

        float textHeight = getTextHeight(mData.get(firstPos).name);

        // 将字母绘制到 title的中间
        c.drawText(mData.get(firstPos).index, child.getPaddingLeft(), mTitleHeight - mTitleHeight
                / 2 + textHeight / 2, mPaint);

        //如果canvas 移动过 回到他保存前的状态
        if(flag){
            c.restore();
        }

    }

github:https://github.com/REIGE/AddressBook

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现通讯录左右滑动翻页功能需要使用ViewPager组件,它是Android中用来实现左右滑动翻页的重要组件,下面是实现该功能的步骤: 1. 在XML布局文件中添加ViewPager组件,例如: ```xml <androidx.viewpager.widget.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 在Activity中声明ViewPager,并将其与Adapter绑定,例如: ```java ViewPager viewPager = findViewById(R.id.viewPager); MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(adapter); ``` 其中MyPagerAdapter是继承自FragmentPagerAdapter的自定义Adapter,需要在其中实现getItem()和getCount()方法,用来返回ViewPager所需的Fragment和Fragment数量。 3. 在Activity中添加ViewPager的滑动监听器,用来更新当前页面的位置,例如: ```java viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // Do nothing } @Override public void onPageSelected(int position) { // Update current page position currentPosition = position; } @Override public void onPageScrollStateChanged(int state) { // Do nothing } }); ``` 其中currentPosition是当前页面位置的变量,在onPageSelected()方法中更新。 4. 在Activity中设置手势监听器,用来实现左右滑动翻页功能,例如: ```java viewPager.setOnTouchListener(new View.OnTouchListener() { private float startX = 0; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = event.getX(); break; case MotionEvent.ACTION_UP: float endX = event.getX(); float deltaX = endX - startX; if (deltaX > 0) { // Swipe right if (currentPosition > 0) { viewPager.setCurrentItem(currentPosition - 1, true); } } else { // Swipe left if (currentPosition < adapter.getCount() - 1) { viewPager.setCurrentItem(currentPosition + 1, true); } } break; default: break; } return false; } }); ``` 其中startX是手指按下时的X坐标,endX是手指抬起时的X坐标,deltaX是两者之差。根据deltaX的正负来判断是向左滑动还是向右滑动,通过setCurrentItem()方法来更新当前页面的位置。 以上就是实现通讯录左右滑动翻页功能的步骤,需要注意的是,在实现手势监听器时,需要返回false,以确保ViewPager能够正常滑动

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值