自定义View之QQ侧滑删除

2017-12-19  本文已影响0人  Noblel

作者:Noblel
链接:http://www.jianshu.com/p/b81cae309594
请尊重原创,谢谢!

需求来源:
这是某一个版本的,现在的版本不是这样的了。

QQ侧滑

实现思路

1.分析元素和效果:
有两块布局,一个是内容,一个是菜单布局,可以左滑而且根据位置自动关闭和打开。

2.自定义属性:
为了简单,这里我就没有写自定义属性,套路都一样。不过思路上要记得这一步。

3.编写SlideView

3.1. 选择合适的布局,这里直接extends最简单的FrameLayout

3.2. 定义变量和初始化布局

原理图
/**
 * 内容视图
 */
private View mContentView;
/**
 * 菜单视图
 */
private View mMenuView;

/**
 * 布局文件加载完成回调的方法
 */
@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    mContentView = getChildAt(0);
    mMenuView = getChildAt(1);
}

item_main.xml

<?xml version="1.0" encoding="utf-8"?>
<com.noblel.slideview.SlideLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <include
        android:id="@+id/item_content"
        layout="@layout/item_content" />

    <include
        android:id="@+id/item_menu"
        layout="@layout/item_menu" />
</com.noblel.slideview.SlideLayout>

item_content我们直接放一个TextView,menu中放三个TextView。

item_content.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:background="#ffffff"
    android:gravity="center"
    android:textColor="#000000"
    android:textSize="25sp"/>

item_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="300dp"
    android:background="#ffffff"
    android:layout_height="60dp">

    <TextView
        android:id="@+id/tv_top"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:background="#C7C7CD"
        android:gravity="center"
        android:padding="5dp"
        android:text="置顶"
        android:textColor="#ffffff"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/tv_no_read"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:background="#FF9D01"
        android:gravity="center"
        android:padding="5dp"
        android:text="标为未读"
        android:textColor="#ffffff"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/tv_delete"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:background="#FF3A30"
        android:gravity="center"
        android:padding="5dp"
        android:text="删除"
        android:textColor="#ffffff"
        android:textSize="25sp" />
</LinearLayout>

3.3. 有两部分视图那么我们需要在onLayout中给他们安排座位。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //获取内容的宽度
    mContentWidth = mContentView.getMeasuredWidth();
    //获取菜单视图的宽度
    mMenuWidth = mMenuView.getMeasuredWidth();
    //获取内容高度
    mContentHeight = mContentView.getMeasuredHeight();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    //指定菜单位置
    mMenuView.layout(mContentWidth, 0, mContentWidth + mMenuWidth, mContentHeight);
}

3.4. 设置侧滑系列操作

3.5 设置相关接口

/**
 * 监听slideLayout状态的改变
 */
public interface OnStateChangeListener {
    void onClose(SlideLayout layout);

    void onDown(SlideLayout layout);

    void onOpen(SlideLayout layout);
}

效果:

效果

源码

QQ侧滑删除源码

问题思考:

  1. ListView多点触控会出现多个item同时打开菜单
    解决办法: 设置android:splitMotionEvents="false"

  2. 获取mContentWidth在onLayout的mMenuView.layout()之后会有一个item显示不出来
    原因: ListView的复用机制中会多次调用onLayout方法,而在mMenuView.layout()之后值都为0,所以显示不出来。具体需要了解ListView复用机制。

getScrollX()和getX()和getRawX()区别?

getX():触摸点相对于其所在视图原点在x轴上的偏移量
getScrollX():当前视图相对于屏幕原点在x轴上的偏移量,就是实际视图(包括未显示出来的)距离屏幕左边缘的距离。如果在右边则为负数,在左边则为正数。
getRawX():触摸点相对于屏幕原点在x轴上的偏移量,即getRawX() = getX() + getScrollX()

scrollTo和scrollBy()区别?

public void scrollBy(int x, int y) {
    //scrollBy就是调用scrollTo()
    scrollTo(mScrollX + x, mScrollY + y);
}

public void scrollTo(int x, int y) {
    if (mScrollX != x || mScrollY != y) {
        int oldX = mScrollX;
        int oldY = mScrollY;
        mScrollX = x;
        mScrollY = y;
        invalidateParentCaches();
        onScrollChanged(mScrollX, mScrollY, oldX, oldY);
        if (!awakenScrollBars()) {
            postInvalidateOnAnimation();
        }
    }
}

scrollTo():在当前视图内容偏移至(x , y)坐标处,即显示(可视)区域位于(x , y)坐标处。
scrollBy(): 在当前视图内容继续偏移(x , y)个单位,显示(可视)区域也跟着偏移(x,y)个单位。

computeScroll()是做什么的?

computeScrollOffset()方法会计算出下一个滚动到的位置,通过getCurrX(),getCurrY()这两个方法获得,在没滚到我们设定距离前返回是true,到了我们设定的距离后就返回false,表示计算完成了,滚动也这个时候完成。起始偏移量和移动距离和时间通过startscroll()设置的。他只是个设置的,并会自动绘制,要绘制就通常我们调用viewgroup的invalidate()等相关方法来重绘制。然后viewgroup会调用computeScroll()这个空方法,在这里面不断循环判断computeScrollOffset()和移动scrollTo()来达到我们自动滚动到目标距离。

上一篇下一篇

猜你喜欢

热点阅读