首页 > 其他分享 >迷你轻量级全方向完美滑动处理侧滑控件SlideLayout

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

时间:2023-05-01 14:31:49浏览次数:46  
标签:case 控件 int private 全方向 SLIDE mSlideView public 轻量级



纯手工超级迷你轻量级全方向完美滑动处理侧滑控件(比官方 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


标签:case,控件,int,private,全方向,SLIDE,mSlideView,public,轻量级
From: https://blog.51cto.com/u_5454003/6238910

相关文章

  • MFC-SetImageList给列表视图控件设置图像列表
     CImageList*pImageList;HBITMAPhbmp1;CBitmap*pBitmap1;HBITMAPhbmp2;CBitmap*pBitmap2;HBITMAPhbmp3;CBitmap*pBitmap3;HBITMAPhbmp4;CBitmap*pBitmap4; pImageList=newCImageList();//创建一个CImageList类的指针变量pImageList->Cr......
  • Leangoo领歌轻量级协作-OKR目标管理
    ​本场景是OKR目标管理模板,用Leangoo管理和跟踪OKR可以提升OKR的透明度和传递的即时性,驱动团队的积极性、促进共享和协作、提升沟通和协作的效率,帮助企业快速落地OKR。OKR(ObjectivesandKeyResults目标与关键结果)是关于目标管理的一种最佳实践,是企业实践的管理理念与经验的总结......
  • C#使用委托在Socket Udp端口侦听线程内更新主窗口控件显示
    c#开启线程侦听SocketUDP端口,端口接收到网络读卡器的读卡数据后刷新UI界面显示接收数据,解析数据包信息并向读卡器发送显示文字、驱动读卡器播报语音、蜂鸣响声提示、开启继电器开关等操作。  .net提示通过设置:CheckForIllegalCrossThreadCalls=false,可以在子线程内强制更新......
  • Ant Design - 组件之 Tree树形控件
    AntDesign-组件之Tree树形控件针对tree树形组件封装了一个树形组件1.组件ui 2.组件名称ThemeCatalog 上面是image目录中的svg3.组件代码index.jsimportReact,{useEffect,useState}from'react';importPropTypesfrom'prop-types';importIcon,{Folde......
  • 界面控件DevExpress WinForm的垂直网格,让数据展示更灵活(二)
    DevExpressWinForm VerticalGrid(垂直网格)组件设计用于提供UI灵活性,它允许显示数据集中的单个行,或在其90度反向网格容器中显示多个数据集行。此外,开发者还可以将其用作属性网格,就像在VisualStudioIDE中找到的那样。PS:DevExpressWinForm拥有180+组件和UI库,能为WindowsForms......
  • Scrum看板工具Leangoo轻量级协作使用场景
    ​国内目前有很多看板工具,我也一直在探索的路上,试用多种工具下来,我个人还是比较推荐leangoo领歌,看板式的管理方式,列表、泳道的多维度,直观透明的特点来呈现敏捷团队的进展,促进团队高效协作。通过看板共享和实时同步团队工作以实现高效协同,团队工作体现为任务卡片,而卡片上的内容......
  • js javascript js隐藏页面上有id的控件,隐藏页面上无控件包含的文字,控制页面控件属性
    1.隐藏页面上有id的控件varinput=document.getElementsByTagName("input");//获取页面所有inputfor(vari=0;i<input.length;i++){if(input.item(i).id.indexOf("txt")>=0)//判断input的id中是否包含txt字符串{......
  • MFC-Create动态创建列表视图控件
     BOOLbb=mylist.Create(LVS_SMALLICON|WS_DLGFRAME,rect,this,10001);//动态创建列表视图控件/*参数1:DWORDdwStyle列表视图控件的风格标准样式:LVS_ALIGNLEFT显示格式是大图标或小图标时,标签......
  • 界面控件DevExpress Blazor UI v22.2 - 支持.NET 7
    DevExpress拥有.NET开发需要的所有平台控件,包含600多个UI控件、报表平台、DevExpressDashboardeXpressApp框架、适用于VisualStudio的CodeRush等一系列辅助工具,该组件拥有众多新产品和数十个具有高影响力的功能,可为桌面、Web和移动应用提供直观的解决方案,全面解决各种使用场......
  • C# WinForm线程里操作控件
    做winform程序,避免不了的要在线程里控制窗体上的控件,直接在子线程里操作控件会报错“线程间操作无效,从不是创建控件***的线程访问它”。解决方法:privatevoidForm1_Load(objectsender,EventArgse){Threadt1=newThread(t1_clock);t1.IsBackground=true;......