向下拖拽展示更多之自定义 RecyclerView

Posted AAA啊哈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了向下拖拽展示更多之自定义 RecyclerView相关的知识,希望对你有一定的参考价值。

本文

http://afra55.github.io/2017/07/27/drag_down_to_show_more/

前情提要

自定义 RecyclerView 向下拖拽展示更多;

需要自定义的参数(可选), 用来限制最大高度:

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

自定义 RecyclerView (大前提需要设置 minHeight, 通过 minHeight 来判断一行的高度)

这里已经做到了与 adapter 的分离,只要adapter 实现 DragMinHeightAdapterListener 接口即可,详细代码附带注释如下:

/**
 * Created by yangshuai on 2017/7/26.
 * link http://afra55.github.io
 */
public class DragMinHeightRecyclerView extends RecyclerView 
    /**
     * 分离adapter, 只需要拿到总数据个数和高亮选择的数据位置
     */
    public interface DragMinHeightAdapterListener 

        /**
         * 获取总数据个数
         * @return int
         */
        int getItemCount();

        /**
         * 获取选择的数据位置
         * @return int
         */
        int getSelectPosition();

    

    /**
     * 限定一个最高高度
     */
    private int maxHeight = ScreenUtil.dip2px(200);

    /**
     * 高度变化的属性动画
     */
    private ValueAnimator mHeightChangeValueAnimator;

    /**
     * 持有 LayoutManager
     */
    private GridLayoutManager mLayoutManager;

    /**
     * 持有 DragMinHeightAdapterListener
     */
    private DragMinHeightAdapterListener mDragMinHeightAdapterListener;


    public DragMinHeightRecyclerView(Context context) 
        super(context);
        init(null, 0);
    

    public DragMinHeightRecyclerView(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
        init(attrs, 0);
    

    public DragMinHeightRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    

    private float mLastTouchY;
    private int mCurrentHeight;

    private void init(AttributeSet attrs, int defStyle) 
        addOnItemTouchListener(new OnItemTouchListener() 
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) 

                // 拦截 item 的触摸事件,直接拦截 RecyclerView 是不行的
                return isNeedInterceptMoveTouch(e);

            

            @Override
            public void onTouchEvent(RecyclerView rv, MotionEvent e) 

            

            @Override
            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) 

            
        );

        if (attrs != null) 
            final TypedArray a = getContext().obtainStyledAttributes(
                    attrs, R.styleable.DragMinHeightRecyclerView, defStyle, 0);
            // 拿到 布局文件配置的 最大高度
            maxHeight = a.getDimensionPixelSize(R.styleable.DragMinHeightRecyclerView_maxHeight, maxHeight);
            a.recycle();
        

    

    /**
     * 判断是否需要拦截触摸事件
     * @param e MotionEvent
     * @return true 拦截; false 不拦截;
     */
    private boolean isNeedInterceptMoveTouch(MotionEvent e) 
        if (e.getAction() == MotionEvent.ACTION_MOVE) 
            // 只针对触摸移动进行拦截处理
            if (getLayoutParams().height < maxHeight) 
                // 没有到达所限定的高度时拦截滑动事件
                return true;
             else if (mDragMinHeightAdapterListener != null && mLayoutManager != null)
                float durationY = e.getY() - mLastTouchY;
                if (durationY < 0 && mLayoutManager.findLastCompletelyVisibleItemPosition() >= mDragMinHeightAdapterListener.getItemCount() - 1) 
                    // 如果达到了限定的最大高度,判断是否展示了最后一个,并向上拖拽,才进行拦截处理;
                    return true;
                
            

        
        return false;
    


    @Override
    public boolean onTouchEvent(MotionEvent e) 

        if (mHeightChangeValueAnimator != null && mHeightChangeValueAnimator.isRunning()
                || getMinimumHeight() >= maxHeight) 
            // 高度变化动画进行中,不处理
            return super.onTouchEvent(e);
        


        float currentTouchY = e.getY();


        switch (e.getAction()) 
            case MotionEvent.ACTION_DOWN:
                // 获取按下时的Y位置
                mLastTouchY = currentTouchY;

                // 获取按下时 recyclerView 的高度
                mCurrentHeight = getLayoutParams().height;
                break;

            case MotionEvent.ACTION_MOVE:
                // 移动的距离,负数表示向上拖拽,正数表示向下拖拽
                float durationY = currentTouchY - mLastTouchY;
                if (!isNeedInterceptMoveTouch(e)
                        || durationY < 0 && getLayoutParams().height <= getMinimumHeight()
                        || durationY > 0 && getLayoutParams().height >= maxHeight) 
                    // 先判断是否进行拦截处理;
                    // 如果向上拖拽并已经到达设置的最小高度限制不拦截;
                    // 如果向下拖拽并已经到达最大高度限制,则不拦截;
                    break;
                

                // 处理拖拽时高度变更;
                getLayoutParams().height = (int) (mCurrentHeight + durationY);
                requestLayout();

                getParent().requestDisallowInterceptTouchEvent(true);

                return true;
            default:
                // 释放触摸时,使用动画移动到指定的最大或最小的高度;没有实现抛的功能;
                int endHeight;
                if (getLayoutParams().height > getMinimumHeight() +  (maxHeight - getMinimumHeight()) / 2) 
                    endHeight = maxHeight;
                 else 
                    endHeight = getMinimumHeight();
                
                mHeightChangeValueAnimator = ValueAnimator.ofInt(getLayoutParams().height, endHeight);
                mHeightChangeValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() 
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) 

                        getLayoutParams().height = (int) animation.getAnimatedValue();
                        requestLayout();

                        if (getLayoutParams().height <= getMinimumHeight()) 
                            resetSmoothShowPosition();
                        
                    

                );

                mHeightChangeValueAnimator.setDuration(100).start();

                break;
        

        return super.onTouchEvent(e);
    

    /**
     * 滑动到选择项的位置
     */
    public void resetSmoothShowPosition() 
        try 
            mLayoutManager.smoothScrollToPosition(this, new RecyclerView.State(), mDragMinHeightAdapterListener.getSelectPosition());
         catch (Exception e1) 
            e1.printStackTrace();
        
    

    /**
     * 立即移动到选择项的位置,无动画
     */
    public void resetShowPosition() 
        try 
            mLayoutManager.scrollToPositionWithOffset(mDragMinHeightAdapterListener.getSelectPosition(), 0);
         catch (Exception e1) 
            e1.printStackTrace();
        
    

    @Override
    public void setLayoutManager(LayoutManager layout) 
        super.setLayoutManager(layout);
        if (layout instanceof GridLayoutManager) 
            mLayoutManager = (GridLayoutManager) layout;
        
    

    @Override
    public void setAdapter(Adapter adapter) 
        super.setAdapter(adapter);
        if (adapter instanceof DragMinHeightAdapterListener) 
            mDragMinHeightAdapterListener = (DragMinHeightAdapterListener) adapter;
            adapter.registerAdapterDataObserver(new AdapterDataObserver() 
                @Override
                public void onChanged() 
                    super.onChanged();
                    // 监听数据变化,来抓取最大高度
                    if (mLayoutManager != null) 
                        int lines = mDragMinHeightAdapterListener.getItemCount() / mLayoutManager.getSpanCount()
                                + (mDragMinHeightAdapterListener.getItemCount() % mLayoutManager.getSpanCount() > 0 ? 1 : 0 );

                        int compareHeight = getMinimumHeight() * lines;
                        if (compareHeight < maxHeight) 
                            maxHeight = compareHeight;
                        
                    
                
            );
        
    

使用简介

    <DragMinHeightRecyclerView
        android:layout_width="match_parent"
        android:background="@color/blue_light"
        apps:maxHeight="300dp"
        android:minHeight="@dimen/normal_item_height"
        android:layout_height="@dimen/normal_item_height"/>

以上是关于向下拖拽展示更多之自定义 RecyclerView的主要内容,如果未能解决你的问题,请参考以下文章

Android基础控件——RecyclerView实现拖拽排序侧滑删除效果

Qt之窗体拖拽自适应分辨率自适应大小 good

django中的admin组件之自定义组件的数据展示以及自定义列

Android recyclerview拖拽自定义功能

[Android]自定义控件LoadMoreRecyclerView

Linux之自定义的 Shell 函数和函数库