Android仿XListView支持下拉刷新和上劃加載更多的自定義RecyclerView
首先給大家展示下效果圖,感覺還不錯(cuò),請(qǐng)繼續(xù)往下閱讀:
下拉刷新:
上劃加載
在項(xiàng)目更新的過程中,遇到了一個(gè)將XListView換成recyclerView的需求,而且更換完之后大體效果不能變,但是對(duì)于下拉刷新這樣的效果,谷歌給出的解決方案是把RecyclerView放在一個(gè)SwipeRefreshLayout中,但是這樣其實(shí)是拉下一個(gè)小圓形控件實(shí)現(xiàn)的,和XListView的header效果不同。在網(wǎng)上找了很多的別人代碼,都沒有實(shí)現(xiàn)我想要的效果,于是自己動(dòng)手寫了一個(gè)。
具體實(shí)現(xiàn)的效果有以下幾條
下拉刷新功能:
1、實(shí)現(xiàn)一個(gè)有彈性的拖出效果:思路參考XListView,recyclerView的position=0的位置放一個(gè)header布局,這個(gè)布局的margin top默認(rèn)為負(fù)的布局高度,所以這塊布局就一直處于屏幕外部,在下拉的時(shí)候通過onTouchListener根據(jù)手指的移動(dòng)動(dòng)態(tài)修改margin top,慢慢的拖出,當(dāng)拖出的距離也就是margin top變?yōu)檎龜?shù)以后,就蓋面header布局的狀態(tài),改變箭頭的方向并改變提示語
2、實(shí)現(xiàn)有彈性的回彈效果:用timerTask寫了一個(gè)動(dòng)態(tài)修改的header布局的margin top的動(dòng)畫,每隔一定的時(shí)間減小margin top的值,當(dāng)用戶松手的時(shí)候通過一個(gè)函數(shù)updateHeaderHeight()來執(zhí)行這個(gè)動(dòng)畫。
3、實(shí)現(xiàn)用戶非手動(dòng)拖拉的自動(dòng)刷新效果:這個(gè)recyclerView還有一個(gè)方法叫forceRefresh(),就是不需要用戶手動(dòng)下拉,頭部自己滾動(dòng)出來,然后刷新完再自己收回去,自動(dòng)下拉也是用一個(gè)timerTask每隔十幾毫秒增加margin top的值讓頭部慢慢露出來
上劃加載更多功能:
1、實(shí)現(xiàn)滾動(dòng)到底部自動(dòng)停住效果: 有時(shí)候recyclerVIew滾動(dòng)太快,滾到底部的時(shí)候會(huì)根據(jù)慣性向上飄,這個(gè)地方到底的時(shí)候監(jiān)控recyclerView滾動(dòng)速度,如果非??煺f明是慣性滾動(dòng),就不修改footer布局的高度
2、實(shí)現(xiàn)向上拖動(dòng)效果:復(fù)寫了recyclerView的onScrollListener,在手指向上滾動(dòng)的時(shí)候,通過updateFooterHeight()方法動(dòng)態(tài)修改底部footerView的margin bottom,同headerView一樣,在手指移動(dòng)的時(shí)候讓這個(gè)margin跟著變大,以增加footer布局的高度,而且手指移動(dòng)的越網(wǎng)上,增加的margin的高度就越小,實(shí)現(xiàn)一個(gè)有彈性的上拉效果,防止誤操作。
3、實(shí)現(xiàn)自動(dòng)回彈的效果:通過監(jiān)控footer布局的margin bottom來確定松手的時(shí)候是否需要開始刷新,如果margin bottom大于某個(gè)值得時(shí)候就修改footer布局的狀態(tài)從normal變成ready,在ready狀態(tài)下松手就開始刷新操作,回彈也像header布局一樣通過一個(gè)timerTask每隔十幾毫秒修改margin的大小來實(shí)現(xiàn)回彈效果
注意事項(xiàng):
1、為了實(shí)現(xiàn)頭部和底部的代碼分離,頭部用的是onTouchListener,底部用的是onScrollListener
2、本recyclerVIew里面已經(jīng)內(nèi)置了一個(gè)layoutManager,所以不要給recyclerView再設(shè)置layoutManager,否則會(huì)出現(xiàn)頭部不出來,下拉報(bào)空指針的情況,底部出現(xiàn)但是滑動(dòng)沒有效果
3、這個(gè)recyclerView內(nèi)置了一個(gè)抽象類作為adapter,請(qǐng)繼承這個(gè)內(nèi)置的AlxDragRecyclerViewAdapter使用,或者按照這里面的邏輯重新寫adapter
有其他的問題歡迎問我
4、一些常用的功能,比如設(shè)置該控件是否能夠支持下拉加載和上拉刷新,等等api接口,請(qǐng)直接參考XListView的用法即可
使用方法:
繼承AlxDragRecyclerViewAdapter寫一個(gè)adapter,然后寫兩個(gè)類分別實(shí)現(xiàn)OnRefreshListener和LoadMoreListener,把具體的刷新邏輯寫在里面,在準(zhǔn)備好顯示數(shù)據(jù)后調(diào)用adapter的notifyDataSetChanged()方法或notifyItemInserted()方法,并執(zhí)行該recyclerView的stopLoadmore()方法和stopRefresh()方法。
下面是代碼,這個(gè)控件有很多的內(nèi)部類,包括頭部,底部的布局控件(CustomDragHeaderView,CustomDragFooterView),adapter,layoutmanager都已經(jīng)以內(nèi)部類的方式集成在里面,減少遷移時(shí)候的復(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;//如果是因?yàn)閼T性向下迅速的俯沖 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,請(qǐng)勿設(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", "開始慣性移動(dòng)"); 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) {//如果到了最后一個(gè) isBottom = true; mFooterView = (CustomDragRecyclerFooterView)layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());//一開始還不能hide,因?yàn)閔ide得到最后一個(gè)可見的就不是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();//這里的測(cè)量一般不會(huì)出問題 // Log.i("Alex2", "底部高度為" + footerHeight); } updateFooterHeight(dy); }else if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad){//如果到了倒數(shù)第二個(gè) 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í)設(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(); } } /** * 在刷新時(shí)要執(zhí)行的方法 */ public interface LoadMoreListener{ public void onLoadMore(); } /** * 點(diǎn)擊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());//自高一般不會(huì)算錯(cuò) // 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) {//在刷新的時(shí)候不把margin設(shè)為負(fù)值以在慣性滑動(dòng)的時(shí)候能滑回去 this.scrollBy(0, (int) delta);//禁止既滾動(dòng),又同時(shí)減少觸摸 // 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); } /** * 在用戶非手動(dòng)強(qiáng)制刷新的時(shí)候,通過一個(gè)動(dòng)畫把頭部一點(diǎn)點(diǎn)冒出來 */ private void smoothShowHeader(){ if(mHeaderView==null)return; // if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完畢的時(shí)候用戶沒有注視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)完全縮回去了,但是動(dòng)畫還沒有結(jié)束,就結(jié)束掉動(dòng)畫 timer.cancel(); } } }; timer = new Timer(); timer.scheduleAtFixedRate(timerTask,0,16); } /** * 在用戶松手的時(shí)候讓頭部自動(dòng)收縮回去 */ private void resetHeaderHeight() { if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0); if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完畢的時(shí)候用戶沒有注視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)完全縮回去了,但是動(dòng)畫還沒有結(jié)束,就結(jié)束掉動(dòng)畫 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); } /** * 因?yàn)樵O(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); } } /** * 停止下拉刷新,并且通過動(dòng)畫讓頭部自己縮回去 */ public void stopRefresh() { if (mIsRefreshing == true) { mIsRefreshing = false; mIsHeaderReady = false; if(mHeaderView==null)return; mHeaderView.setState(CustomDragFooterView.STATE_NORMAL); resetHeaderHeight(); } } /** * 在用戶沒有用手控制的情況下,通過動(dòng)畫把頭部露出來并且執(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; /** * 得到這個(gè)headerView真實(shí)的高度,而且這個(gè)高度是自己定的 * @return */ public int getRealHeight(){ return realHeight; } private void initView(Context context) { mContext = context; this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));//recyclerView里不加這句話的話寬度就會(huì)比較窄 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;//手動(dòng)設(shè)置高度,如果要手動(dòng)加載更多的時(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);//不知道為什么這個(gè)文字總會(huì)向上偏一下,所以要補(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就會(huì)抓不到 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);//一直會(huì)顯示轉(zhuǎn)圈,自動(dòng)加載更多時(shí)使用 } } /** * 為了防止代碼上的混亂,使用這個(gè)recyclerView自己內(nèi)置的一個(gè)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();//第一次初始化顯示的時(shí)候要不要顯示footerView } } @Override public int getItemCount() { return (dataList==null ||dataList.size()==0)?1:dataList.size() + 2;//如果有header,若list不存在或大小為0就沒有footView,反之則有 }//這里要考慮到頭尾部,多以要加2 /** * 根據(jù)位置判斷這里該用哪個(gè)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實(shí)現(xiàn)上拉加載下拉刷新功能
- Android XListView下拉刷新和上拉加載更多
- Android下拉刷新完全解析,教你如何一分鐘實(shí)現(xiàn)下拉刷新功能(附源碼)
- Android下拉刷新ListView——RTPullListView(demo)
- Android PullToRefreshLayout下拉刷新控件的終結(jié)者
- Android中使用RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android下拉刷新上拉加載控件(適用于所有View)
- Android官方下拉刷新控件SwipeRefreshLayout使用詳解
- Android RecyclerView實(shí)現(xiàn)下拉刷新和上拉加載
- Android巧用XListView實(shí)現(xiàn)萬能下拉刷新控件
相關(guān)文章
Android入門之實(shí)現(xiàn)手工發(fā)送一個(gè)BroadCast
這篇文章主要通過手工來發(fā)送一條BroadCast進(jìn)一步來帶大家深入了解BroadCast,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定幫助,感興趣的可以收藏一下2022-12-12Android模擬器安裝APP出現(xiàn)INSTALL_FAILED_NO_MATCHING_ABIS錯(cuò)誤解決方案
這篇文章主要介紹了 Android模擬器安裝APP出現(xiàn)INSTALL_FAILED_NO_MATCHING_ABIS錯(cuò)誤解決方案的相關(guān)資料,需要的朋友可以參考下2016-12-12Android實(shí)現(xiàn)常見的驗(yàn)證碼輸入框?qū)嵗a
我們?cè)陂_發(fā)APP的時(shí)候經(jīng)常要遇到輸入框,下面這篇文章主要給大家介紹了關(guān)于利用Android如何實(shí)現(xiàn)常見的驗(yàn)證碼輸入框的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)下吧。2017-09-09Android如何動(dòng)態(tài)調(diào)整應(yīng)用字體大小詳解
這篇文章主要給大家介紹了關(guān)于Android如何動(dòng)態(tài)調(diào)整應(yīng)用字體大小的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05Android Service中使用Toast無法正常顯示問題的解決方法
這篇文章主要介紹了Android Service中使用Toast無法正常顯示問題的解決方法,分析了Service中Toast無法正常顯示的原因與相關(guān)的解決方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10Android Tween動(dòng)畫之RotateAnimation實(shí)現(xiàn)圖片不停旋轉(zhuǎn)效果實(shí)例介紹
Android中如何使用rotate實(shí)現(xiàn)圖片不停旋轉(zhuǎn)的效果,下面與大家共同分析下Tween動(dòng)畫的rotate實(shí)現(xiàn)旋轉(zhuǎn)效果,感興趣的朋友可以參考下哈2013-05-05Android使用WebView實(shí)現(xiàn)離線閱讀功能
這篇文章主要介紹了Android使用WebView實(shí)現(xiàn)離線閱讀功能,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-04-04Android組件創(chuàng)建DrawerLayout導(dǎo)航
這篇文章主要為大家詳細(xì)介紹了Android組件創(chuàng)建DrawerLayout導(dǎo)航的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01