向下拖拽展示更多之自定义 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实现拖拽排序侧滑删除效果
django中的admin组件之自定义组件的数据展示以及自定义列