Java tutorial
/* * This source code is licensed under the MIT-style license found in the * LICENSE file in the root directory of this source tree. */ package com.netease.hearttouch.htrefreshrecyclerview.base; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.OrientationHelper; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import com.netease.hearttouch.htrefreshrecyclerview.HTLoadMoreListener; import com.netease.hearttouch.htrefreshrecyclerview.HTRecyclerViewDragListener; import com.netease.hearttouch.htrefreshrecyclerview.HTRefreshListener; import com.netease.hearttouch.htrefreshrecyclerview.R; import com.netease.hearttouch.htrefreshrecyclerview.viewimpl.HTDefaultHorizontalRefreshViewHolder; import com.netease.hearttouch.htrefreshrecyclerview.viewimpl.HTDefaultVerticalRefreshViewHolder; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import static android.widget.LinearLayout.HORIZONTAL; import static android.widget.LinearLayout.VERTICAL; import static com.netease.hearttouch.htrefreshrecyclerview.base.HTOrientation.VERTICAL_DOWN; import static com.netease.hearttouch.htrefreshrecyclerview.base.HTOrientation.VERTICAL_UP; /** * */ public abstract class HTBaseRecyclerView extends ViewGroup implements HTRefreshRecyclerViewInterface { private static final String TAG = HTBaseRecyclerView.class.getSimpleName(); /** * ? */ private static Class<? extends HTBaseViewHolder> sViewHolderClass; /** * ? */ public HTRefreshListener mRefreshDelegate; /** * ?? */ public HTLoadMoreListener mLoadMoreDelegate; /** * ? */ protected HTBaseViewHolder mHTViewHolder; protected HTViewHolderTracker mHTViewHolderTracker; protected RecyclerView.OnScrollListener mInnerScrollListener; /** * Adapter */ protected HTWrapperAdapter mHTWrapperAdapter; /** * Adapter */ protected RecyclerView.Adapter mInnerAdapter; /** * ???(????), */ protected boolean mLoadMoreViewDisplay = true; /** * view */ protected final ViewGroup mRefreshContainerView; /** * view */ protected final ViewGroup mLoadMoreContainerView; /** * ?RecyclerView */ protected final RecyclerView mRecyclerView; /** * ??? */ protected HTRefreshUIChangeListener mRefreshUIChangeListener; /** * ??? */ protected HTLoadMoreUIChangeListener mLoadMoreUIChangeListener; /** * ? */ protected HTRecyclerViewDragListener mRecyclerViewDragListener; /** * ? */ protected int mLoadMoreStatus = LoadMoreStatus.IDLE; /** * ? */ protected int mRefreshStatus = RefreshStatus.IDLE; /** * ?,?? */ protected int mHTOrientation = VERTICAL_DOWN; /** * ?? */ protected boolean mHasMore = true; /** * ? */ private final ArrayList<OnScrollListener> mScrollListeners = new ArrayList<>(); /** * ??? */ protected boolean mEnableScrollOnReFresh = false; protected final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); private Paint mRefreshBgPaint; public HTBaseRecyclerView(Context context) { this(context, null, 0); } public HTBaseRecyclerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HTBaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context); setWillNotDraw(false); //?? mRecyclerView = new RecyclerView(getContext(), attrs, defStyleAttr);//?attrsRecyclerView mRefreshContainerView = new FrameLayout(getContext()); mLoadMoreContainerView = new LinearLayout(getContext()); //?? initRefreshBgPaint(context); initAttrs(attrs); initViews(); } private void initRefreshBgPaint(Context context) { mRefreshBgPaint = new Paint(); mRefreshBgPaint.setColor(ContextCompat.getColor(context, android.R.color.transparent)); mRefreshBgPaint.setStyle(Paint.Style.FILL); mRefreshBgPaint.setAntiAlias(true); } private void initAttrs(AttributeSet attrs) { if (attrs == null) return; TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.HTRefreshRecyclerView); try { mHTOrientation = typedArray.getInteger(R.styleable.HTRefreshRecyclerView_htOrientation, 1); } finally { typedArray.recycle(); } } private void initViews() { //RecyclerView?,attrsRecyclerView,??? mRecyclerView.setId(View.NO_ID);//Id?attrs?? mRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);// //??? removeAllViews(); mRecyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setViewLayoutParams(mRefreshContainerView); setViewLayoutParams(mLoadMoreContainerView); addView(mRecyclerView); addView(mRefreshContainerView); //?,? if (sViewHolderClass != null) { try { Constructor constructor = sViewHolderClass.getConstructor(Context.class); try { setRefreshViewHolder((HTBaseViewHolder) constructor.newInstance(getContext())); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } else { HTBaseViewHolder viewHolder; if (checkOrientationVertical()) { viewHolder = new HTDefaultVerticalRefreshViewHolder(getContext()); ((HTDefaultVerticalRefreshViewHolder) viewHolder).setDefaultRefreshViewArrow(mHTOrientation); } else { viewHolder = new HTDefaultHorizontalRefreshViewHolder(getContext()); ((HTDefaultHorizontalRefreshViewHolder) viewHolder).setDefaultRefreshViewArrow(mHTOrientation); } setRefreshViewHolder(viewHolder);//? } } private void initListeners() { //RecyclerView?,?RecyclerViewbug // setRecyclerViewOnTouchListener(); //RecyclerView? setRecyclerViewOnScrollListener(); } /** * ? */ public static void setRefreshViewHolderClass(@NonNull Class<? extends HTBaseViewHolder> mViewHolderClass) { HTBaseRecyclerView.sViewHolderClass = mViewHolderClass; } /** * ? */ public void setRefreshViewHolder(@NonNull HTBaseViewHolder refreshViewHolder) { mHTViewHolder = refreshViewHolder; mHTViewHolderTracker = mHTViewHolder.getViewHolderTracker(); mHTViewHolderTracker.setOrientation(mHTOrientation); mHTViewHolder.computeViewSize(checkOrientationVertical() ? VERTICAL : HORIZONTAL); resetRefreshViewHolderView(); initRefreshView(); initLoadMoreView(); } private void resetRefreshViewHolderView() { mRefreshContainerView.removeAllViews(); mLoadMoreContainerView.removeAllViews(); } private void initRefreshView() { if (mHTViewHolder == null) return; View refreshView = mHTViewHolder.getRefreshView(); if (refreshView != null) { if (refreshView.getParent() != null) { ((ViewGroup) refreshView.getParent()).removeView(refreshView); } int res = mHTViewHolder.getRefreshViewBackgroundResId(); if (res != 0) {//? mRefreshContainerView.setBackgroundResource(res); } else { mRefreshContainerView.setBackgroundResource(android.R.color.transparent); } mRefreshContainerView.removeAllViews(); setViewLayoutParams(refreshView); mRefreshContainerView.addView(refreshView); // hideRefreshView(true); } setRefreshUIChangeListener(mHTViewHolder); } private void initLoadMoreView() { if (mHTViewHolder == null) return; View loadMoreView = mHTViewHolder.getLoadMoreView(); if (loadMoreView != null) { if (loadMoreView.getParent() != null) { ((ViewGroup) loadMoreView.getParent()).removeView(loadMoreView); } int res = mHTViewHolder.getLoadMoreViewBackgroundResId(); if (res != 0) {//? mLoadMoreContainerView.setBackgroundResource(res); } else { mLoadMoreContainerView.setBackgroundResource(android.R.color.transparent); } mLoadMoreContainerView.removeAllViews(); setViewLayoutParams(loadMoreView); mLoadMoreContainerView.addView(loadMoreView); hideLoadMoreView(true); } setLoadMoreUIChangeListener(mHTViewHolder); } private void setViewLayoutParams(View view) { if (view == null) return; ViewGroup.LayoutParams lp = view.getLayoutParams() == null ? new ViewGroup.LayoutParams(0, 0) : view.getLayoutParams(); lp.width = checkOrientationVertical() ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT; lp.height = checkOrientationVertical() ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT; view.setLayoutParams(lp); } protected boolean checkOrientationVertical() { return mHTOrientation == VERTICAL_UP || mHTOrientation == VERTICAL_DOWN; } protected boolean checkOrientationReverse() { return mHTOrientation == HTOrientation.HORIZONTAL_LEFT || mHTOrientation == VERTICAL_UP; } private void checkChildren() { final int childCount = getChildCount(); if (childCount > 2) { throw new IllegalStateException("HTRefreshRecyclerView can only contains 2 children"); } } public void addView(View child) { checkChildren(); super.addView(child); } public void addView(View child, int index) { checkChildren(); super.addView(child, index); } public void addView(View child, int index, LayoutParams params) { checkChildren(); super.addView(child, index, params); } public void addView(View child, LayoutParams params) { checkChildren(); super.addView(child, params); } public void addView(View child, int width, int height) { checkChildren(); super.addView(child, width, height); } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mRefreshContainerView != null) { measureChild(mRefreshContainerView, widthMeasureSpec, heightMeasureSpec); } if (mRecyclerView != null) { measureChild(mRecyclerView, widthMeasureSpec, heightMeasureSpec); } } protected void onLayout(boolean changed, int l, int t, int r, int b) { // super.onLayout(changed, l, t, r, b); if (checkOrientationVertical()) { layoutVertical(mHTOrientation == VERTICAL_DOWN); } else { layoutHorizontal(mHTOrientation == HTOrientation.HORIZONTAL_RIGHT); } } protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mHTViewHolderTracker.isOverRefreshViewSize() && mHTViewHolder.getRefreshViewBackgroundResId() > 0) { int drawOffset = Math .abs(mHTViewHolderTracker.getCurrentPos() - mHTViewHolderTracker.getRefreshViewSize()); mRefreshBgPaint .setColor(ContextCompat.getColor(getContext(), mHTViewHolder.getRefreshViewBackgroundResId())); switch (mHTOrientation) { case VERTICAL_DOWN: canvas.drawRect(0, 0, getWidth(), drawOffset, mRefreshBgPaint); break; case HTOrientation.VERTICAL_UP: canvas.drawRect(0, getHeight() - drawOffset, getWidth(), getHeight(), mRefreshBgPaint); break; case HTOrientation.HORIZONTAL_RIGHT: canvas.drawRect(0, 0, drawOffset, getHeight(), mRefreshBgPaint); break; case HTOrientation.HORIZONTAL_LEFT: canvas.drawRect(getWidth() - drawOffset, 0, getWidth(), getHeight(), mRefreshBgPaint); break; } } } void layoutVertical(boolean isTop) { int offset = mHTViewHolderTracker.getCurrentPos(); int size = mHTViewHolderTracker.getRefreshViewSize(); int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int left, top, right, bottom; if (mRefreshContainerView != null) { left = paddingLeft; if (isTop) { top = -(size - paddingTop - offset); } else { top = mRecyclerView.getMeasuredHeight() - offset; } right = left + mRefreshContainerView.getMeasuredWidth(); bottom = top + mRefreshContainerView.getMeasuredHeight(); mRefreshContainerView.layout(left, top, right, bottom); } if (mRecyclerView != null) { left = paddingLeft; if (isTop) { top = paddingTop + offset; } else { top = paddingTop - offset; } right = left + mRecyclerView.getMeasuredWidth(); bottom = top + mRecyclerView.getMeasuredHeight(); mRecyclerView.layout(left, top, right, bottom); } } void layoutHorizontal(boolean isLeft) { int offset = mHTViewHolderTracker.getCurrentPos(); int size = mHTViewHolderTracker.getRefreshViewSize(); int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int left, top, right, bottom; if (mRefreshContainerView != null) { if (isLeft) { left = -(size - paddingLeft - offset); } else { left = mRecyclerView.getMeasuredWidth() - offset; } top = paddingTop; right = left + mRefreshContainerView.getMeasuredWidth(); bottom = top + mRefreshContainerView.getMeasuredHeight(); mRefreshContainerView.layout(left, top, right, bottom); } if (mRecyclerView != null) { if (isLeft) { left = paddingLeft + offset; } else { left = paddingLeft - offset; } top = paddingTop; right = left + mRecyclerView.getMeasuredWidth(); bottom = top + mRecyclerView.getMeasuredHeight(); mRecyclerView.layout(left, top, right, bottom); } } /** * ??,??? */ public boolean checkChildScroll() { switch (mHTOrientation) { case VERTICAL_UP: return ViewCompat.canScrollVertically(mRecyclerView, 1); case VERTICAL_DOWN: return ViewCompat.canScrollVertically(mRecyclerView, -1); case HTOrientation.HORIZONTAL_LEFT: return ViewCompat.canScrollHorizontally(mRecyclerView, 1); case HTOrientation.HORIZONTAL_RIGHT: return ViewCompat.canScrollHorizontally(mRecyclerView, -1); default: return false; } } /** * ??? */ protected void processLoadMoreStatusChanged() { if (mLoadMoreUIChangeListener == null) return; switch (mLoadMoreStatus) { case LoadMoreStatus.IDLE: mLoadMoreUIChangeListener.onLoadMoreComplete(mHasMore); break; case LoadMoreStatus.LOADING: mLoadMoreUIChangeListener.onLoadMoreStart(mHasMore); break; default: break; } } /** * ? */ public void hideLoadMoreView(boolean isHide) { if (mLoadMoreContainerView != null && mHTViewHolder != null) { int size = 0; if (isHide) { size = -mHTViewHolderTracker.getLoadMoreSize(); } switch (mHTOrientation) { case VERTICAL_DOWN: mLoadMoreContainerView.setPadding(0, 0, 0, isHide ? size : 0); break; case VERTICAL_UP: mLoadMoreContainerView.setPadding(0, isHide ? size : 0, 0, 0); break; case HTOrientation.HORIZONTAL_LEFT: mLoadMoreContainerView.setPadding(isHide ? size : 0, 0, 0, 0); break; case HTOrientation.HORIZONTAL_RIGHT: mLoadMoreContainerView.setPadding(0, 0, isHide ? size : 0, 0); break; } } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); initListeners(); RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); if (adapter != null && !(adapter instanceof HTWrapperAdapter)) {//?,?HTWrapperAdapter throw new IllegalArgumentException("the type of adapter is incorrect !"); } } @Override protected void onDetachedFromWindow() { clear();//? super.onDetachedFromWindow(); } private void clear() { mRecyclerView.clearOnScrollListeners(); mRecyclerView.setOnTouchListener(null); } public void setEnableScrollOnRefresh(boolean enableScrollOnReFresh) { mEnableScrollOnReFresh = enableScrollOnReFresh; } /** * ? * ?? RecyclerView BugIndexOutOfBoundsException: Inconsistency detected. Invalid item position */ /* private void setRecyclerViewOnTouchListener() { mRecyclerView.setOnTouchListener( new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return !mEnableScrollOnReFresh && mRefreshStatus == RefreshStatus.REFRESHING; } } ); }*/ private void setRecyclerViewOnScrollListener() { mInnerScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE && shouldHandleLoadMore()) { performLoadMore();//??? } if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { if (mRecyclerViewDragListener != null) { mRecyclerViewDragListener.onRecyclerViewScroll(); } } for (int i = mScrollListeners.size() - 1; i >= 0; i--) { OnScrollListener scrollListener = mScrollListeners.get(i); if (scrollListener != null) { scrollListener.onScrollStateChanged(recyclerView, newState); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); for (int i = mScrollListeners.size() - 1; i >= 0; i--) { OnScrollListener scrollListener = mScrollListeners.get(i); if (scrollListener != null) { scrollListener.onScrolled(recyclerView, dx, dy); } } } }; mRecyclerView.addOnScrollListener(mInnerScrollListener); } /** * ? */ protected abstract Boolean handleMoveAction(MotionEvent event); /** * ?? */ protected abstract boolean shouldHandleRefresh(); /** * ?? */ protected abstract boolean shouldHandleLoadMore(); /** * */ protected abstract void performRefresh(); /** * */ protected abstract void performLoadMore(); /** * ? */ protected abstract void endRefresh(); /** * ? */ protected abstract void endLoadMore(); @Override public void setOnLoadMoreListener(HTLoadMoreListener loadMoreDelegate) { mLoadMoreDelegate = loadMoreDelegate; } @Override public void setOnRefreshListener(HTRefreshListener refreshDelegate) { mRefreshDelegate = refreshDelegate; } private void setRefreshUIChangeListener(HTRefreshUIChangeListener refreshUIChangeListener) { mRefreshUIChangeListener = refreshUIChangeListener; } private void setLoadMoreUIChangeListener(HTLoadMoreUIChangeListener loadMoreUIChangeListener) { mLoadMoreUIChangeListener = loadMoreUIChangeListener; } public void setLoadMoreViewShow(boolean loadMoreShow) { this.mLoadMoreViewDisplay = loadMoreShow; } @Override public RecyclerView getRecyclerView() { return mRecyclerView; } @Override public RecyclerView.LayoutManager getLayoutManager() { return mRecyclerView.getLayoutManager(); } @Override public void setLayoutManager(RecyclerView.LayoutManager layoutManager) { boolean reverse = checkOrientationReverse(); int orientation = checkOrientationVertical() ? OrientationHelper.VERTICAL : OrientationHelper.HORIZONTAL; //???,layoutManager if (layoutManager instanceof GridLayoutManager) { GridLayoutManager realLayoutManager = (GridLayoutManager) layoutManager; realLayoutManager.setReverseLayout(reverse); realLayoutManager.setOrientation(orientation); } else if (layoutManager instanceof LinearLayoutManager) { LinearLayoutManager realLayoutManager = (LinearLayoutManager) layoutManager; realLayoutManager.setReverseLayout(reverse); realLayoutManager.setOrientation(orientation); } else if (layoutManager instanceof StaggeredGridLayoutManager) { StaggeredGridLayoutManager realLayoutManager = (StaggeredGridLayoutManager) layoutManager; realLayoutManager.setReverseLayout(reverse); realLayoutManager.setOrientation(orientation); } mRecyclerView.setLayoutManager(layoutManager); } @Override public RecyclerView.ItemAnimator getItemAnimator() { return mRecyclerView.getItemAnimator(); } @Override public void setItemAnimator(RecyclerView.ItemAnimator animator) { mRecyclerView.setItemAnimator(animator); } @Override public void addItemDecoration(RecyclerView.ItemDecoration decor) { if (decor != null) {//,?view addItemDecoration(new HTItemDecoration(decor), -1); } } @Override public void removeItemDecoration(RecyclerView.ItemDecoration decor) { mRecyclerView.removeItemDecoration(decor); } @Override public void setHasFixedSize(boolean hasFixedSize) { mRecyclerView.setHasFixedSize(hasFixedSize); } @Override public boolean hasFixedSize() { return getRecyclerView().hasFixedSize(); } @Override public RecyclerView.Adapter getAdapter() { return mInnerAdapter; } public void setAdapter(RecyclerView.Adapter adapter) { if (adapter == null) return; //??viewAdapter mInnerAdapter = adapter; mHTWrapperAdapter = new HTWrapperAdapter(adapter, mLoadMoreContainerView); mRecyclerView.setAdapter(mHTWrapperAdapter); } @Override public void setRecyclerListener(RecyclerView.RecyclerListener listener) { mRecyclerView.setRecyclerListener(listener); } @Override public void clearOnScrollListeners() { mScrollListeners.clear(); } @Override public RecyclerView.ViewHolder getChildViewHolder(View child) { return mRecyclerView.getChildViewHolder(child); } @Override public void addOnItemTouchListener(RecyclerView.OnItemTouchListener listener) { mRecyclerView.addOnItemTouchListener(listener); } @Override public void removeOnItemTouchListener(RecyclerView.OnItemTouchListener listener) { mRecyclerView.removeOnItemTouchListener(listener); } @Override public void addOnScrollListener(OnScrollListener listener) { mScrollListeners.add(listener); } @Override public void removeOnScrollListener(OnScrollListener listener) { mScrollListeners.remove(listener); } @Override public void setRecyclerViewDragListener(HTRecyclerViewDragListener recyclerViewDragListener) { mRecyclerViewDragListener = recyclerViewDragListener; } @Override public void addItemDecoration(RecyclerView.ItemDecoration decor, int index) { if (decor != null) { mRecyclerView.addItemDecoration(new HTItemDecoration(decor), index); } } /** * ? */ public static class RefreshStatus { /** * ?? */ public static final int IDLE = 0; /** * ? */ public static final int REFRESH_PREPARE = 1; /** * ? */ public static final int REFRESHING = 2; /** * ?? */ public static final int COMPLETE = 3; } /** * ? */ public static class LoadMoreStatus { public static final int IDLE = 0; public static final int LOADING = 1; } /** * RecyclerView?? */ public interface OnScrollListener { void onScrollStateChanged(RecyclerView recyclerView, int newState); void onScrolled(RecyclerView recyclerView, int dx, int dy); } /** * ??,?????? */ protected interface HTRefreshUIChangeListener { /** * ? */ void onReset(); /** * */ void onRefreshPrepare(); /** * ? */ void onRefreshing(); /** * ?? */ void onRefreshComplete(); /** * ??(?{@link RefreshStatus#IDLE}{@link RefreshStatus#REFRESHING}) * ???? * * @param scale 0 11 0 * @param moveDistance ?? * @param refreshStatus ??{@link RefreshStatus#IDLE}{@link RefreshStatus#REFRESH_PREPARE}{@link RefreshStatus#REFRESHING}{@link RefreshStatus#COMPLETE} * @param viewHolderTracker ???{@link HTViewHolderTracker} */ void onRefreshPositionChange(float scale, float moveDistance, int refreshStatus, HTViewHolderTracker viewHolderTracker); } /** * ?? */ protected interface HTLoadMoreUIChangeListener { /** * ? * * @param hasMore ?? */ void onLoadMoreStart(boolean hasMore); /** * ?? * * @param hasMore ?? */ void onLoadMoreComplete(boolean hasMore); } }