Android 横向带有吸附效果的横向拖动控件(效果同纵向下拉刷新ListView)

原创 2015年03月29日 21:35:06

先上一张效果图



中间的横向拖动就是我们要做的效果。

一、实现思路

仔细观察不难发现,该拖动view与listview的下拉刷新的效果很类似,手指拖动的时候显示隐藏的view,手指放开自动回弹。只不过区别就是一个横向一个纵向
下拉刷新的实现思路如下:
自定义一个布局继承自LinearLayout,然后在这个布局中加入下拉头和ListView这两个子元素,并让这两个子元素纵向排列。初始化的时候,让下拉头向上偏移出屏幕,这样我们看到的就只有ListView了。然后对ListView的touch事件进行监听,如果当前ListView已经滚动到顶部并且手指还在向下拉的话,那就将下拉头显示出来,松手后进行刷新操作,并将下拉头隐藏。原理如图所示:

我们要实现的效果跟下拉刷新基本一样,只不过是方向是横向而已。如图

二、具体实现

首先我们要先自定义一个LinearLayout,然后在布局中加入隐藏头和剩余部分
布局文件如下:
        <com.boohee.myview.HorizontalDragLinearLayout
            android:id="@+id/main_hori_scroll_ll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/white"
            android:baselineAligned="false"
            android:clickable="true"
            android:descendantFocusability="blocksDescendants"
            android:orientation="horizontal">

            <!--添加一层处理左右拖动时的移动-->
            <LinearLayout
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                >

              ...........<pre name="code" class="java"><span style="white-space:pre">		</span><!--此处添加 剩余显示的部分-->
</LinearLayout> </com.boohee.myview.HorizontalDragLinearLayout>

自定义的LinearLayout,初始化部分:
    private Context context;
    //需要隐藏的view
    private TextView headerView;
    /**
     * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
     */
    private boolean loadOnce;
    /**
     * 需要隐藏View的布局参数
     */
    private MarginLayoutParams headerLayoutParams;

    /**
     * 隐藏view的宽度
     */
    private int hideHeaderWidth;
    private float xDown;
    /**
     * 每次move的x坐标
     */
    private float tmpXMove;

    private float mFocusX = 0.f;
    /**
     * 在被判定为滚动之前用户手指可以移动的最大值。
     */
    private int touchSlop;
    /**
     * 头部回滚的速度
     */
    public static final int SCROLL_SPEED = -10;

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

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

    public HorizontalDragLinearLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setOrientation(HORIZONTAL);
        this.context = context;
        init();
    }

    private void init() {
        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        headerView = new TextView(context);
        LinearLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        params.gravity = Gravity.CENTER_VERTICAL;
        headerView.setLayoutParams(params);
        headerView.setTextColor(Color.BLACK);
        headerView.setGravity(Gravity.CENTER);
        this.addView(headerView, 0);
    }

    public void setHeaderText(String string){
        if (headerView != null){
            headerView.setText(string);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (headerView != null && changed && !loadOnce){
            hideHeaderWidth = - headerView.getWidth();
            headerLayoutParams = (MarginLayoutParams) headerView.getLayoutParams();
            headerLayoutParams.leftMargin = hideHeaderWidth;
            headerView.setLayoutParams(headerLayoutParams);
            loadOnce = true;
        }
    }

初始化的时候动态添加了一个TextView,也就是我们需要隐藏的部分
在第一次onLayout布局的时候,我们根据隐藏头的宽度 调整该隐藏头的margin,使其隐藏。

然后我们需要进行拖动识别,重写onTouchEvent 
    @Override
    public boolean onTouchEvent(MotionEvent motionEvent) {
        //禁止父容器拦截事件
        getParent().requestDisallowInterceptTouchEvent(true);
        switch (motionEvent.getAction()){
            case MotionEvent.ACTION_DOWN:
                xDown = motionEvent.getRawX();
                return true;
            case MotionEvent.ACTION_MOVE:
                float xMove = motionEvent.getRawX();
                int distance = (int) (xMove - xDown);
                if (distance <= 0 && headerLayoutParams.leftMargin <= hideHeaderWidth) {
                    return false;
                }
                if (distance < touchSlop) {
                    return false;
                }
                //大于头部宽度不做处理
                if (distance >= -hideHeaderWidth){
                    return true;
                }
                headerLayoutParams.leftMargin = distance + hideHeaderWidth;
                headerView.setLayoutParams(headerLayoutParams);
                tmpXMove = xMove;
                return true;
            case MotionEvent.ACTION_UP:
                //松手时调用隐藏头部
                new HideHeaderTask().execute();
                break;
            default:
                break;
        }
        return true;
    }

由于外层是用的viewPager+fragment进行页面切换,所以默认情况下touch事件是被viewpager消费掉的。我们要组织父控件拦截事件
        getParent().requestDisallowInterceptTouchEvent(true);
其中涉及Android 的touch 分发,可以参考鸿洋大大的这篇博客点击打开链接
在move的时候得到移动的距离,然后对隐藏头设置margin,使其及该LinearLayout的其他子view跟随隐藏头一起向右移动,达到我们想要的效果

up的时候启动一个AsyncTask 动态改变隐藏头的margin,去恢复到最初始的状态,即隐藏头隐藏的状态。

    /**
     * 隐藏头部
     */
    class HideHeaderTask extends AsyncTask<Void, Integer, Integer> {

        @Override
        protected Integer doInBackground(Void... params) {
            int leftMargin = headerLayoutParams.leftMargin;
            while (true) {
                leftMargin = leftMargin + SCROLL_SPEED;
                if (leftMargin <= hideHeaderWidth) {
                    leftMargin = hideHeaderWidth;
                    break;
                }
                publishProgress(leftMargin);
                sleep(10);
            }
            return leftMargin;
        }

        @Override
        protected void onProgressUpdate(Integer... leftMargin) {
            headerLayoutParams.leftMargin = leftMargin[0];
            headerView.setLayoutParams(headerLayoutParams);
        }

        @Override
        protected void onPostExecute(Integer leftMargin) {
            headerLayoutParams.leftMargin = leftMargin;
            headerView.setLayoutParams(headerLayoutParams);
        }
    }

    private void sleep(int i) {
        try {
            Thread.sleep(i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

搞定。

另外 效果中带悬浮进度的progressBar 连接在这里
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

横向滑动加载更多的控件的实现

概述: 公司业务要求做一个横向滑动的,可以加载更多的控件。参考了郭霖大神关于下拉刷新的博客,地址为:http://blog.csdn.net/guolin_blog/arti...

Android -- RecyclerView实现顶部吸附效果

1,先来看一下今天实现的效果: 2,这次的效果是使用ItemDecoration来实践的,来看一看我们的实现吧   第一步:首先添加依赖,由于我们这些数据是请求网络的,所以一下我们添加网络框架...

Android自定义Viewgroup切换View带有吸附效果

Android自定义Viewgroup切换View带有吸附效果,防止OOM

Android 开源之StickyHeaderListView 标题渐变、吸附悬停、筛选分类、动态头部

StickyHeaderListView 是基于实际需求做出的灵活可定制的UI功能,具体实现功能如下: 一、支持无限循环的广告位。 二、高度可动态配置的Header2和Header3(使用Grid...

Android -- RecyclerView实现顶部吸附效果

1,先来看一下今天实现的效果: 2,这次的效果是使用ItemDecoration来实践的,来看一看我们的实现吧   第一步:首先添加依赖,由于我们这些数据是请求网络的,所以一下我们添加网络框架...

android 超简单的拖动按钮 悬浮按钮 吸附按钮

android 超简单的拖动按钮 悬浮按钮 吸附按钮

页面向下滚动时导航顶部吸附效果

页面向下滚动时导航顶部吸附效果,这是无论在Web端还是移动端都有一种效果。 页面顶部是宣传图片,图片下方是导航菜单,当浏览器窗口滚动条向下拉动或鼠标滚轮向下滚动时(移动端为手指向上滑动屏幕),一...

Android 吸入动画效果实现分解

Android 吸入动画效果详解 .    这里,我要介绍的是如何在Android上面实现一个类似的效果。先看看我实现的效果图。    上图演示了动画的某几帧,其中从1 - 4,演示了图片从原...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)