Android仿XListView支持下拉刷新和上劃加載更多的自定義RecyclerView
首先給大家展示下效果圖,感覺還不錯,請繼續(xù)往下閱讀:
下拉刷新:


上劃加載

在項目更新的過程中,遇到了一個將XListView換成recyclerView的需求,而且更換完之后大體效果不能變,但是對于下拉刷新這樣的效果,谷歌給出的解決方案是把RecyclerView放在一個SwipeRefreshLayout中,但是這樣其實是拉下一個小圓形控件實現(xiàn)的,和XListView的header效果不同。在網(wǎng)上找了很多的別人代碼,都沒有實現(xiàn)我想要的效果,于是自己動手寫了一個。
具體實現(xiàn)的效果有以下幾條
下拉刷新功能:
1、實現(xiàn)一個有彈性的拖出效果:思路參考XListView,recyclerView的position=0的位置放一個header布局,這個布局的margin top默認(rèn)為負(fù)的布局高度,所以這塊布局就一直處于屏幕外部,在下拉的時候通過onTouchListener根據(jù)手指的移動動態(tài)修改margin top,慢慢的拖出,當(dāng)拖出的距離也就是margin top變?yōu)檎龜?shù)以后,就蓋面header布局的狀態(tài),改變箭頭的方向并改變提示語
2、實現(xiàn)有彈性的回彈效果:用timerTask寫了一個動態(tài)修改的header布局的margin top的動畫,每隔一定的時間減小margin top的值,當(dāng)用戶松手的時候通過一個函數(shù)updateHeaderHeight()來執(zhí)行這個動畫。
3、實現(xiàn)用戶非手動拖拉的自動刷新效果:這個recyclerView還有一個方法叫forceRefresh(),就是不需要用戶手動下拉,頭部自己滾動出來,然后刷新完再自己收回去,自動下拉也是用一個timerTask每隔十幾毫秒增加margin top的值讓頭部慢慢露出來
上劃加載更多功能:
1、實現(xiàn)滾動到底部自動停住效果: 有時候recyclerVIew滾動太快,滾到底部的時候會根據(jù)慣性向上飄,這個地方到底的時候監(jiān)控recyclerView滾動速度,如果非??煺f明是慣性滾動,就不修改footer布局的高度
2、實現(xiàn)向上拖動效果:復(fù)寫了recyclerView的onScrollListener,在手指向上滾動的時候,通過updateFooterHeight()方法動態(tài)修改底部footerView的margin bottom,同headerView一樣,在手指移動的時候讓這個margin跟著變大,以增加footer布局的高度,而且手指移動的越網(wǎng)上,增加的margin的高度就越小,實現(xiàn)一個有彈性的上拉效果,防止誤操作。
3、實現(xiàn)自動回彈的效果:通過監(jiān)控footer布局的margin bottom來確定松手的時候是否需要開始刷新,如果margin bottom大于某個值得時候就修改footer布局的狀態(tài)從normal變成ready,在ready狀態(tài)下松手就開始刷新操作,回彈也像header布局一樣通過一個timerTask每隔十幾毫秒修改margin的大小來實現(xiàn)回彈效果
注意事項:
1、為了實現(xiàn)頭部和底部的代碼分離,頭部用的是onTouchListener,底部用的是onScrollListener
2、本recyclerVIew里面已經(jīng)內(nèi)置了一個layoutManager,所以不要給recyclerView再設(shè)置layoutManager,否則會出現(xiàn)頭部不出來,下拉報空指針的情況,底部出現(xiàn)但是滑動沒有效果
3、這個recyclerView內(nèi)置了一個抽象類作為adapter,請繼承這個內(nèi)置的AlxDragRecyclerViewAdapter使用,或者按照這里面的邏輯重新寫adapter
有其他的問題歡迎問我
4、一些常用的功能,比如設(shè)置該控件是否能夠支持下拉加載和上拉刷新,等等api接口,請直接參考XListView的用法即可
使用方法:
繼承AlxDragRecyclerViewAdapter寫一個adapter,然后寫兩個類分別實現(xiàn)OnRefreshListener和LoadMoreListener,把具體的刷新邏輯寫在里面,在準(zhǔn)備好顯示數(shù)據(jù)后調(diào)用adapter的notifyDataSetChanged()方法或notifyItemInserted()方法,并執(zhí)行該recyclerView的stopLoadmore()方法和stopRefresh()方法。
下面是代碼,這個控件有很多的內(nèi)部類,包括頭部,底部的布局控件(CustomDragHeaderView,CustomDragFooterView),adapter,layoutmanager都已經(jīng)以內(nèi)部類的方式集成在里面,減少遷移時候的復(fù)雜度
import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.xxx.app.R;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Alex on 2016/1/27.
*/
public class AlxRefreshLoadMoreRecyclerView extends RecyclerView {
private int footerHeight = -1;
LinearLayoutManager layoutManager;
// -- footer view
private CustomDragRecyclerFooterView mFooterView;
private boolean mEnablePullLoad;
private boolean mPullLoading;
private boolean isBottom;
private boolean mIsFooterReady = false;
private LoadMoreListener loadMoreListener;
// -- header view
private CustomDragHeaderView mHeaderView;
private boolean mEnablePullRefresh = true;
private boolean mIsRefreshing;
private boolean isHeader;
private boolean mIsHeaderReady = false;
private Timer timer;
private float oldY;
Handler handler = new Handler();
private OnRefreshListener refreshListener;
private AlxDragRecyclerViewAdapter adapter;
private int maxPullHeight = 50;//最多下拉高度的px值
private static final int HEADER_HEIGHT = 68;//頭部高度68dp
private static final int MAX_PULL_LENGTH = 150;//最多下拉150dp
private OnClickListener footerClickListener;
public AlxRefreshLoadMoreRecyclerView(Context context) {
super(context);
initView(context);
}
public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
public void setAdapter(AlxDragRecyclerViewAdapter adapter){
super.setAdapter(adapter);
this.adapter = adapter;
}
public boolean ismPullLoading() {
return mPullLoading;
}
public boolean ismIsRefreshing() {
return mIsRefreshing;
}
private void updateFooterHeight(float delta) {
if(mFooterView==null)return;
int bottomMargin = mFooterView.getBottomMargin();
// Log.i("Alex3","初始delta是"+delta);
if(delta>50)delta = delta/6;
if(delta>0) {//越往下滑越難滑
if(bottomMargin>maxPullHeight)delta = delta*0.65f;
else if(bottomMargin>maxPullHeight * 0.83333f)delta = delta*0.7f;
else if(bottomMargin>maxPullHeight * 0.66667f)delta = delta*0.75f;
else if(bottomMargin>maxPullHeight >> 1)delta = delta*0.8f;
else if(bottomMargin>maxPullHeight * 0.33333f)delta = delta*0.85f;
else if(bottomMargin>maxPullHeight * 0.16667F && delta > 20)delta = delta*0.2f;//如果是因為慣性向下迅速的俯沖
else if(bottomMargin>maxPullHeight * 0.16667F)delta = delta*0.9f;
// Log.i("Alex3","bottomMargin是"+mFooterView.getBottomMargin()+" delta是"+delta);
}
int height = mFooterView.getBottomMargin() + (int) (delta+0.5);
if (mEnablePullLoad && !mPullLoading) {
if (height > 150){//必須拉超過一定距離才加載更多
// if (height > 1){//立即刷新
mFooterView.setState(CustomDragRecyclerFooterView.STATE_READY);
mIsFooterReady = true;
// Log.i("Alex2", "ready");
} else {
mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);
mIsFooterReady = false;
// Log.i("Alex2", "nomal");
}
}
mFooterView.setBottomMargin(height);
}
private void resetFooterHeight() {
int bottomMargin = mFooterView.getBottomMargin();
if (bottomMargin > 20) {
Log.i("Alex2", "準(zhǔn)備重置高度,margin是" + bottomMargin + "自高是" + footerHeight);
this.smoothScrollBy(0,-bottomMargin);
//一松手就立即開始加載
if(mIsFooterReady){
startLoadMore();
}
}
}
public void setLoadMoreListener(LoadMoreListener listener){
this.loadMoreListener = listener;
}
public void initView(Context context){
layoutManager = new LinearLayoutManager(context);//自帶layoutManager,請勿設(shè)置
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
int height = wm.getDefaultDisplay().getHeight();
layoutManager.offsetChildrenVertical(height*2);//預(yù)加載2/3的卡片
this.setLayoutManager(layoutManager);
// Log.i("Alex", "屏幕密度為" + getContext().getResources().getDisplayMetrics().density);
maxPullHeight = dp2px(getContext().getResources().getDisplayMetrics().density,MAX_PULL_LENGTH);//最多下拉150dp
this.footerClickListener = new footerViewClickListener();
this.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState){
case RecyclerView.SCROLL_STATE_IDLE:
// Log.i("Alex2", "停下了||放手了");
if(isBottom)resetFooterHeight();
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
// Log.i("Alex2", "開始拖了,現(xiàn)在margin是" + (mFooterView == null ? "" : mFooterView.getBottomMargin()));
break;
case RecyclerView.SCROLL_STATE_SETTLING:
// Log.i("Alex2", "開始慣性移動");
break;
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int lastItemPosition = layoutManager.findLastVisibleItemPosition();
// Log.i("Alex2","mEnable是"+mEnablePullLoad+"lastitemPosition是"+lastItemPosition+" itemcount是"+layoutManager.getItemCount());
if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad) {//如果到了最后一個
isBottom = true;
mFooterView = (CustomDragRecyclerFooterView)layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());//一開始還不能hide,因為hide得到最后一個可見的就不是footerview了
// Log.i("Alex2","到底啦?。?+"mfooterView是"+mFooterView);
if(mFooterView!=null) mFooterView.setOnClickListener(footerClickListener);
if(footerHeight==-1 && mFooterView!=null){
mFooterView.show();
mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);
footerHeight = mFooterView.getMeasuredHeight();//這里的測量一般不會出問題
// Log.i("Alex2", "底部高度為" + footerHeight);
}
updateFooterHeight(dy);
}else if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad){//如果到了倒數(shù)第二個
startLoadMore();//開始加載更多
}
else {
isBottom = false;
}
}
});
}
/**
* 設(shè)置是否開啟上拉加載更多的功能
*
* @param enable
*/
public void setPullLoadEnable(boolean enable) {
mPullLoading = false;
mEnablePullLoad = enable;
if(adapter!=null)adapter.setPullLoadMoreEnable(enable);//adapter和recyclerView要同時設(shè)置
if(mFooterView==null)return;
if (!mEnablePullLoad) {
// this.smoothScrollBy(0,-footerHeight);
mFooterView.hide();
mFooterView.setOnClickListener(null);
mFooterView.setBottomMargin(0);
//make sure "pull up" don't show a line in bottom when listview with one page
} else {
mFooterView.show();
mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);
mFooterView.setVisibility(VISIBLE);
//make sure "pull up" don't show a line in bottom when listview with one page
// both "pull up" and "click" will invoke load more.
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startLoadMore();
}
});
}
}
/**
* 停止loadmore
*/
public void stopLoadMore() {
if (mPullLoading == true) {
mPullLoading = false;
if(mFooterView==null)return;
mFooterView.show();
mFooterView.setState(CustomDragRecyclerFooterView.STATE_ERROR);
}
}
private void startLoadMore() {
if(mPullLoading)return;
mPullLoading = true;
if(mFooterView!=null)mFooterView.setState(CustomDragRecyclerFooterView.STATE_LOADING);
Log.i("Alex2", "現(xiàn)在開始加載");
mIsFooterReady = false;
if (loadMoreListener != null) {
loadMoreListener.onLoadMore();
}
}
/**
* 在刷新時要執(zhí)行的方法
*/
public interface LoadMoreListener{
public void onLoadMore();
}
/**
* 點擊loadMore后要執(zhí)行的事件
*/
class footerViewClickListener implements OnClickListener {
@Override
public void onClick(View v) {
startLoadMore();
}
}
private void updateHeaderHeight(float delta) {
mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);
// Log.i("Alex2", "現(xiàn)在在頭部?。。?! header自高是" + mHeaderView.getHeight() + " margin 是" + mHeaderView.getTopMargin());//自高一般不會算錯
// Log.i("Alex2", "正在設(shè)置margin" + mHeaderView.getTopMargin() +"delta是"+delta);
if(delta>0){//如果是往下拉
int topMargin = mHeaderView.getTopMargin();
if(topMargin>maxPullHeight * 0.33333f)delta = delta*0.5f;
else if(topMargin>maxPullHeight * 0.16667F)delta = delta*0.55f;
else if(topMargin>0)delta = delta*0.6f;
else if(topMargin<0)delta = delta*0.6f;//如果沒有被完全拖出來
mHeaderView.setTopMargin(mHeaderView.getTopMargin() + (int)delta);
} else{//如果是推回去
if(!mIsRefreshing || mHeaderView.getTopMargin()>0) {//在刷新的時候不把margin設(shè)為負(fù)值以在慣性滑動的時候能滑回去
this.scrollBy(0, (int) delta);//禁止既滾動,又同時減少觸摸
// Log.i("Alex2", "正在往回推" + delta);
mHeaderView.setTopMargin(mHeaderView.getTopMargin() + (int) delta);
}
}
if(mHeaderView.getTopMargin()>0 && !mIsRefreshing){
mIsHeaderReady = true;
mHeaderView.setState(CustomDragHeaderView.STATE_READY);
}//設(shè)置為ready狀態(tài)
else if(!mIsRefreshing){
mIsHeaderReady = false;
mHeaderView.setState(CustomDragHeaderView.STATE_NORMAL);
}//設(shè)置為普通狀態(tài)并且縮回去
}
@Override
public void smoothScrollToPosition(final int position) {
super.smoothScrollToPosition(position);
final Timer scrollTimer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
int bottomCardPosition = layoutManager.findLastVisibleItemPosition();
if(bottomCardPosition<position+1){//如果要向下滾
handler.post(new Runnable() {
@Override
public void run() {
smoothScrollBy(0,50);
}
});
}else if(bottomCardPosition>position){//如果要向上滾
handler.post(new Runnable() {
@Override
public void run() {
smoothScrollBy(0,-50);
}
});
}else {
if(scrollTimer!=null)scrollTimer.cancel();
}
}
};
scrollTimer.schedule(timerTask,0,20);
}
/**
* 在用戶非手動強(qiáng)制刷新的時候,通過一個動畫把頭部一點點冒出來
*/
private void smoothShowHeader(){
if(mHeaderView==null)return;
// if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完畢的時候用戶沒有注視header
// mHeaderView.setTopMargin(0);
// return;
// }
if(timer!=null)timer.cancel();
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if(mHeaderView==null){
if(timer!=null)timer.cancel();
return;
}
// Log.i("Alex2","topMargin是"+mHeaderView.getTopMargin()+" height是"+mHeaderView.getHeight());
if(mHeaderView.getTopMargin()<0){
handler.post(new Runnable() {
@Override
public void run() {
if (mIsRefreshing) {//如果目前是ready狀態(tài)或者正在刷新狀態(tài)
mHeaderView.setTopMargin(mHeaderView.getTopMargin() +2);
}
}
});
} else if(timer!=null){//如果已經(jīng)完全縮回去了,但是動畫還沒有結(jié)束,就結(jié)束掉動畫
timer.cancel();
}
}
};
timer = new Timer();
timer.scheduleAtFixedRate(timerTask,0,16);
}
/**
* 在用戶松手的時候讓頭部自動收縮回去
*/
private void resetHeaderHeight() {
if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);
if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完畢的時候用戶沒有注視header
mHeaderView.setTopMargin(-mHeaderView.getRealHeight());
return;
}
if(timer!=null)timer.cancel();
final TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if(mHeaderView==null)return;
// Log.i("Alex2","topMargin是"+mHeaderView.getTopMargin()+" height是"+mHeaderView.getHeight());
if(mHeaderView.getTopMargin()>-mHeaderView.getRealHeight()){//如果header沒有完全縮回去
handler.post(new Runnable() {
@Override
public void run() {
if (mIsHeaderReady || mIsRefreshing) {//如果目前是ready狀態(tài)或者正在刷新狀態(tài)
// Log.i("Alex2", "現(xiàn)在是ready狀態(tài)");
int delta = mHeaderView.getTopMargin() / 9;
if (delta < 5) delta = 5;
if (mHeaderView.getTopMargin() > 0)
mHeaderView.setTopMargin(mHeaderView.getTopMargin() - delta);
} else {//如果是普通狀態(tài)
// Log.i("Alex2", "現(xiàn)在是普通狀態(tài)");
mHeaderView.setTopMargin(mHeaderView.getTopMargin() - 5);
}
}
});
} else if(timer!=null){//如果已經(jīng)完全縮回去了,但是動畫還沒有結(jié)束,就結(jié)束掉動畫
timer.cancel();
handler.post(new Runnable() {
@Override
public void run() {
mHeaderView.setState(mHeaderView.STATE_FINISH);
}
});
}
}
};
timer = new Timer();
timer.scheduleAtFixedRate(timerTask,0,10);
}
/**
* 頭部是通過onTouchEvent控制的
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
int delta = (int)(event.getY()-oldY);
oldY = event.getY();
if (layoutManager.findViewByPosition(0) instanceof CustomDragHeaderView) {
isHeader = true;
updateHeaderHeight(delta);//更新margin高度
}else{
isHeader = false;
if(mHeaderView!=null && !mIsRefreshing)mHeaderView.setTopMargin(-mHeaderView.getRealHeight());
}
break;
// case MotionEvent.ACTION_DOWN:
// Log.i("Alex", "touch down");
// oldY = event.getY();
// if(timer!=null)timer.cancel();
// break;
case MotionEvent.ACTION_UP:
// Log.i("Alex", "抬手啦!?。?! touch up ");
if(mIsHeaderReady && !mIsRefreshing)startRefresh();
if(isHeader)resetHeaderHeight();//抬手之后恢復(fù)高度
break;
case MotionEvent.ACTION_CANCEL:
// Log.i("Alex", "touch cancel");
break;
}
return super.onTouchEvent(event);
}
/**
* 因為設(shè)置了子元素的onclickListener之后,ontouch方法的down失效,所以要在分發(fā)前獲取手指的位置
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// Log.i("Alex", "touch down分發(fā)前");
oldY = ev.getY();
if (timer != null) timer.cancel();
break;
}
return super.dispatchTouchEvent(ev);
}
public void setOnRefreshListener(OnRefreshListener listener){
this.refreshListener = listener;
}
/**
* 設(shè)置是否支持下啦刷新的功能
*
* @param enable
*/
public void setPullRefreshEnable(boolean enable) {
mIsRefreshing = false;
mEnablePullRefresh = enable;
if(mHeaderView==null)return;
if (!mEnablePullRefresh) {
mHeaderView.setOnClickListener(null);
} else {
mHeaderView.setState(CustomDragFooterView.STATE_NORMAL);
mHeaderView.setVisibility(VISIBLE);
}
}
/**
* 停止下拉刷新,并且通過動畫讓頭部自己縮回去
*/
public void stopRefresh() {
if (mIsRefreshing == true) {
mIsRefreshing = false;
mIsHeaderReady = false;
if(mHeaderView==null)return;
mHeaderView.setState(CustomDragFooterView.STATE_NORMAL);
resetHeaderHeight();
}
}
/**
* 在用戶沒有用手控制的情況下,通過動畫把頭部露出來并且執(zhí)行刷新
*/
public void forceRefresh(){
if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);
if(mHeaderView!=null)mHeaderView.setState(CustomDragHeaderView.STATE_REFRESHING);
mIsRefreshing = true;
Log.i("Alex2", "現(xiàn)在開始強(qiáng)制刷新");
mIsHeaderReady = false;
smoothShowHeader();
if (refreshListener != null)refreshListener.onRefresh();
}
private void startRefresh() {
mIsRefreshing = true;
mHeaderView.setState(CustomDragHeaderView.STATE_REFRESHING);
Log.i("Alex2", "現(xiàn)在開始加載");
mIsHeaderReady = false;
if (refreshListener != null) refreshListener.onRefresh();
}
public interface OnRefreshListener{
public void onRefresh();
}
/**
* 適用于本recycler的頭部下拉刷新view
*/
public static class CustomDragHeaderView extends LinearLayout {
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_REFRESHING = 2;
public final static int STATE_FINISH = 3;
public float screenDensity;
private final int ROTATE_ANIM_DURATION = 180;
private Context mContext;
private View mContentView;
private View mProgressBar;
private ImageView mArrowImageView;
private TextView mHintTextView;
private Animation mRotateUpAnim;
private Animation mRotateDownAnim;
public CustomDragHeaderView(Context context) {
super(context);
initView(context);
}
public CustomDragHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private int mState;
public void setState(int state) {
if (state == mState)
return;
if (state == STATE_REFRESHING) { // 顯示進(jìn)度
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);
mHintTextView.setText(R.string.xlistview_header_hint_normal);
}
else if (mState == STATE_REFRESHING) {//如果是從刷新狀態(tài)過來
// mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(INVISIBLE);
mHintTextView.setText("load completed");
}
break;
case STATE_READY:
if (mState != STATE_READY) {
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mRotateUpAnim);
}
mHintTextView.setText(R.string.xlistview_header_hint_ready);
break;
case STATE_REFRESHING:
mHintTextView.setText(R.string.xlistview_header_hint_loading);
break;
case STATE_FINISH:
mArrowImageView.setVisibility(View.VISIBLE);
mHintTextView.setText(R.string.xlistview_header_hint_normal);
break;
default:
}
mState = state;
}
public void setTopMargin(int height) {
if (mContentView==null) return ;
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
lp.topMargin = height;
mContentView.setLayoutParams(lp);
}
//
public int getTopMargin() {
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
return lp.topMargin;
}
public void setHeight(int height){
if (mContentView==null) return ;
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
lp.height = height;
mContentView.setLayoutParams(lp);
}
private int realHeight;
/**
* 得到這個headerView真實的高度,而且這個高度是自己定的
* @return
*/
public int getRealHeight(){
return realHeight;
}
private void initView(Context context) {
mContext = context;
this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));//recyclerView里不加這句話的話寬度就會比較窄
LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.xlistview_header, null);
addView(moreView);
moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mContentView = moreView.findViewById(R.id.xlistview_header_content);
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
Log.i("Alex", "初始height是" + mContentView.getHeight());
lp.height = 150;//手動設(shè)置高度,如果要手動加載更多的時候才設(shè)置
screenDensity = getContext().getResources().getDisplayMetrics().density;//設(shè)置屏幕密度,用來px向dp轉(zhuǎn)化
lp.height = dp2px(screenDensity,HEADER_HEIGHT);//頭部高度75dp
realHeight = lp.height;
lp.topMargin = -lp.height;
mContentView.setLayoutParams(lp);
mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);
mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);
mHintTextView.setPadding(0,dp2px(screenDensity,3),0,0);//不知道為什么這個文字總會向上偏一下,所以要補(bǔ)回來
mProgressBar = findViewById(R.id.xlistview_header_progressbar);
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 static int dp2px(float density, int dp) {
if (dp == 0) {
return 0;
}
return (int) (dp * density + 0.5f);
}
public static class CustomDragRecyclerFooterView extends LinearLayout {
public final static int STATE_NORMAL = 0;
public final static int STATE_READY = 1;
public final static int STATE_LOADING = 2;
public final static int STATE_ERROR = 3;
private Context mContext;
private View mContentView;
private View mProgressBar;
private TextView mHintView;
public CustomDragRecyclerFooterView(Context context) {
super(context);
initView(context);
}
public CustomDragRecyclerFooterView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public void setState(int state) {
mProgressBar.setVisibility(View.INVISIBLE);
// mHintView.setVisibility(View.INVISIBLE);
if (state == STATE_READY) {
mHintView.setVisibility(View.VISIBLE);
mHintView.setText("松手加載更多");
} else if (state == STATE_LOADING) {
mProgressBar.setVisibility(View.VISIBLE);
mHintView.setVisibility(INVISIBLE);
} else if(state == STATE_ERROR){
mProgressBar.setVisibility(GONE);
mHintView.setVisibility(VISIBLE);
mHintView.setText(R.string.xlistview_footer_hint_ready);
}
else {
mHintView.setVisibility(View.VISIBLE);
mHintView.setText(R.string.xlistview_footer_hint_normal);
mHintView.setText("Load more");
}
}
public void setBottomMargin(int height) {
if (height < 0) return ;
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
lp.bottomMargin = height;
mContentView.setLayoutParams(lp);
}
public int getBottomMargin() {
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
return lp.bottomMargin;
}
/**
* normal status
*/
public void normal() {
mHintView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
/**
* loading status
*/
public void loading() {
mHintView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
}
/**
* hide footer when disable pull load more
*/
public void hide() {
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
lp.height = 1;//這里如果設(shè)為0那么layoutManger就會抓不到
mContentView.setLayoutParams(lp);
mContentView.setBackgroundColor(Color.BLACK);//這里的顏色要和自己的背景色一致
}
/**
* show footer
*/
public void show() {
LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
lp.height = LayoutParams.WRAP_CONTENT;
lp.width = LayoutParams.MATCH_PARENT;
mContentView.setLayoutParams(lp);
mContentView.setBackgroundColor(Color.WHITE);//這里的顏色要和自己的背景色一致
}
private void initView(Context context) {
mContext = context;
this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_customdragfooterview, null);
addView(moreView);
moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mContentView = moreView.findViewById(R.id.rlContentView);
mProgressBar = moreView.findViewById(R.id.pbContentView);
mHintView = (TextView)moreView.findViewById(R.id.ctvContentView);
mHintView.setText("load more");
// mProgressBar.setVisibility(VISIBLE);//一直會顯示轉(zhuǎn)圈,自動加載更多時使用
}
}
/**
* 為了防止代碼上的混亂,使用這個recyclerView自己內(nèi)置的一個adapter
* @param <T>
*/
public static abstract class AlxDragRecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
protected static final int TYPE_HEADER = 436874;
protected static final int TYPE_ITEM = 256478;
protected static final int TYPE_FOOTER = 9621147;
private int ITEM;
private ViewHolder vhItem;
protected boolean loadMore;
private List<T> dataList;
public List<T> getDataList() {
return dataList;
}
public void setDataList(List<T> dataList) {
this.dataList = dataList;
}
public AlxDragRecyclerViewAdapter(List<T> dataList,int itemLayout,boolean pullEnable){
this.dataList = dataList;
this.ITEM = itemLayout;
this.loadMore = pullEnable;
}
public abstract ViewHolder setItemViewHolder(View itemView);
private T getObject(int position){
if(dataList!=null && dataList.size()>=position)return dataList.get(position-1);//如果有header
return null;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
//inflate your layout and pass it to view holder
View itemView = LayoutInflater.from(parent.getContext()).inflate(ITEM,null);
Log.i("Alex","itemView是"+itemView);
this.vhItem = setItemViewHolder(itemView);
Log.i("Alex","vhItem是"+vhItem);
return vhItem;
} else if (viewType == TYPE_HEADER) {
//inflate your layout and pass it to view holder
View headerView = new CustomDragHeaderView(parent.getContext());
return new VHHeader(headerView);
} else if(viewType==TYPE_FOOTER){
CustomDragRecyclerFooterView footerView = new CustomDragRecyclerFooterView(parent.getContext());
return new VHFooter(footerView);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
public void setPullLoadMoreEnable(boolean enable){
this.loadMore = enable;
}
public boolean getPullLoadMoreEnable(){return loadMore;}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {//相當(dāng)于getView
Log.i("Alex","正在綁定"+position+" "+holder.getClass());
if (vhItem!=null && holder.getClass() == vhItem.getClass()) {
//cast holder to VHItem and set data
initItemView(holder,position,getObject(position));
}else if (holder instanceof AlxDragRecyclerViewAdapter.VHHeader) {
//cast holder to VHHeader and set data for header.
}else if(holder instanceof AlxDragRecyclerViewAdapter.VHFooter){
if(!loadMore)((VHFooter)holder).footerView.hide();//第一次初始化顯示的時候要不要顯示footerView
}
}
@Override
public int getItemCount() {
return (dataList==null ||dataList.size()==0)?1:dataList.size() + 2;//如果有header,若list不存在或大小為0就沒有footView,反之則有
}//這里要考慮到頭尾部,多以要加2
/**
* 根據(jù)位置判斷這里該用哪個ViewHolder
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
if (position == 0)return TYPE_HEADER;
else if(isPositonFooter(position))return TYPE_FOOTER;
return TYPE_ITEM;
}
protected boolean isPositonFooter(int position){//這里的position從0算起
if (dataList == null && position == 1) return true;//如果沒有item
return position == dataList.size() + 1;//如果有item(也許為0)
}
// class VHItem extends RecyclerView.ViewHolder {
// public VHItem(View itemView) {
// super(itemView);
// }
// public View getItemView(){return itemView;}
// }
//
protected class VHHeader extends RecyclerView.ViewHolder {
public VHHeader(View headerView) {
super(headerView);
}
}
protected class VHFooter extends RecyclerView.ViewHolder {
public CustomDragRecyclerFooterView footerView;
public VHFooter(View itemView) {
super(itemView);
footerView = (CustomDragRecyclerFooterView)itemView;
}
}
public abstract void initItemView(ViewHolder itemHolder,int posion,T entity);
}
}
- Android通過XListView實現(xiàn)上拉加載下拉刷新功能
- Android XListView下拉刷新和上拉加載更多
- Android下拉刷新完全解析,教你如何一分鐘實現(xiàn)下拉刷新功能(附源碼)
- Android下拉刷新ListView——RTPullListView(demo)
- Android PullToRefreshLayout下拉刷新控件的終結(jié)者
- Android中使用RecyclerView實現(xiàn)下拉刷新和上拉加載
- Android下拉刷新上拉加載控件(適用于所有View)
- Android官方下拉刷新控件SwipeRefreshLayout使用詳解
- Android RecyclerView實現(xiàn)下拉刷新和上拉加載
- Android巧用XListView實現(xiàn)萬能下拉刷新控件
相關(guān)文章
Android入門之實現(xiàn)手工發(fā)送一個BroadCast
這篇文章主要通過手工來發(fā)送一條BroadCast進(jìn)一步來帶大家深入了解BroadCast,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Android有一定幫助,感興趣的可以收藏一下2022-12-12
Android模擬器安裝APP出現(xiàn)INSTALL_FAILED_NO_MATCHING_ABIS錯誤解決方案
這篇文章主要介紹了 Android模擬器安裝APP出現(xiàn)INSTALL_FAILED_NO_MATCHING_ABIS錯誤解決方案的相關(guān)資料,需要的朋友可以參考下2016-12-12
Android實現(xiàn)常見的驗證碼輸入框?qū)嵗a
我們在開發(fā)APP的時候經(jīng)常要遇到輸入框,下面這篇文章主要給大家介紹了關(guān)于利用Android如何實現(xiàn)常見的驗證碼輸入框的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)下吧。2017-09-09
Android如何動態(tài)調(diào)整應(yīng)用字體大小詳解
這篇文章主要給大家介紹了關(guān)于Android如何動態(tài)調(diào)整應(yīng)用字體大小的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
Android Service中使用Toast無法正常顯示問題的解決方法
這篇文章主要介紹了Android Service中使用Toast無法正常顯示問題的解決方法,分析了Service中Toast無法正常顯示的原因與相關(guān)的解決方法,具有一定參考借鑒價值,需要的朋友可以參考下2016-10-10
Android Tween動畫之RotateAnimation實現(xiàn)圖片不停旋轉(zhuǎn)效果實例介紹
Android中如何使用rotate實現(xiàn)圖片不停旋轉(zhuǎn)的效果,下面與大家共同分析下Tween動畫的rotate實現(xiàn)旋轉(zhuǎn)效果,感興趣的朋友可以參考下哈2013-05-05
Android使用WebView實現(xiàn)離線閱讀功能
這篇文章主要介紹了Android使用WebView實現(xiàn)離線閱讀功能,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-04-04
Android組件創(chuàng)建DrawerLayout導(dǎo)航
這篇文章主要為大家詳細(xì)介紹了Android組件創(chuàng)建DrawerLayout導(dǎo)航的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01

