您的位置:首页 > 其它

关于自定义父控件实现右滑动最后,继续滑动,加载刷新更多的另一种实现方案

2017-08-08 17:24 477 查看
关于自定义父控件实现右滑动最后,继续滑动,加载刷新更多

本次采用继承ReleativeLayout作为RecyclerView父控件实现,与前文处理不一样的地方这是弹性实现上文采用设置margin,本次是view的width。

实现过程,既然作为父控件,那么手势操作的处理,我们不用关心处理的过程,即onTouchEvent,我们关注事件的分发,即onInterceptTouchEvent

和dispatchTouchEvent。

那么onInterceptTouchEvent我们不拦截事件,让分发的流程正常向下,就是——

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/**
* 不拦截,直接传递给子的view。
*/
return false;
}


我们在dispatchTouchEvent中做处理。首先需要知道此函数返回值用处——

/**
* 事件分发
*  false-->转给父类onTouchEvent
*  dispatchTouchEvent(ev)-->事件向下分发onInterceptTouchEvent
*  true-->事件被自身消耗
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mChildView == null) {
return super.dispatchTouchEvent(ev);
}

int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:

startDownX = ev.getX();
isReleaseFinger = false;

case MotionEvent.ACTION_MOVE:

float nowX = ev.getX();
int scrollX = (int) (nowX - startDownX);
isReleaseFinger = false;

if ((isCanPullToLeftDirector() && scrollX < 0)) {// 继续向左边做移动

int delta = (int) (scrollX * DRAG_RATE);
mIRefreshMovedViewLayout.doToMove(delta);

if (mIRefreshMovedViewLayout.getVisibleWidth() > 0
&& mIRefreshMovedViewLayout.getRefreshState() < IPullToLeftRefreshState.STATE_REFRESHING) {

return super.dispatchTouchEvent(ev);

} else if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
}

return true;

} else {
startDownX = ev.getX();
if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
return true;
}

return super.dispatchTouchEvent(ev);
}
case MotionEvent.ACTION_UP:
isReleaseFinger = true;

default:

isReleaseFinger = false;
if (mIRefreshMovedViewLayout.releaseAction()) {

if (mOnPullToLeftListener != null) {
mOnPullToLeftListener.onPullToLeftRefresh();

} else {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
doWhatCompleteToRefresh();
}
}, 1200);
}

} else {
return super.dispatchTouchEvent(ev);
}
}
return super.dispatchTouchEvent(ev);
}

isCanPullToLeftDirector()方法作用为:是否滑动至最右端
具体如下:
@Override
public boolean isCanPullToLeftDirector() {
final RecyclerView.Adapter adapter = ((RecyclerView) mChildView).getAdapter();

if (null == adapter) {
return true;
}

final int lastItemPosition = adapter.getItemCount() - 1;
if (((RecyclerView) mChildView)
.getLayoutManager() instanceof LinearLayoutManager) {

final int lastVisiblePosition = ((LinearLayoutManager) ((RecyclerView) mChildView).getLayoutManager()).findLastVisibleItemPosition();

if (lastVisiblePosition >= lastItemPosition) {

final int childIndex = lastVisiblePosition - ((LinearLayoutManager) ((RecyclerView) mChildView)
.getLayoutManager()).findFirstVisibleItemPosition();

final int childCount = ((RecyclerView) mChildView).getChildCount();
final int index = Math.max(childIndex, childCount - 1);
final View lastVisibleChild = ((RecyclerView) mChildView).getChildAt(index);
if (lastVisibleChild != null) {
boolean isArriveToMostRight = lastVisibleChild.getRight() <= mChildView.getRight() - mChildView.getLeft();
return isArriveToMostRight;
}
}
}
return false;
}


本类实现的全部代码:


public class PullToLeftRefreshLinearLayout extends RelativeLayout
implements ViewTreeObserver.OnGlobalLayoutListener, IPullToLeftRefresh{

private static final float DRAG_RATE = 0.50f;
/**
* 必须是RecyclerView
*/
private View mChildView;

/**
* 第二个控件,必须实现IRefreshMovedViewLayout接口
*/
private IRefreshMovedViewLayout mIRefreshMovedViewLayout;

/**
*  用于记录childView(RecyclerView)正常的布局位置
*/
private Rect originalRect = new Rect();

/**
* 是否松开了手指
*/
private boolean isReleaseFinger;

/**
* 按下时候的X坐标
*/
private float startDownX;

/**
* 刷新监听
*/
OnPullToLeftListener mOnPullToLeftListener;

/**
* 设置监听

12bd1
* @param leftListener
*/
public void setOnPullToLeftListener(OnPullToLeftListener leftListener){
this.mOnPullToLeftListener = leftListener;
}

/**
* 回调接口
*/
public interface OnPullToLeftListener{
void onPullToLeftRefresh();
}

public PullToLeftRefreshLinearLayout(Context context) {
super(context);
}

public PullToLeftRefreshLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public PullToLeftRefreshLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);

/**
* 初始值设置
*/
originalRect.set(mChildView.getLeft(), mChildView.getTop(), mChildView.getRight(), mChildView.getBottom());

}

/**
* 得到子孩子RecyclerView
* @return
*/
public RecyclerView getChildView(){
if (mChildView == null){
return null;
}
return  (RecyclerView) mChildView;
}

/**
* 加载布局后初始化,这个方法会在加载完布局后调用
*/
@Override
protected void onFinishInflate() {

if (getChildCount() > 0) {

if (getChildCount() != 2){
throw new RuntimeException("必须有两个子控件且仅能有两个子控件");
}

for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) instanceof RecyclerView) {
if (mChildView == null) {
mChildView = getChildAt(i);
} else {
throw new RuntimeException("只能存在一个RecyclerView");
}
} else if (getChildAt(i) instanceof IRefreshMovedViewLayout) {
if (this.mIRefreshMovedViewLayout == null) {
this.mIRefreshMovedViewLayout = (IRefreshMovedViewLayout) getChildAt(i);
}else {
throw new RuntimeException("只能存在一个实现IRefreshMovedViewLayout接口的控件");
}
}
}
}

if (mChildView == null) {
throw new RuntimeException("子容器中必须有一个RecyclerView");
}
if (this.mIRefreshMovedViewLayout == null) {
throw new RuntimeException("子容器中必须有一个实现IRefreshMovedViewLayout接口的控件");
}

getViewTreeObserver().addOnGlobalLayoutListener(this);

((RecyclerView) mChildView).addOnScrollListener(new RecyclerView.OnScrollListener() {

boolean isToLeftSliding = false;

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

//if (newState == RecyclerView.SCROLL_STATE_IDLE && isToLeftSliding){
//    recyclerView.smoothScrollBy(UiUtils.dip2px(50),0);
//}

super.onScrollStateChanged(recyclerView, newState);
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

if (dx>0){
isToLeftSliding = true;
} else {
isToLeftSliding = false;
}

super.onScrolled(recyclerView, dx, dy);
}
});

super.onFinishInflate();
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout() {
requestLayout();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}

@Override
public boolean isCanPullToLeftDirector() {
final RecyclerView.Adapter adapter = ((RecyclerView) mChildView).getAdapter();

if (null == adapter) {
return true;
}

final int lastItemPosition = adapter.getItemCount() - 1;
if (((RecyclerView) mChildView)
.getLayoutManager() instanceof LinearLayoutManager) {

final int lastVisiblePosition = ((LinearLayoutManager) ((RecyclerView) mChildView).getLayoutManager()).findLastVisibleItemPosition();

if (lastVisiblePosition >= lastItemPosition) {

final int childIndex = lastVisiblePosition - ((LinearLayoutManager) ((RecyclerView) mChildView)
.getLayoutManager()).findFirstVisibleItemPosition();

final int childCount = ((RecyclerView) mChildView).getChildCount();
final int index = Math.max(childIndex, childCount - 1);
final View lastVisibleChild = ((RecyclerView) mChildView).getChildAt(index);
if (lastVisibleChild != null) {
boolean isArriveToMostRight = lastVisibleChild.getRight() <= mChildView.getRight() - mChildView.getLeft();
return isArriveToMostRight;
}
}
}
return false;
}

@Override
public void doWhatCompleteToRefresh() {
mIRefreshMovedViewLayout.refreshComplete();
}

@Override
public void doWhatRecoverLayout() {
mChildView.layout(originalRect.left, originalRect.top, originalRect.right, originalRect.bottom);
requestLayout();
}

@Override
public void setMoveViews(View movedView) {
if (movedView instanceof IRefreshMovedViewLayout) {

this.mIRefreshMovedViewLayout = (IRefreshMovedViewLayout) movedView;
requestLayout();
}
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/**
* 不拦截,直接传递给子的view。
*/
return false;
}

/**
* 事件分发
*  false-->转给父类onTouchEvent
*  dispatchTouchEvent(ev)-->事件向下分发onInterceptTouchEvent
*  true-->事件被自身消耗
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mChildView == null) {
return super.dispatchTouchEvent(ev);
}

int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:

startDownX = ev.getX();
isReleaseFinger = false;

case MotionEvent.ACTION_MOVE:

float nowX = ev.getX();
int scrollX = (int) (nowX - startDownX);
isReleaseFinger = false;

if ((isCanPullToLeftDirector() && scrollX < 0)) {// 继续向左边做移动

int delta = (int) (scrollX * DRAG_RATE);
mIRefreshMovedViewLayout.doToMove(delta);

if (mIRefreshMovedViewLayout.getVisibleWidth() > 0
&& mIRefreshMovedViewLayout.getRefreshState() < IPullToLeftRefreshState.STATE_REFRESHING) {

return super.dispatchTouchEvent(ev);

} else if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
}

return true;

} else {
startDownX = ev.getX();
if (mIRefreshMovedViewLayout.getVisibleWidth() < 0){
mIRefreshMovedViewLayout.setVisibleWidth(UiUtils.dip2px(50));
return true;
}

return super.dispatchTouchEvent(ev);
}
case MotionEvent.ACTION_UP:
isReleaseFinger = true;

default:

isReleaseFinger = false;
if (mIRefreshMovedViewLayout.releaseAction()) {

if (mOnPullToLeftListener != null) {
mOnPullToLeftListener.onPullToLeftRefresh();

} else {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
doWhatCompleteToRefresh();
}
}, 1200);
}

} else {
return super.dispatchTouchEvent(ev);
}
}
return super.dispatchTouchEvent(ev);
}
}


下面是跟随滑动有移动的View的实现,放在RecyclerView右侧,即父控件PullToLeftRefreshLinearLayout最右端对齐,

当中TextTip提供给使用者对四个状态的提示文案:

public class MovedViewLayout extends LinearLayout implements IRefreshMovedViewLayout{

public class TextTip{
public String mRefreshing = "正在刷新加载";
public String mReleaseFingerToRefresh = "松开手指开始刷新加载";
public String mRefreshComplete = "刷新加载完成";
public String mRefreshNormal = "查看更多";
}

/**
* 状态提示类,供给外部使用
*/
TextTip mTextTip;

public TextTip getTextTip(){
return mTextTip;
}

/**
* 默认状态是正常
*/
private @IPullToLeftRefreshState int mRefreshState = IPullToLeftRefreshState.STATE_NORMAL;

public TextView tv_moved_view;
public ImageView iv_moved_view;
public LinearLayout mContainer;

public int mMeasuredWidth;

IPullRefreshStateListener mIPullRefreshStateListener;

public void setIPullRefreshStateListener(IPullRefreshStateListener zIPullRefreshStateListener){
this.mIPullRefreshStateListener = zIPullRefreshStateListener;
}

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

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

public MovedViewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initSetLayout(context);
}

private void initSetLayout(Context context){

LayoutInflater.from(context).inflate(R.layout.look_more, this);

mContainer = (LinearLayout) findViewById(R.id.ll_moved_view);
iv_moved_view = (ImageView) findViewById(R.id.iv_moved_view);
tv_moved_view = (TextView) findViewById(R.id.tv_moved_view);

measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
setGravity(Gravity.CENTER);

mContainer.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));

mMeasuredWidth = getMeasuredWidth();
setVisibility(GONE);

mTextTip = new TextTip();
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}

/**
* 重置状态。
*/
public void resetState() {
/**
* 移动至0会移到屏幕外,不可见
*/
smoothScrollTo(0);
setRefreshState(IPullToLeftRefreshState.STATE_NORMAL);
}

@Override
public void doToMove(float delta) {

if (getVisibleWidth() > 0 || delta < 0) {

if(getVisibility() == View.GONE)
setVisibility(VISIBLE);

if (getVisibleWidth()>mMeasuredWidth*4){

return;
}

setVisibleWidth((int) Math.abs(delta) + getVisibleWidth());

if (mRefreshState <= IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH) {

if (getVisibleWidth()>mMeasuredWidth * 2) {
setRefreshState(IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH);

//} else if (mRefreshState == IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH){
//    setRefreshState(IPullToLeftRefreshState.STATE_REFRESHING);

} else {
setRefreshState(IPullToLeftRefreshState.STATE_NORMAL);
}

}
}
}

@Override
public boolean releaseAction() {
boolean isOnRefresh = false;
int visibleWidth = getVisibleWidth();
if (visibleWidth == 0)
isOnRefresh = false;

if (getVisibleWidth() > mMeasuredWidth && mRefreshState < IPullToLeftRefreshState.STATE_REFRESHING) {
setRefreshState(IPullToLeftRefreshState.STATE_REFRESHING);
isOnRefresh = true;
}

if (mRefreshState == IPullToLeftRefreshState.STATE_REFRESHING && visibleWidth <= mMeasuredWidth) {
//return;
}

int destWidth = 0;

if (mRefreshState == IPullToLeftRefreshState.STATE_REFRESHING) {
destWidth = mMeasuredWidth;
}

/**
* destWidth = 0:移动至不可见
*/
smoothScrollTo(destWidth);

return isOnRefresh;
}

@Override
public void refreshComplete() {
setRefreshState(IPullToLeftRefreshState.STATE_DONE_FINISHED);
new Handler().postDelayed(new Runnable() {
public void run() {
resetState();
}
}, 1000);
}

@Override
public int getVisibleWidth() {

int zWidth = mContainer.getWidth();
return zWidth;
}

@Override
public void setVisibleWidth(int value) {
if (value < 0)
value = 0;

LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();//new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
lp.width = value;
mContainer.setLayoutParams(lp);
invalidate();

}

private void smoothScrollTo(int destWidth) {

ValueAnimator animator = ValueAnimator.ofInt(getVisibleWidth(), destWidth);
animator.setDuration(300).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setVisibleWidth((int) animation.getAnimatedValue());
}
});
animator.start();
}

@Override
public void setRefreshState(@IPullToLeftRefreshState int state) {

if (state == mRefreshState)
return;

switch (state){
case IPullToLeftRefreshState.STATE_NORMAL:
if(getVisibility() == View.GONE)
setVisibility(VISIBLE);
tv_moved_view.setText(mTextTip.mRefreshNormal);

break;
case IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH:
if(getVisibility() == View.GONE) setVisibility(VISIBLE);
tv_moved_view.setText(mTextTip.mReleaseFingerToRefresh);

break;
case IPullToLeftRefreshState.STATE_REFRESHING:
if(getVisibility() == View.GONE) setVisibility(VISIBLE);
tv_moved_view.setText(mTextTip.mRefreshing);

break;
case IPullToLeftRefreshState.STATE_DONE_FINISHED:
if(getVisibility() == View.GONE) setVisibility(VISIBLE);
tv_moved_view.setText(mTextTip.mRefreshComplete);

break;
}

if (mIPullRefreshStateListener != null){
mIPullRefreshStateListener.onPullRefreshState(mRefreshState, state);
}

mRefreshState = state;
}

@Override
public int getRefreshState() {
return mRefreshState;
}

@Override
public void doInitRefreshState(@IPullToLeftRefreshState int state, boolean isArriveToMostRight) {

if (isArriveToMostRight)
smoothScrollTo(mMeasuredWidth);
else
smoothScrollTo(0);

this.mRefreshState = state;
}
}


当中涉及的四种状态的接口定义:

@Retention(RetentionPolicy.SOURCE)
@IntDef({
IPullToLeftRefreshState.STATE_DONE_FINISHED,
IPullToLeftRefreshState.STATE_NORMAL,
IPullToLeftRefreshState.STATE_REFRESHING,
IPullToLeftRefreshState.STATE_RELEASE_TO_REFRESH
})
//@Documented()
public @interface IPullToLeftRefreshState {
/**
* 正常状态下
*/
int STATE_NORMAL = 10;
/**
* 松开手指去刷新
*/
int STATE_RELEASE_TO_REFRESH = 11;
/**
* 正在刷新
*/
int STATE_REFRESHING = 12;
/**
* 刷新完成
*/
int STATE_DONE_FINISHED = 13;
}


跟随滑动而移动View需要实现的接口定义:

public interface IRefreshMovedViewLayout {

void doToMove(float delta);

boolean releaseAction();

void refreshComplete();

int getVisibleWidth();

void setVisibleWidth(int value);

void setRefreshState(@IPullToLeftRefreshState int state);// 设置状态

@IPullToLeftRefreshState int getRefreshState();// 获取刷新状态

void doInitRefreshState(@IPullToLeftRefreshState int state, boolean isArriveToMostRight);

}


父控件需要实现的接口定义:

public interface IPullToLeftRefresh {
/**
* 是否在最后能够进行向左拉
* @return
*/
boolean isCanPullToLeftDirector();

/**
* 完成刷新
*/
void doWhatCompleteToRefresh();

/**
* 还原布局
*/
void doWhatRecoverLayout();

/**
* 跟随移动的view
*/
void setMoveViews(View movedView);
}


状态发生变化时调用的回调接口:

public interface IPullRefreshStateListener {

void onPullRefreshState(@IPullToLeftRefreshState int fromState, @IPullToLeftRefreshState int toState);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐