高仿QQ消息,可以下拉刷新带小红点

最近在做即时通讯这一块,老板不知道怎么想的,反正完全是照着QQ做的,而我个人又非常喜欢qq侧滑操作这个效果,于是想找来用,但是翻遍了网络,没有一个像样的例子,有相似的也是各种bug各种限制,我也崩溃啊,没办法只好自己搞一个了。不多说上效果图:

高仿QQ消息界面功能

具体实现呢,是自定义的listview来做的,本来的思路是自定义item的根布局来做,结果出来之后,事件的分发处理,特别不好弄,各种不流畅。

于是改用自定义listview,结果很喜人,个人觉得比较完美,支持3.0以下,
由于是属性动画需要支持到3.0以下,所以使用了nineoldandroid这个开源的属性动画库。

下面看代码:

public class InfoListView extends ListView {
    private int mScreenWidth;    // 屏幕宽度
    private float mDownX;            // 按下点的x值
    private float mDownY;            // 按下点的y值
    private int mActionViewWidth;// 操作view的宽度

    /**
     * 执行动画的时间
     */
    protected long mAnimationTime = 150;

    private boolean isActionViewShow = false;    // 删除按钮是否正在显示

    private ViewGroup mPointChild;    // 当前处理的item
    private LinearLayout.LayoutParams mLayoutParams;    // 当前处理的item的LayoutParams

    private int touchSlop;//最小偏移量超过这个值才处理滑动事件

    private float scroll;//偏移的距离

    private int openedIntemPosition = -1;//记录已经打开的item的位置

    private int childPosition;//手指落下位置的item的position

    private boolean iswiping = false;//手指是否正在滑动

    private boolean isDownToNormal = false;//判断之前是否是手指落下导致消失。

    private boolean isUPToNormal = false;//判断之前是否是手指离开导致消失。

    private long lastTime;//计算手指两次的触摸间隔

    public InfoListView(Context context) {
        this(context, null);
    }

    public InfoListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public InfoListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // 获取屏幕宽度
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        mScreenWidth = dm.widthPixels;
        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
        touchSlop = configuration.getScaledTouchSlop();

    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                performActionDown(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                return performActionMove(ev);
            case MotionEvent.ACTION_UP:
                performActionUp(ev);
                break;
        }


        return super.onTouchEvent(ev);

    }

    // 处理action_down事件
    private void performActionDown(MotionEvent ev) {


        //如果触摸间隔小于100MS 不让进来~
        if (System.currentTimeMillis() - lastTime <= mAnimationTime) {

            lastTime = System.currentTimeMillis();
            return;
        }

        lastTime = System.currentTimeMillis();

        mDownX = ev.getX();
        mDownY = ev.getY();

        if (isActionViewShow) {

            childPosition = pointToPosition((int) mDownX, (int) mDownY);

            if (childPosition == AdapterView.INVALID_POSITION) {

                return;

            }
            if ((openedIntemPosition != childPosition - getFirstVisiblePosition())) {

                //全部恢复初始值不作响应
                turnToNormal();
                isDownToNormal = true;
                mDownX = -1;
                mDownY = -1;
                return;
            }

        }

        if ((!isActionViewShow && openedIntemPosition == -1)) {
            // 获取当前点的item
            childPosition = pointToPosition((int) mDownX, (int) mDownY);

            if (childPosition == AdapterView.INVALID_POSITION) {

                return;

            }

            openedIntemPosition = childPosition - getFirstVisiblePosition();

            mPointChild = (ViewGroup) getChildAt(openedIntemPosition);
            // 获取操作view宽度
            mActionViewWidth = mPointChild.getChildAt(1).getLayoutParams().width;


            mLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0)
                    .getLayoutParams();
            // 为什么要重新设置layout_width 等于屏幕宽度
            // 因为match_parent时,不管你怎么滑,都不会显示删除按钮
            // why? 因为match_parent时,ViewGroup就不去布局剩下的view
            mLayoutParams.width = mScreenWidth;
            mPointChild.getChildAt(0).setLayoutParams(mLayoutParams);

        }

    }

    // 处理action_move事件
    private boolean performActionMove(MotionEvent ev) {


        if ((Math.abs(ev.getX() - mDownX) > touchSlop
                && Math.abs(ev.getY() - mDownY) < touchSlop)
                || (Math.abs(ev.getX() - mDownX) > touchSlop
                && Math.abs(ev.getY() - mDownY) > touchSlop
                && (Math.abs(ev.getX() - mDownX) > Math.abs(ev.getY() - mDownY)))) {


            iswiping = true;

            //当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生
            MotionEvent cancelEvent = MotionEvent.obtain(ev);
            cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
                    (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
            onTouchEvent(cancelEvent);

        }

        if (isActionViewShow) {

            childPosition = pointToPosition((int) mDownX, (int) mDownY);

            if (childPosition == AdapterView.INVALID_POSITION) {

                return true;

            }
            if ((openedIntemPosition != childPosition - getFirstVisiblePosition())) {

                //全部恢复初始值不作响应
                turnToNormal();
                isDownToNormal = true;
                mDownX = -1;
                mDownY = -1;
                iswiping = false;
                return true;
            }

        }


        if (iswiping) {

            scroll = ev.getX() - mDownX;

            if (Math.abs(scroll) <= mActionViewWidth) {

                if ((scroll <= 0 && !isActionViewShow)) {


                    ViewHelper.setTranslationX(mPointChild.getChildAt(0), scroll);

                    ViewHelper.setTranslationX(mPointChild.getChildAt(1), scroll);


                } else if ((scroll >= 0 && isActionViewShow && openedIntemPosition != -1)) {

                    ViewHelper.setTranslationX(mPointChild.getChildAt(0), (scroll - mActionViewWidth));
                    ViewHelper.setTranslationX(mPointChild.getChildAt(1), (scroll - mActionViewWidth));


                }

            }

            return true;
        }

        return super.onTouchEvent(ev);

    }


    // 处理action_up事件
    private void performActionUp(MotionEvent ev) {


        if (!iswiping) {

            if (isActionViewShow && openedIntemPosition != -1) {

                isUPToNormal = true;
                turnToNormal();
            } else {

                turnToNormal();
            }


            return;
        }


        scroll = ev.getX() - mDownX;
        // 偏移量大于操作view的一半,则显示
        // 否则恢复默认
        //向左滑动


        if (scroll < 0) {


            if ((-scroll >= mActionViewWidth / 4) && !isActionViewShow) {

                show();

            } else if ((-scroll < mActionViewWidth / 4) || ((-scroll >= mActionViewWidth / 4) && isActionViewShow)) {

                if (isActionViewShow) {

                    childPosition = pointToPosition((int) mDownX, (int) mDownY);

                    if (childPosition == AdapterView.INVALID_POSITION) {

                        return;

                    }
                    if (openedIntemPosition == childPosition - getFirstVisiblePosition()) {

                        return;
                    }

                }

                turnToNormal();

            }

        } else if (scroll > 0) {//向右滑动


            if (scroll < mActionViewWidth / 4 && isActionViewShow && openedIntemPosition != -1) {

                show();

            } else if (scroll >= mActionViewWidth / 4 && isActionViewShow && openedIntemPosition != -1) {

                turnToNormal();

            } else {

                //如果没有展开的其它情况全部恢复原样
                turnToNormal();

            }

        }


        iswiping = false;


    }


    @Override
    public boolean performItemClick(View view, int position, long id) {

        //当有展开的item的时候,不响应item的点击事件
        if (isActionViewShow || isDownToNormal || isUPToNormal) {

            isDownToNormal = false;
            isUPToNormal = false;
            if (isActionViewShow && openedIntemPosition != -1) {

                turnToNormal();
            }
            return false;
        }
        return super.performItemClick(view, position, id);
    }


    /**
     * 隐藏操作view
     */
    public void turnToNormal() {

        if (mPointChild == null) {

            return;
        }
        isActionViewShow = false;
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mPointChild.getChildAt(0), "translationX", 0);

        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(mPointChild.getChildAt(1),
                "translationX", mActionViewWidth);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(objectAnimator, objectAnimator1);
        animatorSet.setDuration(mAnimationTime);
        animatorSet.start();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {

                openedIntemPosition = -1;

            }
        });

    }

    /**
     * 显示操作view
     */
    private void show() {

        if (mPointChild == null) {

            return;
        }

        isActionViewShow = true;
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mPointChild.getChildAt(0), "translationX",
                -mActionViewWidth);

        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(mPointChild.getChildAt(1), "translationX",
                -mActionViewWidth);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(objectAnimator, objectAnimator1);
        animatorSet.setDuration(mAnimationTime);
        animatorSet.start();
        
    }

    /**
     * 是否显示
     * @return
     */
    public boolean isActionViewShow() {
        return isActionViewShow;
    }

}

这里面需要注意的是item的布局:

<?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:background="@android:color/white"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <!--初显示界面-->
    <LinearLayout
        android:id="@+id/id_front"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:paddingBottom="10dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="10dp"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/infoImage"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_centerVertical="true"
            android:scaleType="centerCrop"
            android:src="@android:drawable/star_on" />

        <LinearLayout
            android:layout_weight="3.0"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:layout_toLeftOf="@+id/infoTimeAndCount"
            android:layout_toRightOf="@+id/infoImage"
            android:gravity="center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/infoTitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Jay" />

            <TextView
                android:textSize="13sp"
                android:id="@+id/infoLast"
                android:textColor="@android:color/darker_gray"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="什么事啊??" />

        </LinearLayout>

        <LinearLayout
            android:layout_weight="1.0"
            android:id="@+id/infoTimeAndCount"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:gravity="center_vertical|right"
            android:orientation="vertical">

            <TextView
                android:id="@+id/infoTime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Jay" />

            <TextView

                android:id="@+id/infoCount"
                android:textSize="13sp"
                android:textColor="@android:color/white"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:gravity="center"
                android:text="99+"

                />

        </LinearLayout>
    </LinearLayout>

    <!--滑动显示界面-->
    <LinearLayout
        android:id="@+id/id_back"
        android:layout_width="160dp"
        android:layout_height="70dp"
        android:background="@android:color/white"
        android:gravity="center|right"
        android:tag="id_back">

        <TextView
            android:id="@+id/id_putTop"
            android:textSize="19sp"
            android:textColor="@android:color/white"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:background="@android:color/darker_gray"
            android:clickable="true"
            android:gravity="center"
            android:text="置顶" />

        <TextView
            android:id="@+id/id_delete"
            android:textSize="19sp"
            android:textColor="@android:color/white"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:background="@color/red"
            android:clickable="true"
            android:gravity="center"
            android:text="删除" />

    </LinearLayout>

</LinearLayout>

以上就是关键代码了,大家可以直接拿来用,附上源码下载地址:~
完美仿QQ消息界面功能

程序员内功修炼手册 不定期分享程序员基础知识,大前端知识!想跟博主一块成长的快快关注吧!

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值