完全カスタムRecyclerViewドロップダウン・リフレッシュアップロード
30445 ワード
GoogleがSwipeRefreshLayoutを発売してからますます多くのブログがSwipeRefreshLayoutを使ってドロップダウンのリフレッシュを完了していますが、製品マネージャーはGoogleのスタイルをまったく使用しません.どの会社にも独自のドロップダウンスタイルがあります.このとき、RecyclerViewのドロップダウンのリフレッシュを完全にカスタマイズする必要があります.基本的にはネット上のすべてのドロップダウンのリフレッシュを調べました.しかし、効果はあまりよくありません.個人的には、私が書いたこのプルダウンのリフレッシュ効果について、絶対的な66666を感じています.改善意見を提出してください.
くだらないことを言わないで先に効果図を書きます.
RecyclerViewが登場すると、Androidのドロップダウンのリフレッシュとロードがより容易になります.もちろん、既存のライブラリもたくさんありますが、常に異なるニーズがあります.また、他の余分な機能を必要とせずに、最も基本的なドロップダウン・リフレッシュとより多くの機能のロードしか必要ありません.最も純粋なドロップダウン・リフレッシュとロードが必要です.だから、自分でやるのは明らかに最高の結果で、小さな練習です.
コードを説明する前に、実装原理について説明します.
まず,我々はまずRecyclerViewのAdapterクラス装飾器を書き換えることによってaddHeaderView,addFootView法を実現するために頭部と尾を追加する.ヘッドの実装はダイナミックに変更されたヘッドコントロールの高さ、テールの実装はダイナミックに変更されたBottomMargin、テールはデフォルトで表示されているので、Marginを使用して実装することができ、ドロップダウンのリフレッシュとアップロード機能を実現するには、ストレッチ効果が必要になるので、上のようにHeaderはheightを設定することで、Footerは、BottomMarginを設定することでストレッチ効果をシミュレートします.では、リバウンド効果は?高さや間隔を設定するだけでは模擬リバウンド効果は得られないので、Scrollerで模擬リバウンド効果を実現する必要がある.
このプロジェクトは、主要な5つの部分に分けています.HeaderAndFooterWrapper、PullRefreshRecyclerView、RecyclerViewFooter、RecyclerViewHeader、MessageRelativeLayout
以下、別々にご紹介します.
HeaderAndFooterWrapper
このクラスは主にRecyclerViewのaddHeaderView,addFootViewメソッドを実現しており、詳細は弘洋大神のAndroidを参考に優雅にRecyclerViewにHeaderViewとFooterViewを追加
詳細コードは次のとおりです.
RecyclerViewHeader
RecyclerViewHeaderはLinearLayoutがドロップダウン・リフレッシュを実現するために使用したインタフェースの表示を継承し、通常、リフレッシュの準備、ロード中の3つの状態に分けることができます.レイアウトファイルを追加する場合、高さ0を指定します.これはヘッダーを非表示にするためです.setState()はヘッダーを設定する状態です.ヘッダーは異なる状態に応じて、コントロールの非表示、表示、文字の変更などの操作を完了する必要があります.この方法は主にPullRefreshRecyclerViewで呼び出されます.その他にもsetVisiableHeight()とgetVisiableHeight()があります.この2つの方法は、Headerのルートレイアウトファイルの高さプロパティを設定して取得し、ストレッチと収縮の効果を達成するためです.
Headerの話が終わったら、Footerを見てみましょう.Footerはより多くの機能を搭載したときのインタフェースの展示を完了するため、基本的な考え方はHeaderと同じですが、Footerのストレッチと表示効果は高さでシミュレーションするのではなく、BottomMarginを設定することで完成します.
MessageRelativeLayout
このクラスは主に、リフレッシュが完了したときにどれだけ更新されたか、ネットワークエラーなどのヒントを表示できるようにするためです.レイアウトファイルを追加するときは、高さ0を指定します.これはヒントを隠すためです.ヘッダレイアウトと似た考え方を実現します.この機能を無視して、コードの使用に影響を与えません.
PullRefreshRecyclerView
Header,Footer,Messageを理解した後,最もコアなPullRefreshRecyclerViewのコード実装について紹介する.このコードの一部を削除して再構築します.
最后に重要なことは3回言います:本プロジェクトの拡張性はきわめて高くて、ソースコードをダウンロードして観覧することを提案してこのプロジェクトの構造をもっとはっきり理解することができます
ソースアドレス:http://download.csdn.net/download/q714093365/9928217
転載は出典を明記してください.http://blog.csdn.net/q714093365/article/details/77063084
くだらないことを言わないで先に効果図を書きます.
RecyclerViewが登場すると、Androidのドロップダウンのリフレッシュとロードがより容易になります.もちろん、既存のライブラリもたくさんありますが、常に異なるニーズがあります.また、他の余分な機能を必要とせずに、最も基本的なドロップダウン・リフレッシュとより多くの機能のロードしか必要ありません.最も純粋なドロップダウン・リフレッシュとロードが必要です.だから、自分でやるのは明らかに最高の結果で、小さな練習です.
コードを説明する前に、実装原理について説明します.
まず,我々はまずRecyclerViewのAdapterクラス装飾器を書き換えることによってaddHeaderView,addFootView法を実現するために頭部と尾を追加する.ヘッドの実装はダイナミックに変更されたヘッドコントロールの高さ、テールの実装はダイナミックに変更されたBottomMargin、テールはデフォルトで表示されているので、Marginを使用して実装することができ、ドロップダウンのリフレッシュとアップロード機能を実現するには、ストレッチ効果が必要になるので、上のようにHeaderはheightを設定することで、Footerは、BottomMarginを設定することでストレッチ効果をシミュレートします.では、リバウンド効果は?高さや間隔を設定するだけでは模擬リバウンド効果は得られないので、Scrollerで模擬リバウンド効果を実現する必要がある.
このプロジェクトは、主要な5つの部分に分けています.HeaderAndFooterWrapper、PullRefreshRecyclerView、RecyclerViewFooter、RecyclerViewHeader、MessageRelativeLayout
以下、別々にご紹介します.
HeaderAndFooterWrapper
このクラスは主にRecyclerViewのaddHeaderView,addFootViewメソッドを実現しており、詳細は弘洋大神のAndroidを参考に優雅にRecyclerViewにHeaderViewとFooterViewを追加
詳細コードは次のとおりです.
/**
* Created by on 2017/7/5.
* :
* :
*/
public class HeaderAndFooterWrapper extends RecyclerView.Adapter {
private static final int BASE_ITEM_TYPE_HEADER = 100000;
private static final int BASE_ITEM_TYPE_FOOTER = 200000;
//
private SparseArrayCompat mHeaderViews = new SparseArrayCompat<>();
private SparseArrayCompat mFootViews = new SparseArrayCompat<>();
private RecyclerView.Adapter mInnerAdapter;
/**
* adapter
*
* @param adapter
*/
public HeaderAndFooterWrapper(RecyclerView.Adapter adapter) {
mInnerAdapter = adapter;
}
private boolean isHeaderViewPos(int position) {
return position < getHeadersCount();
}
private boolean isFooterViewPos(int position) {
return position >= getHeadersCount() + getRealItemCount();
}
/**
*
*
* @param view
*/
public void addHeaderView(View view) {
mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
}
/**
*
*
* @param view
*/
public void addFootView(View view) {
mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);
}
/**
*
*
* @return
*/
public int getHeadersCount() {
return mHeaderViews.size();
}
/**
*
*
* @return
*/
public int getFootersCount() {
return mFootViews.size();
}
/**
* adapter
*
* @return
*/
private int getRealItemCount() {
return mInnerAdapter.getItemCount();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (mHeaderViews.get(viewType) != null) {
// ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mHeaderViews.get(viewType));
// return holder;
return new HeaderViewHolder(mHeaderViews.get(viewType));
} else if (mFootViews.get(viewType) != null) {
// ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mFootViews.get(viewType));
// return holder;
return new FootViewHolder(mFootViews.get(viewType));
}
return mInnerAdapter.onCreateViewHolder(parent, viewType);
}
@Override
public int getItemViewType(int position) {
if (isHeaderViewPos(position)) {
return mHeaderViews.keyAt(position);
} else if (isFooterViewPos(position)) {
return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
}
return mInnerAdapter.getItemViewType(position - getHeadersCount());
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderViewPos(position)) {
return;
}
if (isFooterViewPos(position)) {
return;
}
mInnerAdapter.onBindViewHolder(holder, position - getHeadersCount());
}
@Override
public int getItemCount() {
return getHeadersCount() + getFootersCount() + getRealItemCount();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
mInnerAdapter.onAttachedToRecyclerView(recyclerView);
/**
*
*/
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int viewType = getItemViewType(position);
if (mHeaderViews.get(viewType) != null) {
return gridLayoutManager.getSpanCount();
} else if (mFootViews.get(viewType) != null) {
return gridLayoutManager.getSpanCount();
} else {
return 1;
}
}
});
}
}
// /**
// * StaggeredGridLayoutManager ,
// * @param holder
// */
// @Override
// public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
// mInnerAdapter.onViewAttachedToWindow(holder);
// int position = holder.getLayoutPosition();
// if (isHeaderViewPos(position) || isFooterViewPos(position)) {
// ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
//
// if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams) {
//
// StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
//
// p.setFullSpan(true);
// }
// }
// }
private class HeaderViewHolder extends RecyclerView.ViewHolder {
HeaderViewHolder(View itemView) {
super(itemView);
}
}
private class FootViewHolder extends RecyclerView.ViewHolder {
FootViewHolder(View itemView) {
super(itemView);
}
}
}
RecyclerViewHeader
RecyclerViewHeaderはLinearLayoutがドロップダウン・リフレッシュを実現するために使用したインタフェースの表示を継承し、通常、リフレッシュの準備、ロード中の3つの状態に分けることができます.レイアウトファイルを追加する場合、高さ0を指定します.これはヘッダーを非表示にするためです.setState()はヘッダーを設定する状態です.ヘッダーは異なる状態に応じて、コントロールの非表示、表示、文字の変更などの操作を完了する必要があります.この方法は主にPullRefreshRecyclerViewで呼び出されます.その他にもsetVisiableHeight()とgetVisiableHeight()があります.この2つの方法は、Headerのルートレイアウトファイルの高さプロパティを設定して取得し、ストレッチと収縮の効果を達成するためです.
/**
* Created by on 2017/7/18.
* :
* :
*/
public class RecyclerViewHeader extends LinearLayout {
/**
*
*/
private final int ROTATE_ANIM_DURATION = 180;
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_REFRESHING = 2;
/**
*
*/
private int mState = STATE_NORMAL;
//
private LinearLayout mContainer;
//
private ImageView mArrowImageView;
// private ProgressBar mProgressBar;
private TextView mHintTextView;
//
// private RotateAnimation mRotateUpAnim;
// private Animation mRotateDownAnim;
private TextView mTitleTextView;
private RelativeLayout mRealityContent;
public RecyclerViewHeader(Context context) {
this(context, null);
}
public RecyclerViewHeader(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RecyclerViewHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
// , view 0
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
//
mContainer = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pullrefrefh_recyclerview_header, (ViewGroup) getParent(), true);
//
addView(mContainer, lp);
//
setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
//
mRealityContent = (RelativeLayout) mContainer.findViewById(R.id.pullRefresh_reality_content);
mArrowImageView = (ImageView) mContainer.findViewById(R.id.pullRefresh_arrow);
mHintTextView = (TextView) mContainer.findViewById(R.id.pullRefresh_text);
// mProgressBar = (ProgressBar) findViewById(R.id.pullRefresh_progressbar);
mTitleTextView = (TextView) mContainer.findViewById(R.id.pullRefresh_title);
//
// mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
// Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
// 0.5f);
// mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
// mRotateUpAnim.setFillAfter(true);
// mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
// Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
// 0.5f);
// mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
// mRotateDownAnim.setFillAfter(true);
}
public void setState(int state) {
//
if (mState == state) return;
//
if (state == STATE_REFRESHING) { //
// mArrowImageView.clearAnimation();
// mArrowImageView.setVisibility(View.INVISIBLE);
// mProgressBar.setVisibility(View.VISIBLE);
} else { //
// mArrowImageView.setVisibility(View.VISIBLE);
// mProgressBar.setVisibility(View.INVISIBLE);
}
switch (state) {
case STATE_NORMAL://
if (mState == STATE_READY) {
// mArrowImageView.startAnimation(mRotateDownAnim);
}
if (mState == STATE_REFRESHING) {
// mArrowImageView.clearAnimation();
}
mHintTextView.setText(" ");
break;
case STATE_READY://
if (mState != STATE_READY) {
// mArrowImageView.clearAnimation();
// mArrowImageView.startAnimation(mRotateUpAnim);
mHintTextView.setText(" ");
}
break;
case STATE_REFRESHING://
mHintTextView.setText(" ...");
break;
default:
}
mState = state;
}
/**
*
*
* @param imagePath
*/
public void setPullImage(String imagePath) {
Drawable fromPath = Drawable.createFromPath(imagePath);
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
// mArrowImageView.setBackground(fromPath);
mArrowImageView.setImageBitmap(bitmap);
}
/**
*
*
* @param text
*/
public void setPullContent(String text) {
mTitleTextView.setText(text);
}
/**
*
*/
public int getRealityHeight() {
return mRealityContent.getHeight();
}
/**
*
*
* @param height
*/
public void setVisibleHeight(int height) {
if (height < 0) {
height = 0;
}
LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();
lp.height = height;
mContainer.setLayoutParams(lp);
}
/**
*
*
* @return
*/
public int getVisibleHeight() {
return mContainer.getLayoutParams().height;
}
}
Headerの話が終わったら、Footerを見てみましょう.Footerはより多くの機能を搭載したときのインタフェースの展示を完了するため、基本的な考え方はHeaderと同じですが、Footerのストレッチと表示効果は高さでシミュレーションするのではなく、BottomMarginを設定することで完成します.
/**
* Created by on 2017/8/9.
* :
* :
*/
public class RecyclerViewFooter extends LinearLayout {
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_LOADING = 2;
private Context context;
private View contentView;
private View progressBar;
private TextView hintView;
public RecyclerViewFooter(Context context) {
this(context, null);
}
public RecyclerViewFooter(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RecyclerViewFooter(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView();
}
private void initView() {
LinearLayout moreView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pullrefrefh_recyclerview_footer, null);
addView(moreView);
moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
contentView = moreView.findViewById(R.id.pullrefrefh_footer_content);
progressBar = moreView.findViewById(R.id.pullrefrefh_footer_ProgressBar);
hintView = (TextView) moreView.findViewById(R.id.pullrefrefh_footer_hint_TextView);
}
/**
*
*
* @param state
*/
public void setState(int state) {
hintView.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
hintView.setVisibility(View.INVISIBLE);
if (state == STATE_READY) {
hintView.setVisibility(View.VISIBLE);
hintView.setText(" ");
} else if (state == STATE_LOADING) {
progressBar.setVisibility(View.VISIBLE);
} else {
hintView.setVisibility(View.VISIBLE);
hintView.setText(" ");
}
}
/**
* BottomMargin
*
* @param height
*/
public void setBottomMargin(int height) {
if (height < 0) return;
LayoutParams lp = (LayoutParams) contentView.getLayoutParams();
lp.bottomMargin = height;
contentView.setLayoutParams(lp);
}
/**
* BottomMargin
*
* @return
*/
public int getBottomMargin() {
LayoutParams lp = (LayoutParams) contentView.getLayoutParams();
return lp.bottomMargin;
}
/**
* hide footer when disable pull load more
*/
public void hide() {
LayoutParams lp = (LayoutParams) contentView.getLayoutParams();
lp.height = 0;
contentView.setLayoutParams(lp);
}
/**
* show footer
*/
public void show() {
LayoutParams lp = (LayoutParams) contentView.getLayoutParams();
lp.height = LayoutParams.WRAP_CONTENT;
contentView.setLayoutParams(lp);
}
}
MessageRelativeLayout
このクラスは主に、リフレッシュが完了したときにどれだけ更新されたか、ネットワークエラーなどのヒントを表示できるようにするためです.レイアウトファイルを追加するときは、高さ0を指定します.これはヒントを隠すためです.ヘッダレイアウトと似た考え方を実現します.この機能を無視して、コードの使用に影響を与えません.
/**
* Created by on 2017/7/18.
* :
* :
*/
public class MessageRelativeLayout extends RelativeLayout {
//
private LinearLayout mHeaderMessageView;
private TextView mHeaderMessageText;
private int mHeaderMessageViewHeight;
//
private Scroller mScroller;
public MessageRelativeLayout(Context context) {
this(context, null);
}
public MessageRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MessageRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
//
mScroller = new Scroller(context, new DecelerateInterpolator());
// textView
mHeaderMessageView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.pullrefresh_header_message, (ViewGroup) getParent(), false);
mHeaderMessageView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0));
mHeaderMessageText = (TextView) mHeaderMessageView.findViewById(R.id.pullRefresh_message);
//
mHeaderMessageText.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mHeaderMessageViewHeight = mHeaderMessageText.getHeight();//57
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//
addView(mHeaderMessageView, 1);
}
public void showMessage() {
mScroller.startScroll(0, getHeaderMessageViewHeight(), 0, 0, PullRefreshRecyclerView.SCROLL_DURATION);
invalidate();
}
public void hideMessage() {
mScroller.startScroll(0, getVisibleHeight(), 0, -getVisibleHeight(), PullRefreshRecyclerView.SCROLL_DURATION);
invalidate();
}
/**
*
*/
public void setMessage(String message) {
mHeaderMessageText.setText(message);
}
/**
*
*
* @return
*/
public int getHeaderMessageViewHeight() {
return mHeaderMessageViewHeight;
}
/**
*
*
* @param height
*/
private void setVisibleHeight(int height) {
if (height < 0) {
height = 0;
}
LayoutParams lp = (LayoutParams) mHeaderMessageView.getLayoutParams();
lp.height = height;
mHeaderMessageView.setLayoutParams(lp);
}
/**
*
*
* @return
*/
public int getVisibleHeight() {
return mHeaderMessageView.getLayoutParams().height;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
setVisibleHeight(mScroller.getCurrY());
postInvalidate();
}
super.computeScroll();
}
}
PullRefreshRecyclerView
Header,Footer,Messageを理解した後,最もコアなPullRefreshRecyclerViewのコード実装について紹介する.このコードの一部を削除して再構築します.
/**
* Created by on 2017/7/18.
* :
* :
*/
public class PullRefreshRecyclerView extends RecyclerView {
private float mLastY = -1; // save event y
/**
*
*/
public final static int SCROLL_DURATION = 200;
/**
*
*/
public final static int MESSAGE_SHOW_DURATION = 2000;
/**
*
*/
private final static float OFFSET_RADIO = 1.5f;
/**
* , 50px
*/
private static final int PULL_LOAD_MORE_DELTA = 50;
/**
* ,
*/
private boolean mEnableAutoLoading = false;
/**
*
*/
private boolean mEnablePullLoad = true;
/**
*
*/
private boolean mEnablePullRefresh = true;
/**
*
*/
private boolean mPullLoading = false;
/**
*
*/
private boolean mPullRefreshing = false;
/**
*
*/
private int mScrollBack;
private final static int SCROLLBACK_HEADER = 0;
private final static int SCROLLBACK_FOOTER = 1;
//
private Scroller mScroller;
//
private RecyclerViewHeader mHeaderView;
//
private RecyclerViewFooter mFooterView;
//
private MessageRelativeLayout mParent;
//adapter
private HeaderAndFooterWrapper mHeaderAndFooterWrapper;
public PullRefreshRecyclerView(Context context) {
this(context, null);
}
public PullRefreshRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public PullRefreshRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
//
mScroller = new Scroller(context, new DecelerateInterpolator());
//
mHeaderView = new RecyclerViewHeader(context);
mHeaderView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
//
mFooterView = new RecyclerViewFooter(context);
mFooterView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
private Adapter adapter;
@Override
public void setAdapter(Adapter adapter) {
this.adapter = adapter;
mHeaderAndFooterWrapper = new HeaderAndFooterWrapper(adapter);
super.setAdapter(mHeaderAndFooterWrapper);
// ,
mHeaderAndFooterWrapper.addHeaderView(mHeaderView);
// ,
mHeaderAndFooterWrapper.addFootView(mFooterView);
//
if (getParent() instanceof MessageRelativeLayout) {
mParent = (MessageRelativeLayout) getParent();
}
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (mLastY == -1) {
mLastY = e.getRawY();
}
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
//
mLastY = e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float moveY = e.getRawY();
//
float distanceY = moveY - mLastY;
mLastY = moveY;
// // 0 deltaY 0
if ((((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0 || ((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 1) && (mHeaderView.getVisibleHeight() > 0 || distanceY > 0)) {
//
updateHeaderHeight(distanceY / OFFSET_RADIO);
} else if (isSlideToBottom() && (mFooterView.getBottomMargin() > 0 || distanceY < 0)) {
Log.e("PullRefreshRecyclerView","-------111------"+distanceY);
// ,
updateFooterHeight(-distanceY / OFFSET_RADIO);
}else if (distanceY > 0){
updateFooterHeight(-distanceY / OFFSET_RADIO);
}
break;
default:
mLastY = -1; //
if ((((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0 || ((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 1)) {
//
if (mEnablePullRefresh && mHeaderView.getVisibleHeight() > mHeaderView.getRealityHeight()) {
//
mPullRefreshing = true;
mHeaderView.setState(RecyclerViewHeader.STATE_REFRESHING);
//
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
resetHeaderHeight();
} else if (isSlideToBottom()) {
// invoke load more.
if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA && !mPullLoading) {
mPullLoading = true;
mFooterView.setState(RecyclerViewFooter.STATE_LOADING);
if (mOnRefreshListener != null) {
mOnRefreshListener.onLoadMore();
}
}
resetFooterHeight();
} else {
// resetFooterHeight();
resetHeaderHeight();
}
break;
}
return super.onTouchEvent(e);
}
/**
*
*
* @param distance
*/
private void updateFooterHeight(float distance) {
int height = mFooterView.getBottomMargin() + (int) distance;
Log.e("PullRefreshRecyclerView","-------------"+height);
if (mEnablePullLoad && !mPullLoading) {
if (height > PULL_LOAD_MORE_DELTA) {
//
mFooterView.setState(RecyclerViewFooter.STATE_READY);
} else {
mFooterView.setState(RecyclerViewFooter.STATE_NORMAL);
}
}
mFooterView.setBottomMargin(height);
}
/**
*
*
* @param distance
*/
private void updateHeaderHeight(float distance) {
// ,
mHeaderView.setVisibleHeight((int) distance + mHeaderView.getVisibleHeight());
// ,
if (mEnablePullRefresh && !mPullRefreshing) {
//
if (mHeaderView.getVisibleHeight() > mHeaderView.getRealityHeight()) {
mHeaderView.setState(RecyclerViewHeader.STATE_READY);
} else {
mHeaderView.setState(RecyclerViewHeader.STATE_NORMAL);
}
}
//
smoothScrollBy(0, 0);
}
/**
*
*/
private void resetHeaderHeight() {
int height = mHeaderView.getVisibleHeight();
if (height == 0) // =0
return;
if (mPullRefreshing && height <= mHeaderView.getRealityHeight()) {
return;
}
int finalHeight = 0;
if (mPullRefreshing && height > mHeaderView.getRealityHeight()) {
finalHeight = mHeaderView.getRealityHeight();
}
if (mParent != null) {
if (mHeaderView.getVisibleHeight() == mParent.getHeaderMessageViewHeight()) {
finalHeight = mParent.getHeaderMessageViewHeight();
}
}
mScrollBack = SCROLLBACK_HEADER;//
mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);
//
invalidate();
}
/**
*
*/
private void resetFooterHeight() {
int bottomMargin = mFooterView.getBottomMargin();
if (bottomMargin > 0) {
mScrollBack = SCROLLBACK_FOOTER;//
mScroller.startScroll(0, bottomMargin, 0, -bottomMargin, SCROLL_DURATION);
invalidate();
}
}
/**
*
*/
public void stopRefresh() {
mScrollBack = SCROLLBACK_HEADER;//
int obligateHeight;
if (mParent != null) {
obligateHeight = mParent.getHeaderMessageViewHeight();
} else {
obligateHeight = 0;
}
int height = mHeaderView.getVisibleHeight();
if (mPullRefreshing) {
//
mPullRefreshing = false;
//
if (mParent != null) {
mParent.showMessage();
}
mScroller.startScroll(0, height, 0, obligateHeight - height, SCROLL_DURATION);
//
invalidate();
//
if (mParent != null) {
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(1, MESSAGE_SHOW_DURATION);
}
}
}
/**
*
*/
public void stopLoadMore() {
if (mPullLoading) {
mPullLoading = false;
mFooterView.setState(RecyclerViewFooter.STATE_NORMAL);
}
}
/**
*
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mHeaderView.getVisibleHeight() == mParent.getHeaderMessageViewHeight()) {
// resetHeaderHeight();
mScroller.startScroll(0, mHeaderView.getVisibleHeight(), 0, -mHeaderView.getVisibleHeight(), SCROLL_DURATION);
postInvalidate();
}
mParent.hideMessage();
}
};
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
if (mScrollBack == SCROLLBACK_HEADER) {
mHeaderView.setVisibleHeight(mScroller.getCurrY());
} else {
mFooterView.setBottomMargin(mScroller.getCurrY());
}
postInvalidate();
}
super.computeScroll();
}
private OnRefreshListener mOnRefreshListener;
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}
/**
* ,
*/
public interface OnRefreshListener {
void onRefresh();
void onLoadMore();
}
/**
*
*
* @return
*/
private boolean isSlideToBottom() {
return computeVerticalScrollExtent() + computeVerticalScrollOffset() >= computeVerticalScrollRange();
}
}
最后に重要なことは3回言います:本プロジェクトの拡張性はきわめて高くて、ソースコードをダウンロードして観覧することを提案してこのプロジェクトの構造をもっとはっきり理解することができます
ソースアドレス:http://download.csdn.net/download/q714093365/9928217
転載は出典を明記してください.http://blog.csdn.net/q714093365/article/details/77063084