自定义控件ScrollRelativeLayout

ScrollRelativeLayout


有些页面我们需要除了TitleBar以外的区域整体可以随着手指滑动而滑动,松手后整体又会以相应interpolator的效果复位。


这里写图片描述

内部的4个Item是RelativeLayout布局。

  • 问题点1: 当按下位置在内部的RelativeLayout上时滑动无效

  • 问题点2: 滑动整体过后界面的位置复原

先看下这个自定义的RelativeLayout

public class ScrollRelativeLayout extends RelativeLayout {
    private Context ct;
    private boolean isMove;

    public ScrollRelativeLayout(Context context) {
        super(context);
        ct = context;
        initView();
    }

    public ScrollRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        ct = context;
        initView();
    }

    public ScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ct = context;
        initView();
    }

    private void initView() {
        AnticipateInterpolator  interpolator = new AnticipateInterpolator();
        mScroller = new Scroller(ct, interpolator);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
            isMove = true;
        } else {
            isMove = false;
        }
        super.computeScroll();
    }


    /**
     * 点击时候Y的坐标
     */
    private int downY = 0;
    /**
     * 拖动时候Y的坐标
     */
    private int moveY = 0;
    /**
     * 松开时候Y的坐标
     */
    private int upY = 0;
    /**
     * 拖动时候Y的方向距离
     */
    private int scrollY = 0;

    private Scroller mScroller;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int offViewY = 0;//屏幕顶部和该布局顶部的距离

        if (!isMove) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                     System.out.println("down");
                    downY = (int) event.getRawY();
                    return true;
                case MotionEvent.ACTION_MOVE:
                                    System.out.println("move");
                    moveY = (int) event.getRawY();
                    scrollY = moveY - downY;
//                    System.out.println(scrollY);
                    if (scrollY > 0) {/**向下拖动*/
                        scrollTo(0, -scrollY / 2);
                    } else {/**向上拖动*/
                        scrollTo(0, -scrollY / 2);
                    }
                    break;
                case MotionEvent.ACTION_UP:
//                    System.out.println("up");
                    upY = (int) event.getRawY();
                    if (downY-upY > 0) {/**向上滑动*/
                        startAnimation(-scrollY/2, scrollY/2, 500);
                    } else/**向下滑动*/ {
                        startAnimation(-scrollY/2, scrollY/2, 500);
                    }
                    break;
            }
        }
        return super.onTouchEvent(event);
    }

    int down1Y ;
    int move1Y;
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {


        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                down1Y = (int) ev.getRawY();
                System.out.println("down1Y");
                break;
            case MotionEvent.ACTION_MOVE:
                move1Y = (int) ev.getRawY();
                System.out.println("move1");
                if (Math.abs(move1Y - down1Y) > 10) {
                    downY=down1Y;
                    return true;
                }
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    private void startAnimation(int startY, int dy, int duration) {
        isMove = true;
        mScroller.startScroll(0, startY, 0, dy, duration);
        invalidate();
    }
}

这里是布局文件的使用这个自定义布局

 <com.youarenotin.bzgj.UI.Widgets.ScrollRelativeLayout
        android:id="@+id/setting_scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/mine_setting1">

        <RelativeLayout
            android:id="@id/setting_common_info"
            android:layout_width="fill_parent"
            android:layout_height="50.0dip"
            android:layout_marginTop="30.0dip"
            android:background="#ffffffff">


            <TextView
                style="@style/TextStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="25.0dip"
                android:layout_marginTop="20.0dip"
                android:text="常用信息" />

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerInParent="true"
                android:layout_marginRight="10.0dip"
                android:src="@drawable/more"
                android:visibility="visible" />
        </RelativeLayout>

        <View
            android:layout_width="fill_parent"
            android:layout_height="0.100000024dip"
            android:layout_below="@id/setting_common_info"
            android:layout_marginLeft="20.0dip"
            android:background="@drawable/hengchang" />

        <RelativeLayout
            android:id="@id/setting_Anewmessage_info"
            android:layout_width="fill_parent"
            android:layout_height="50.0dip"
            android:layout_below="@id/setting_common_info"
            android:layout_marginTop="0.100000024dip"
            android:background="#ffffffff">

            <TextView
                style="@style/TextStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="25.0dip"
                android:layout_marginTop="20.0dip"
                android:text="新消息提示" />

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerInParent="true"
                android:layout_marginRight="10.0dip"
                android:src="@drawable/more"
                android:visibility="visible" />
        </RelativeLayout>

        <View
            android:layout_width="wrap_content"
            android:layout_height="0.100000024dip"
            android:layout_below="@id/setting_Anewmessage_info"
            android:layout_marginLeft="20.0dip"
            android:background="@drawable/hengchang" />

        <RelativeLayout
            android:id="@id/setting_pwd_security"
            android:layout_width="fill_parent"
            android:layout_height="50.0dip"
            android:layout_below="@id/setting_Anewmessage_info"
            android:layout_marginTop="0.100000024dip"
            android:background="#ffffffff">

            <TextView
                style="@style/TextStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="25.0dip"
                android:layout_marginTop="20.0dip"
                android:text="账户与安全" />
        </RelativeLayout>

        <View
            android:layout_width="wrap_content"
            android:layout_height="0.100000024dip"
            android:layout_below="@id/setting_pwd_security"
            android:layout_marginLeft="20.0dip"
            android:background="@drawable/hengchang" />

        <RelativeLayout
            android:id="@id/setting_about"
            android:layout_width="fill_parent"
            android:layout_height="50.0dip"
            android:clickable="false"
            android:focusable="false"
            android:layout_below="@id/setting_pwd_security"
            android:layout_marginTop="0.100000024dip"
            android:background="#ffffffff">

            <TextView
                style="@style/TextStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="25.0dip"
                android:layout_marginTop="20.0dip"
                android:text="关于XXXX" />

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerInParent="true"
                android:layout_marginRight="10.0dip"
                android:src="@drawable/more"
                android:visibility="visible" />

        </RelativeLayout>

        <View
            android:layout_width="wrap_content"
            android:layout_height="0.100000024dip"
            android:layout_below="@id/setting_about"
            android:background="@drawable/hengchang" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"
            android:layout_marginRight="10.0dip"
            android:src="@drawable/more"
            android:visibility="invisible" />
    </com.youarenotin.bzgj.UI.Widgets.ScrollRelativeLayout>

解决问题1: 这个问题涉及到触摸事件的拦截与消费
拦截事件 onInterceptTouchEvent(ev)这个方法是从触摸位置的顶层view向下层传递具体细节部分可参看Android-onInterceptTouchEvent()和onTouchEvent()总结

本例中的onInterceptTouchEvent

当点击非内部Item的区域时,直接由本类中的onTouchEvent来处理触摸事件。

这里写图片描述

这里返回true表示由本类来消费该次触摸事件,接下来的move和up都由本类onTouchEvent来处理。 getRawY()和getY()的区别是前者测量的是触点相对屏幕的距离,而后者测量的是相对于容器的位置坐标,因为滑动过程中我们需要让容器也跟着滑动所以不多说用getRawY()。

当点击内部Item时,由于我们在引用这个类时,为每个Item添加了onClickListener()的回调,所以onTouchEvent的事件传递被内部item消费了。股本类中onTouchEvent将收不到任何ev。但是当我们在点击的过程中稍微的滑动了一下这里面设置的阈值是10

if (Math.abs(move1Y - down1Y) > 10) {
                    downY=down1Y;
                    return true;
                }

当在点击内部item的同时抖动的距离超过10个像素的话,我们认为该操作是要滑动而不是点击事件,故return true; 拦截事件的向下传递 ,由本类onTouchEvent处理。滑动处理部分在 move这个事件里,细节可参考 scrollto的用法。

解决问题二 : 滑动过程中松手后恢复事件在onTouchEvent中的ACTION_UP中处理,这里需要借助一个帮助类scroller,需要注意的是

 private void startAnimation(int startY, int dy, int duration) {
        isMove = true;
        mScroller.startScroll(0, startY, 0, dy, duration);
        invalidate();
    }

中startY是相对于该空间最最原始的位置与移动过程无关并且它这个方向与我们常识中的坐标轴方向正好相反,好费解.
在invalidate()后会不断调用该view的computeScroll()方法

 @Override
    public void computeScroll() {
    //计算是否还有需要移动的距离
        if (mScroller.computeScrollOffset()) {//需要 继续用scroller是移动渲染界面
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
            isMove = true;
        } else {//不需要 ,移动完毕 
            isMove = false;
        }
        super.computeScroll();
    }

总结: 小细节问题基本上就这些。其中有几个关键技术点需要仔细看一些大手的文章和demo才能明白。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值