`

迷你轻量级全方向完美滑动处理侧滑控件SlideLayout

 
阅读更多

纯手工超级迷你轻量级全方向完美滑动处理侧滑控件(比官方 support v4 包 SlidingPaneLayout 控件更加 Q 迷你,累计代码不足 300 行),支持上下左右有各种侧拉,可配置侧拉松手临界距离,支持单独使用、ListView、GridView、RecycleView、ScrollView、ViewPager 等各种嵌套(作为 item 使用或者作为以上所有控件的父容器使用),具体不同配置展示效果如下图。

like SlidingPaneLayout, all direction support.
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * like SlidingPaneLayout, all direction support.
 */
public class SlideLayout extends ViewGroup {
    public static final int STATE_CLOSE = 0;
    public static final int STATE_SLIDING = 1;
    public static final int STATE_OPEN = 2;

    private static final int SLIDE_RIGHT = 0;
    private static final int SLIDE_LEFT = 1;
    private static final int SLIDE_TOP = 2;
    private static final int SLIDE_BOTTOM = 3;

    private View mContentView;
    private View mSlideView;

    private Scroller mScroller;

    private int mLastX = 0;
    private int mLastY = 0;

    private int mSlideCriticalValue = 0;
    private boolean mIsScrolling = false;
    private int mSlideDirection;

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

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

    public SlideLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SlideLayout);
        mSlideDirection = typedArray.getInt(R.styleable.SlideLayout_slideDirection, SLIDE_RIGHT);
        mSlideCriticalValue = typedArray.getDimensionPixelSize(R.styleable.SlideLayout_slideCriticalValue, 0);
        typedArray.recycle();

        mScroller = new Scroller(context);
    }

    public int getSlideState() {
        int retValue = STATE_CLOSE;
        if (mIsScrolling) {
            retValue = STATE_SLIDING;
        } else {
            int scrollOffset = (mSlideDirection == SLIDE_LEFT || mSlideDirection == SLIDE_RIGHT) ?
                                getScrollX() : getScrollY();
            retValue = (scrollOffset == 0) ? STATE_CLOSE : STATE_OPEN;
        }
        return retValue;
    }

    public void smoothCloseSlide() {
        smoothScrollTo(0, 0);
    }

    public void smoothOpenSlide() {
        switch (mSlideDirection) {
            case SLIDE_RIGHT:
                smoothScrollTo(mSlideView.getMeasuredWidth(), 0);
                break;
            case SLIDE_LEFT:
                smoothScrollTo(-mSlideView.getMeasuredWidth(), 0);
                break;
            case SLIDE_TOP:
                smoothScrollTo(0, -mSlideView.getMeasuredHeight());
                break;
            case SLIDE_BOTTOM:
                smoothScrollTo(0, mSlideView.getMeasuredHeight());
                break;
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalArgumentException("SlideLayout only need contains two child (content and slide).");
        }

        mContentView = getChildAt(0);
        mSlideView = getChildAt(1);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mContentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
        switch (mSlideDirection) {
            case SLIDE_LEFT:
                mSlideView.layout(-mSlideView.getMeasuredWidth(), 0, 0, getMeasuredHeight());
                break;
            case SLIDE_RIGHT:
                mSlideView.layout(getMeasuredWidth(), 0,
                        mSlideView.getMeasuredWidth() + getMeasuredWidth(), getMeasuredHeight());
                break;
            case SLIDE_TOP:
                mSlideView.layout(0, -mSlideView.getMeasuredHeight(), getMeasuredWidth(), 0);
                break;
            case SLIDE_BOTTOM:
                mSlideView.layout(0, getMeasuredHeight(),
                        getMeasuredWidth(), mSlideView.getMeasuredHeight() + getMeasuredHeight());
                break;
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return mIsScrolling || super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int eventX = (int) event.getX();
        int eventY = (int) event.getY();
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                mIsScrolling = false;
                //Maybe child not set OnClickListener, so ACTION_DOWN need to return true and use super.
                super.dispatchTouchEvent(event);
                return true;
            case MotionEvent.ACTION_MOVE:
                int offsetX = eventX - mLastX;
                int offsetY = eventY - mLastY;
                int directionMoveOffset = 0;
                if (mSlideDirection == SLIDE_LEFT || mSlideDirection == SLIDE_RIGHT) {
                    directionMoveOffset = Math.abs(offsetX) - Math.abs(offsetY);
                } else {
                    directionMoveOffset = Math.abs(offsetY) - Math.abs(offsetX);
                }
                if (!mIsScrolling && directionMoveOffset < ViewConfiguration.getTouchSlop()) {
                    break;
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                mIsScrolling = true;
                int newScrollX = 0;
                int newScrollY = 0;
                switch (mSlideDirection) {
                    case SLIDE_RIGHT:
                        newScrollX = scrollX - offsetX;
                        if (newScrollX < 0) {
                            newScrollX = 0;
                        } else if (newScrollX > mSlideView.getMeasuredWidth()) {
                            newScrollX = mSlideView.getMeasuredWidth();
                        }
                        break;
                    case SLIDE_LEFT:
                        newScrollX = scrollX - offsetX;
                        if (newScrollX < -mSlideView.getMeasuredWidth()) {
                            newScrollX = -mSlideView.getMeasuredWidth();
                        } else if (newScrollX > 0) {
                            newScrollX = 0;
                        }
                        break;
                    case SLIDE_TOP:
                        newScrollY = scrollY - offsetY;
                        if (newScrollY < -mSlideView.getMeasuredHeight()) {
                            newScrollY = -mSlideView.getMeasuredHeight();
                        } else if (newScrollY > 0) {
                            newScrollY = 0;
                        }
                        break;
                    case SLIDE_BOTTOM:
                        newScrollY = scrollY - offsetY;
                        if (newScrollY < 0) {
                            newScrollY = 0;
                        } else if (newScrollY > mSlideView.getMeasuredHeight()) {
                            newScrollY = mSlideView.getMeasuredHeight();
                        }
                        break;
                }
                scrollTo(newScrollX, newScrollY);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsScrolling = false;
                getParent().requestDisallowInterceptTouchEvent(false);
                int finalScrollX = 0;
                int finalScrollY = 0;
                switch (mSlideDirection) {
                    case SLIDE_RIGHT:
                        if (scrollX > getSlideCriticalValue()) {
                            finalScrollX = mSlideView.getMeasuredWidth();
                        }
                        break;
                    case SLIDE_LEFT:
                        if (scrollX < -getSlideCriticalValue()) {
                            finalScrollX = -mSlideView.getMeasuredWidth();
                        }
                        break;
                    case SLIDE_TOP:
                        if (scrollY < -getSlideCriticalValue()) {
                            finalScrollY = -mSlideView.getMeasuredHeight();
                        }
                        break;
                    case SLIDE_BOTTOM:
                        if (scrollY > getSlideCriticalValue()) {
                            finalScrollY = mSlideView.getMeasuredHeight();
                        }
                        break;
                }
                smoothScrollTo(finalScrollX, finalScrollY);
                break;
        }

        mLastX = eventX;
        mLastY = eventY;
        return super.dispatchTouchEvent(event);
    }

    //TODO  when mSlideCriticalValue != 0, slide critical need fix.
    private int getSlideCriticalValue() {
        if (mSlideDirection == SLIDE_LEFT || mSlideDirection == SLIDE_RIGHT) {
            if (mSlideCriticalValue == 0) {
                mSlideCriticalValue = mSlideView.getMeasuredWidth() / 2;
            }
        } else {
            if (mSlideCriticalValue == 0) {
                mSlideCriticalValue = mSlideView.getMeasuredHeight() / 2;
            }
        }
        return mSlideCriticalValue;
    }

    private void smoothScrollTo(int destX, int destY) {
        int scrollX = getScrollX();
        int deltaX = destX - scrollX;
        int scrollY = getScrollY();
        int deltaY = destY - scrollY;
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY,
                (int) (Math.abs(Math.sqrt(deltaX*deltaX + deltaY*deltaY)) * 3));
        postInvalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
}


自定义属性:
<declare-styleable name="SlideLayout">
        <attr name="slideDirection">
            <enum name="fromRight" value="0"/>
            <enum name="fromLeft" value="1"/>
            <enum name="fromTop" value="2"/>
            <enum name="fromBottom" value="3"/>
        </attr>

        <attr name="slideCriticalValue" format="dimension"/>
    </declare-styleable>


like SlidingPaneLayout, but this used to mini lib and only support right slide.
used to no support v4 import.

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
 * like SlidingPaneLayout, but this used to mini lib and only support right slide.
 * used to no support v4 import.
 */
public class MiniSlideRightLayout extends LinearLayout {
    public static final int STATE_CLOSE = 0;
    public static final int STATE_SLIDING = 1;
    public static final int STATE_OPEN = 2;

    private View mContentView;
    private View mSlideView;

    private Scroller mScroller;

    private int mLastX = 0;
    private int mLastY = 0;

    private int mSlideSensitiveWidth = 0;

    private boolean mIsScrolling = false;

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

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

    public MiniSlideRightLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        setOrientation(HORIZONTAL);
        mScroller = new Scroller(context);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalArgumentException("SlideLayout only need contains two child (content and slide).");
        }

        mContentView = getChildAt(0);
        mSlideView = getChildAt(1);
    }

    public int getSlideState() {
        int retValue = STATE_CLOSE;
        if (mIsScrolling) {
            retValue = STATE_SLIDING;
        } else {
            retValue = (getScrollX() == 0) ? STATE_CLOSE : STATE_OPEN;
        }
        return retValue;
    }

    public void smoothCloseSlide() {
        smoothScrollTo(0, 0);
    }

    public void smoothOpenSlide() {
        smoothScrollTo(mSlideView.getMeasuredWidth(), 0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return mIsScrolling || super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int eventX = (int) event.getX();
        int eventY = (int) event.getY();
        int scrollX = getScrollX();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                mIsScrolling = false;
                //Maybe child not set OnClickListener, so ACTION_DOWN need to return true and use super.
                super.dispatchTouchEvent(event);
                return true;
            case MotionEvent.ACTION_MOVE:
                int offsetX = eventX - mLastX;
                int offsetY = eventY - mLastY;
                if (Math.abs(offsetX) - Math.abs(offsetY) < 1) {
                    break;
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                mIsScrolling = true;
                int newScrollX = scrollX - offsetX;
                if (newScrollX < 0) {
                    newScrollX = 0;
                } else if (newScrollX > mSlideView.getMeasuredWidth()) {
                    newScrollX = mSlideView.getMeasuredWidth();
                }
                scrollTo(newScrollX, 0);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mIsScrolling = false;
                getParent().requestDisallowInterceptTouchEvent(false);
                int finalScrollX = 0;
                mSlideSensitiveWidth = mSlideView.getMeasuredWidth() / 2;
                if (scrollX > mSlideSensitiveWidth) {
                    finalScrollX = mSlideView.getMeasuredWidth();
                }
                smoothScrollTo(finalScrollX, 0);
                break;
        }

        mLastX = eventX;
        mLastY = eventY;
        return super.dispatchTouchEvent(event);
    }

    private void smoothScrollTo(int destX, int destY) {
        int scrollX = getScrollX();
        int deltaX = destX - scrollX;
        int scrollY = getScrollY();
        int deltaY = destY - scrollY;
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY,
                (int) (Math.abs(Math.sqrt(deltaX*deltaX + deltaY*deltaY)) * 3));
        postInvalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }
}


https://github.com/yanbober/SlideLayout
分享到:
评论

相关推荐

    链式事件流轻量级迷你JS库hubJS

    hubJS是一个专为JavaScript开发者设计的轻量级迷你库,它的核心功能是简化事件处理流程,让开发者能够方便地管理自定义发布者、DOM元素、Fetch请求、WebSocket以及socket io等多种来源的事件流。hubJS的设计理念在于...

    迷你IIS迷你IIS迷你IIS

    迷你IIS,全称为“MiniIIS”,是一个小型的、轻量级的Web服务器软件,设计用于替代传统的Internet Information Services(IIS)。IIS是由微软公司开发的一款强大的Web服务器,广泛应用于Windows操作系统环境中,用于...

    一个轻量级的机器学习框架(纯python+numpy实现的迷你版scikit-learn).zip

    标题中的“一个轻量级的机器学习框架(纯python+numpy实现的迷你版scikit-learn)”指的是一项利用Python编程语言和Numpy库构建的简易机器学习库。这个库可能是为了教学目的或者对小型数据集进行快速实验而设计的,...

    Android代码-一个可配置的迷你版轻量级Label辅助类,支持多种配置效果。

    一个可配置的迷你版轻量级 Label 辅助类,支持多种配置效果,具体不同配置展示效果如下图。 说明文档 如下是关于 Label View 的相关使用方式、属性说明、拓展自定义的解释说明。 使用样例 已实现类说明 类别 ...

    SparkLineLayout,用于绘制迷你图/图形的简单而轻量级的库。支持标记和渐变。.zip

    SparkLineLayout是一个开源项目,专为Android平台设计,旨在提供一个简单且轻量级的库,用于绘制迷你图(也称为微图表或微型图形)。这种类型的图表通常在空间有限或者需要快速概览数据变化的情况下非常有用。...

    minigui控件处理细节

    minigui控件处理细节是指在minigui环境下对控件的各种处理操作,包括控件的隐藏、控件之间的切换、改变和移动窗口大小、控件前景色和背景色的设置、控件字体的设置、控件的透明处理等。 控件的隐藏是指在窗口中隐藏...

    foobar2000+迷你歌词(经典版本完美组合)

    总结来说,“foobar2000+迷你歌词”的经典组合以其轻量级的特性、强大的音频处理能力以及无缝的歌词体验,满足了众多音乐爱好者的期待。它们不仅提供了高质量的音乐播放,还注重用户体验,使用户能够沉浸在音乐的...

    轻量级嵌入式MQDataCarrier.zip

    DataCarrierDataCarrier 是轻量级,嵌入式,高流通量,发布订阅模式的 MQ.特点订阅式 MQ. 支持多商家与多用户。轻量级与嵌入式,jdk1.6.中的迷你Java库。高流通量, 用于 Sky-Walking APM。产生异步数据。使用简单...

    迷你SQL2000_v1.29.zip

    迷你SQL2000_v1.29.zip是一款专为简单工作环境设计的轻量级SQL Server版本。作为一款绿色版数据库系统,它无需安装,用户可以直接运行,大大简化了部署流程,尤其适合那些需要快速启动数据库服务或者对系统资源有...

    miniXmlParser C语言实现的轻量级的xml配置文档解析器

    迷你XML解析器(miniXmlParser)是一个用C语言编写的轻量级XML配置文档解析工具。这个解析器设计的目标是简化XML数据的处理,尤其适用于嵌入式系统或资源有限的环境,它提供了高效的内存管理和简洁的API接口。在本文...

    迷你的滑动窗口CSS模板_窄 jquery 滑动 灰色 按钮 图标.zip

    在这个特定的压缩包中,"迷你的滑动窗口CSS模板_窄 jquery 滑动 灰色 按钮 图标.zip" 提供了一个轻量级且适应窄屏幕的设计,结合了jQuery库来实现动态滑动效果。 **jQuery** 是一个广泛使用的JavaScript库,简化了...

    FreeRTOS是一个轻量级的实时操作系统内核.docx

    FreeRTOS是一个轻量级的实时操作系统(RTOS)内核,专为嵌入式系统设计。它由Richard Barry在2003年开发,并由亚马逊的FreeRTOS项目(一个由Amazon Web Services (AWS) 支持的开源项目)进一步推动和发展。以下是对...

    迷你的滑动窗口CSS模板-窄 jquery 滑动 灰色 按钮 图标.rar

    jQuery是一个轻量级的JavaScript库,它简化了HTML文档遍历、事件处理、动画和Ajax交互。在这个模板中,jQuery被用来实现滑动效果,使得用户可以轻松地打开和关闭窗口,或者在不同的内容之间滑动切换,提高了页面的可...

    miniweb一个小巧轻量级http服务器

    一个小巧轻量级http服务器,可以用于局域网文件共享,绿色免安装,还有一个很好的用途就是为路由器刷机提供SFTP、FTP替代。对没有内置SFTP、FTP支持的路由器,你可以用电脑上的miniweb和路由器上的wget传输文件

    简单迷你图片滑动展示网站模板下载-简单 迷你 图片 相册 滚动 web简历 窄 滑动 展示 花 作品 案例 工作室.rar

    该压缩包文件包含一个名为"简单迷你图片滑动展示网站模板下载_简单 迷你 图片 相册 滚动 web简历 窄 滑动 展示 花 作品 案例 工作室"的网站模板,适合用于创建一个简洁而精致的在线图片展示平台。这个模板特别适用于...

    简单实用的滑动单页HTML模板-黄色 迷你 窄 滑动 简历 html 设计 个人.rar

    Markdown是一种轻量级的标记语言,用于编写易于阅读和编写的纯文本格式,通常用于项目文档或快速记录信息。用户应该首先查看这个文件,了解如何安装和自定义模板。 而“简单实用的滑动单页HTML模板_黄色 迷你 窄 ...

    ComponentOne迷你图控件,进行可视化数据趋势分析

    迷你图 —— Sparklines是迷你的轻量级图表,有助于快速可视化数据。 它们是由数据可视化传奇人物Edward Tufte发明的,他将其描述为“数据密集,设计简单,字节大小的图形。”虽然迷你图不包含传统图表中的许多元素...

    MATLAB迷你便携版-5.78MB

    总的来说,MATLAB迷你便携版为那些需要轻量级计算解决方案的用户提供了便利,无论是在学术研究还是工程实践中,都能找到它的用武之地。然而,对于需要更复杂功能的专业用户,可能还需要考虑使用官方完整版MATLAB。在...

Global site tag (gtag.js) - Google Analytics