Android RecyclerViewでは、より多くのローディングとドロップダウンリフレッシュ機能の実現方法を紹介します。


RecyclerViewはもう長い間出てきましたが、プロジェクトの前にはListViewが使われていました。最近は新しいプロジェクトにRecycleViewが多く使われています。特に滝の流れの下で更新します。ネット上でいいですよね。適当な自分でまとめました。
まず図を貼ってみます。
这里写图片描述 
 
这里写图片描述
RecyclerViewを使って、アップロードとドロップダウンリフレッシュの機能を実現するには、自分で2つの方法があります。
1.システムが持参するAndroid.support.v.4.widget.SwipeRefresh Layoutという価格で実現します。
2.カスタムの中にRecyleViewのコントロールがあります。
RecycleViewを使うと頭を追加するのが大変です。以前はlistviewを使っていましたが、自分でheaderとboottmを追加することができます。でも、RecycleViewはそんなにいい操作ではないようです。最初の使用システムはAndroid.support.v 4.widget.SwipeRefresh Layoutで実現されていますが、製品は一般的にこのようなボトムアップをしないでください。自分を追い込むために、自分のアニメを持っているというのが一般的です。だから方法を使うしかないです。
大体においては、方法2の実現形態では、親のレイアウトはVieGroupであり、中にViewを追加した最初のコントロールはheaderの第二のコントロールはRecycleViewであり、一番下のプルダウンローディングについては、RecycleViwのAdapterによって追加しようとする。
考えがあればやり始めます。

package com.krain.srecyclerview.fruitview;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.krain.srecyclerview.R;
/**
* Created by dafuShao on 2016/9/9 0009.
*
*/
public class ElizabethView extends FrameLayout {
private ImageView imageView;
private AnimationDrawable animationDrawable;
public ElizabethView(Context context) {
super(context);
initview(context);
}
public ElizabethView(Context context, AttributeSet attrs) {
super(context, attrs);
initview(context);
}
public ElizabethView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initview(context);
}
private void initview(Context context){
View view= LayoutInflater.from(context).inflate(R.layout.elizabeth_item,null);
imageView=(ImageView) view.findViewById(R.id.elizabeth_im);
animationDrawable= (AnimationDrawable) imageView.getBackground();
addView(view);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
//    
public void startAnim(){
animationDrawable.start();
}
//    
public void stopAnim(){
animationDrawable.stop();
}
}
これは価格を制御するのです。中には小さな臭い卵があります。目を左右にします。
以下はカスタムのRecyclerViewを含むコントロールコードです。

package com.krain.srecyclerview.srecyclerview;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;
import com.krain.srecyclerview.R;
import com.krain.srecyclerview.fruitview.ElizabethView;
public class SRecyclerView extends ViewGroup {
Context context;
RecyclerView mRecyclerView;
ElizabethView mHeaderView;
TextView mFootViewTips;//footview     
AdapterWrapper mAdapter;
boolean mIsTop = true;//         
RecyclerView.LayoutManager mLayoutManager;
int mLastVisibleItem;
int mFirstVisibleItem;
OnRecyclerStatusChangeListener mRecyclerChangeListener;
int mStatus;//    
int mHeadviewHeight;//headview   
Scroller mScroller;
int mFristScollerY;//    getscrolly
boolean mHasFooter;//          
boolean mShowFootVisible;//    FOOTERview mHasFooter true     
boolean mHasRefresh = true;//        
private final int DEFAULT_MIN_PAGEINDEX = 1;//       
int mMaxPage = DEFAULT_MIN_PAGEINDEX;//      
int mCurrentPage = DEFAULT_MIN_PAGEINDEX;//     , 1  
private final int STATUS_NORMAL = 0, STATUS_REFRESH = 1, STATUS_LOAD = 2;
private final int MSG_LOAD_COMPLETE = 1, MSG_REFRESH_COMPLETE = 0;//handle   
private final int DELAY_LOAD_COMPLETE = 1000, DELAY_REFRESH_COMPLETE = 1000;//           
public SRecyclerView(Context context) {
super(context);
init(context);
}
public SRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
*       
*
* @param maxPage
*/
public void setMaxPage(int maxPage) {
this.mMaxPage = maxPage;
}
/**
*         
*
* @param hasLoadmore
*/
public void setLoadmore(boolean hasLoadmore) {
mHasFooter = hasLoadmore;
}
public void setRecyclerViewLayoutManage(RecyclerView.LayoutManager mLayoutManage){
this.mLayoutManager=mLayoutManage;
}
/**
*         
*/
public void disableRefresh() {
mHasRefresh = false;
}
public void setAdapter(BaseRecyclerViewAdapter adapter) {
int height = 0;
if (mMaxPage == DEFAULT_MIN_PAGEINDEX) {
mHasFooter = false;
}
mAdapter = new AdapterWrapper(context, adapter);
mRecyclerView.setAdapter(mAdapter);
}
private int getViewHeight(View view) {
int measure = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measure, measure);
return view.getMeasuredHeight();
}
/**
*   viewgroup   recyclerview
*
* @return
*/
public RecyclerView getRecyclerView() {
return mRecyclerView;
}
public void setOnRecyclerChangeListener(OnRecyclerStatusChangeListener listener) {
mRecyclerChangeListener = listener;
}
/**
*   RecyclerView  、  Item   
*
* @param animator
*/
public void setItemAnimator(RecyclerView.ItemAnimator animator) {
mRecyclerView.setItemAnimator(animator);
}
public void notifyDataSetChanged() {
mStatus = STATUS_NORMAL;//  set    loadmore   ,         
mAdapter.notifyDataSetChanged();
}
public void notifyDataInsert(int positionStart, int itemCount) {
mStatus = STATUS_NORMAL;//  set    loadmore   ,         
mAdapter.notifyItemRangeInserted(positionStart, itemCount);
}
public void notifyDataRemove(int position) {
mStatus = STATUS_NORMAL;//  set    loadmore   ,         
mAdapter.notifyItemRemoved(position);
}
/**
*      
*
* @param context
*/
void init(Context context) {
this.context = context;
mScroller = new Scroller(context);
// if (mLayoutManager!=null){
// addChildView(context,mLayoutManager);
// }else{
// addChildView(context, new LinearLayoutManager(context));
// }
addChildView(context);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
/**
*    View
*
* @param context
*/
void addChildView(Context context, RecyclerView.LayoutManager mLayoutManager) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mHeaderView = new ElizabethView(context);
mRecyclerView = new RecyclerView(context);
addView(mHeaderView);
addView(mRecyclerView);
//mLayoutManager = new LinearLayoutManager(context);
// mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
//   Item  、      
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addOnScrollListener(onScrollListener);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(params);
}
void addChildView(Context contex) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mHeaderView = new ElizabethView(context);
mRecyclerView = new RecyclerView(context);
addView(mHeaderView);
addView(mRecyclerView);
//mLayoutManager = new LinearLayoutManager(context);
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
//   Item  、      
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addOnScrollListener(onScrollListener);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(params);
}
/**
*   Recyclerview     (       )
*/
float lastY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
lastY = ev.getRawY();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
return false;
case MotionEvent.ACTION_MOVE:
if (mHasRefresh && mIsTop && ev.getRawY() > lastY)
return true;
break;
}
return false;
}
float offsetY = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
offsetY = Math.abs(event.getRawY() - lastY); //Y     
if (offsetY > 0)
scrollToOffset(offsetY);
else {
mIsTop = false;
}
break;
case MotionEvent.ACTION_UP:
if (getScrollY() <= 0) {
doRefresh();
mHeaderView.stopAnim();
mHeaderView.startAnim();
} else complete();
break;
}
return super.onTouchEvent(event);
}
/**
*     view       
*
* @param offsetY Y    
*/
void scrollToOffset(float offsetY) {
//           scrolly         ,          ,     only
if (getScrollY() == mFristScollerY && mRecyclerChangeListener != null)
mRecyclerChangeListener.startRefresh();
int value = Math.round(offsetY / 2.0F);
value = mFristScollerY - value;
scrollTo(0, value);
}
/**
*       ,   header      
*/
void doRefresh() {
mStatus = STATUS_REFRESH;
int currentY = getScrollY();
mScroller.startScroll(0, currentY, 0, (mFristScollerY - mHeadviewHeight) - currentY);
invalidate();
if (mRecyclerChangeListener != null) mRecyclerChangeListener.onRefresh();
handler.sendEmptyMessageDelayed(MSG_REFRESH_COMPLETE, DELAY_REFRESH_COMPLETE);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MSG_LOAD_COMPLETE) {
View footview = mAdapter.getFootView();
if (footview != null)
mRecyclerView.smoothScrollBy(0, -footview.getMeasuredHeight());
} else if (msg.what == MSG_REFRESH_COMPLETE)
complete();
}
};
/**
* header        
*/
public void complete() {
mCurrentPage = DEFAULT_MIN_PAGEINDEX;//       page     
if (mFootViewTips != null)
mFootViewTips.setText(context.getString(R.string.loading));//  foot        
if (mRecyclerChangeListener != null) mRecyclerChangeListener.refreshComplete();
mStatus = STATUS_NORMAL;
int currentY = getScrollY();
mScroller.startScroll(0, currentY, 0, mFristScollerY - currentY);
mHeaderView.stopAnim();
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
height += child.getMeasuredHeight();
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = getPaddingLeft();
int top = getPaddingTop();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (i == 0) {//  header       
int headerLeft = getMeasuredWidth() / 2 - child.getMeasuredWidth() / 2;
child.layout(headerLeft, top, headerLeft + child.getMeasuredWidth(), top + child.getMeasuredHeight());
} else
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
top += child.getMeasuredHeight();
}
mHeadviewHeight = getPaddingTop() + mHeaderView.getMeasuredHeight();
scrollTo(0, mHeadviewHeight);//   header     recyleview
mFristScollerY = getScrollY();
}
/**
* RecyclerView       
*/
RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//      
if (mFirstVisibleItem == 0) {
mIsTop = true;
} else {
mIsTop = false;
if (mStatus != STATUS_LOAD && mShowFootVisible && mLastVisibleItem + 1 == mAdapter.getItemCount()) {
if (mCurrentPage == mMaxPage) {
//            
mFootViewTips = (TextView) mAdapter.getFootView().findViewById(R.id.footer_tips);
mFootViewTips.setText(context.getString(R.string.last_page_tips));
handler.sendEmptyMessageDelayed(MSG_LOAD_COMPLETE, DELAY_LOAD_COMPLETE);
} else {
mStatus = STATUS_LOAD;
if (mRecyclerChangeListener != null) {
mRecyclerChangeListener.onLoadMore();
mCurrentPage++;
}
}
}
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (mLayoutManager instanceof LinearLayoutManager) {
mLastVisibleItem = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
mFirstVisibleItem = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();
setFootviewVisible();
} else if (mLayoutManager instanceof GridLayoutManager) {
mLastVisibleItem = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
mFirstVisibleItem = ((GridLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();
setFootviewVisible();
} else if (mLayoutManager instanceof StaggeredGridLayoutManager) {
//  StaggeredGridLayoutManager             item    ,            
//             position             position  
int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()];
((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions);
mLastVisibleItem = findMax(lastPositions);
mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0];
setFootviewVisible();
}
}
};
void setFootviewVisible() {
//                         Recyclerview     footer
if (mHasFooter && mFirstVisibleItem == 0) {
/**
*       mShowFootVisible               ,    item      
* recyclerview       footview, item    view        
*/
if (mLastVisibleItem + 1 == mAdapter.getItemCount()) {
mShowFootVisible = false;
} else mShowFootVisible = true;
notifyDataSetChanged();
}
}
private int findMax(int[] positions) {
int max = positions[0];
for (int value : positions) {
if (value > max) {
max = value;
}
}
return max;
}
private class AdapterWrapper extends RecyclerView.Adapter {
private static final int TYPE_ITEM = 0;
private static final int TYPE_FOOTER = 1;
private RecyclerView.Adapter mAdapter;
private Context mContext;
View footer;
public AdapterWrapper(Context context, RecyclerView.Adapter wrappedAdapter) {
this.mContext = context;
this.mAdapter = wrappedAdapter;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = null;
switch (viewType) {
case TYPE_ITEM:
holder = mAdapter.onCreateViewHolder(parent, viewType);
break;
case TYPE_FOOTER:
footer = LayoutInflater.from(mContext).inflate(R.layout.lib_recyle_footview, null);
LinearLayout linearLayout= (LinearLayout) footer.findViewById(R.id.loading_layout);
StaggeredGridLayoutManager.LayoutParams layoutParams =
new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setFullSpan(true);
linearLayout.setLayoutParams(layoutParams);
holder = new FooterViewHolder(footer);
break;
}
return holder;
}
public View getFootView() {
return footer;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!mHasFooter || position + 1 != getItemCount()) {
mAdapter.onBindViewHolder(holder, position);
}
}
@Override
public int getItemCount() {
return mShowFootVisible ? mAdapter.getItemCount() + 1 : mAdapter.getItemCount();
}
@Override
public int getItemViewType(int position) {
if (mShowFootVisible && position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
private class FooterViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public TextView tvLoading;
public LinearLayout llyLoading;
public FooterViewHolder(View itemView) {
super(itemView);
progressBar = (ProgressBar) itemView.findViewById(R.id.progress_loading);
tvLoading = (TextView) itemView.findViewById(R.id.footer_tips);
llyLoading = (LinearLayout) itemView.findViewById(R.id.loading_layout);
}
}
}
}
最後にもう一つがAdapterのコードです。

package com.krain.srecyclerview.srecyclerview;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
public abstract class BaseRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
private OnItemClickLisener mItemListener;
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
View view = getItemView(viewType, parent);
VH vh = getViewHolder(view);
view.setOnClickListener(new OnRecyclerAdapterclickListener(vh, viewType));
view.setOnLongClickListener(new OnRecyclerAdapterclickListener(vh, viewType));
return vh;
}
public abstract VH getViewHolder(View itemView);
/**
*   item view
*
* @return
*/
public abstract View getItemView(int viewType, ViewGroup parent);
/**
*   Adapter  itemn      
*/
public Object getItem(int position) {
return null;
}
/**
* item      
*
* @param mItemListener
*/
public void setOnItemListener(OnItemClickLisener mItemListener) {
this.mItemListener = mItemListener;
}
@Override
public abstract void onBindViewHolder(VH holder, int position);
@Override
public abstract int getItemCount();
class OnRecyclerAdapterclickListener implements View.OnClickListener, View.OnLongClickListener {
VH viewholder;
int viewType;
public OnRecyclerAdapterclickListener(VH viewholder, int viewType) {
this.viewholder = viewholder;
this.viewType = viewType;
}
@Override
public void onClick(View v) {
if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) {
mItemListener.onItemClick(viewholder.getAdapterPosition(), viewType, viewholder, v);
}
}
@Override
public boolean onLongClick(View v) {
if (mItemListener != null && viewholder.getAdapterPosition() != RecyclerView.NO_POSITION) {
mItemListener.onItemLongClick(viewholder.getAdapterPosition(), viewType, viewholder, v);
}
return false;
}
}
}
注意してください。
Reciviewのレイアウトを変えたいなら、ここで修正します。

void addChildView(Context contex) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mHeaderView = new ElizabethView(context);
mRecyclerView = new RecyclerView(context);
addView(mHeaderView);
addView(mRecyclerView);
//mLayoutManager = new LinearLayoutManager(context);
mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
//   Item  、      
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addOnScrollListener(onScrollListener);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutParams(params);
}
mLayoutManager=new Stagg eredGrade youtManager(2,Stage Gred LayoutManager.VERRTICAL)この行は対応に変更すればいいです。ここでは何日か包装したことがありません。
もう一つ注意したいのは、スライドで最後のラインかどうかを判断することです。滝の流れなら、他の方法とは違って判断することに注意してください。

if (mLayoutManager instanceof StaggeredGridLayoutManager) {
//  StaggeredGridLayoutManager             item    ,            
//             position             position  
int[] lastPositions = new int[((StaggeredGridLayoutManager) mLayoutManager).getSpanCount()];
((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(lastPositions);
mLastVisibleItem = findMax(lastPositions);
mFirstVisibleItem = ((StaggeredGridLayoutManager) mLayoutManager).findFirstVisibleItemPositions(lastPositions)[0];
setFootviewVisible();
}
StaggredGrade LayoutManagerの特殊性により、最後に表示されるitemが複数存在する可能性がありますので、ここで取ったのは配列です。この配列を得てから、配列の中のpositionの値を一番大きいのは最後に表示されたpositionの値です。最後の行を設定して、より多くの表示をロードします。
以上は小编が绍介したAndroid RecyclerViewで、より多くのダウンロードと更新机能の実现方法を绍介しました。皆さんの助けになりたいです。もし何か疑问があれば、メッセージをください。