用命令去监测启动速度,怎样去优化和加速应用的启动速度。
阅读源码的 Activity启动流程。
基于头部和底部的RecyclerView,监听手指下拉的距离,改变头部刷新View的marginTop值。
一. PullRefreshActivity 使用类,监听上拉和下拉动作。
/**
* 基于头部+底部控件,RecyclerView支持下拉刷新和上拉加载更多。
*/
public class PullRefreshActivity extends AppCompatActivity
implements RefreshRecyclerView.OnRefreshListener,
LoadRefreshRecyclerView.OnLoadMoreListener {
private static final String TAG = "PullRefreshActivity";
private LoadRefreshRecyclerView mRecyclerView;
RecyclerAdapter mListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pull_refresh);
initData();
mRecyclerView = findViewById(R.id.recycler_view);
// 添加头部和底部的刷新效果。正在加载数据页面和无数据页面。
mRecyclerView.addRefreshViewCreator(new DefaultRefreshCreator()); // 下拉刷新view
mRecyclerView.addLoadViewCreator(new DefaultLoadCreator()); // 上拉加载更多内容...
mRecyclerView.setOnRefreshListener(this);
mRecyclerView.setOnLoadMoreListener(this);
mRecyclerView.addLoadingView(findViewById(R.id.load_view)); // 加载中view
mRecyclerView.addEmptyView(findViewById(R.id.empty_view)); // 无数据的view
GridLayoutManager mLayoutManager = new GridLayoutManager(this, 4);
mRecyclerView.setLayoutManager(mLayoutManager);
mListAdapter = new RecyclerAdapter(this, mData);
mRecyclerView.setAdapter(mListAdapter);
// 添加header。
View header = LayoutInflater.from(this).inflate(R.layout.layout_header_footer, mRecyclerView, false);
mRecyclerView.addHeaderView(header);
header.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRecyclerView.removeHeaderView(header);
}
});
}
private List mData = new ArrayList<>(); // 数据源
protected void initData() {
mData = new ArrayList();
for (int i = 'A'; i <= 'z'; i++) {
mData.add("" + (char) i);
}
Log.d("TAG", "length:" + mData.size());
}
@Override
public void onRefresh() {
Log.d(TAG, "onRefresh");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mData.add(0, "onRefresh---");
mListAdapter.notifyDataSetChanged();
mRecyclerView.onStopRefresh();
}
}, 2000);
}
@Override
public void onLoad() {
Log.d(TAG, "onLoadMore");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mData.add("onLoadMore---");
mListAdapter.notifyDataSetChanged();
mRecyclerView.onStopLoad();
}
}, 2000);
}
}
二. 上拉,下拉接口定义
/**
* 上拉加载更多的辅助类为了匹配所有效果
*/
public abstract class LoadViewCreator {
/**
* 获取上拉加载更多的View
* @param context 上下文
* @param parent RecyclerView
*/
public abstract View getLoadView(Context context, ViewGroup parent);
/**
* 正在上拉
* @param currentDragHeight 当前拖动的高度
* @param loadViewHeight 总的加载高度
* @param currentLoadStatus 当前状态
*/
public abstract void onPull(int currentDragHeight, int loadViewHeight, int currentLoadStatus);
/**
* 正在加载中
*/
public abstract void onLoading();
/**
* 停止加载
*/
public abstract void onStopLoad();
}
/**
* 下拉刷新的辅助类为了匹配所有效果
*/
public abstract class RefreshViewCreator {
/**
* 获取下拉刷新的View
* @param context 上下文
* @param parent RecyclerView
*/
public abstract View getRefreshView(Context context, ViewGroup parent);
/**
* 正在下拉
* @param currentDragHeight 当前拖动的高度
* @param refreshViewHeight 总的刷新高度
* @param currentRefreshStatus 当前状态
*/
public abstract void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus);
// 正在刷新中
public abstract void onRefreshing();
// 停止刷新。
public abstract void onStopRefresh();
}
三. 上拉和下拉接口的实现
// 底部加载更多默认的view.
public class DefaultLoadCreator extends LoadViewCreator {
private View mRefreshIv;
private View mRefreshTv;
@Override
public View getLoadView(Context context, ViewGroup parent) {
View refreshView = LayoutInflater.from(context).inflate(R.layout.layout_refresh_footer_view, parent, false);
mRefreshIv = refreshView.findViewById(R.id.refresh_iv);
mRefreshTv = refreshView.findViewById(R.id.refresh_tv);
return refreshView;
}
@Override
public void onPull(int currentDragHeight, int loadViewHeight, int currentLoadStatus) {
float rotate = ((float) currentDragHeight) / loadViewHeight;
// 不断下拉的过程中不断的旋转图片
mRefreshIv.setRotation(rotate * 360);
mRefreshTv.setVisibility(View.INVISIBLE);
mRefreshIv.setVisibility(View.VISIBLE);
}
@Override
public void onLoading() {
// 刷新的时候不断旋转
RotateAnimation animation = new RotateAnimation(0, 720,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setRepeatCount(-1);
animation.setDuration(1000);
mRefreshIv.startAnimation(animation);
}
@Override
public void onStopLoad() {
// 停止加载的时候清除动画
mRefreshIv.setRotation(0);
mRefreshIv.clearAnimation();
mRefreshIv.setVisibility(View.INVISIBLE);
mRefreshTv.setVisibility(View.VISIBLE);
}
}
/**
* 默认样式的头部刷新UI,
*/
public class DefaultRefreshCreator extends RefreshViewCreator {
// 加载数据的ImageView
private View mRefreshIv;
@Override
public View getRefreshView(Context context, ViewGroup parent) {
View refreshView = LayoutInflater.from(context).inflate(R.layout.layout_refresh_header_view, parent, false);
mRefreshIv = refreshView.findViewById(R.id.refresh_iv);
return refreshView;
}
@Override
public void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus) {
float rotate = ((float) currentDragHeight) / refreshViewHeight;
// 不断下拉的过程中不断的旋转图片
mRefreshIv.setRotation(rotate * 360);
}
@Override
public void onRefreshing() {
// 刷新的时候不断旋转
RotateAnimation animation = new RotateAnimation(0, 720,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setRepeatCount(-1);
animation.setDuration(1000);
mRefreshIv.startAnimation(animation);
}
@Override
public void onStopRefresh() {
// 停止加载的时候清除动画
mRefreshIv.setRotation(0);
mRefreshIv.clearAnimation();
}
}
相关默认header和footer的布局xml
// layout_refresh_header_view.xml
// layout_refresh_footer_view.xml
四. LoadRefreshRecyclerView
package com.example.recyclerview01.day04;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.view.ViewCompat;
/**
* 下拉刷新上拉加载更多的RecyclerView
* 底部加载更多UI,目前一直显示。
*/
public class LoadRefreshRecyclerView extends RefreshRecyclerView {
// 上拉加载更多的辅助类
private LoadViewCreator mLoadCreator;
// 上拉加载更多头部的高度
private int mLoadViewHeight = 0;
// 上拉加载更多的头部View
private View mLoadView;
// 手指按下的Y位置
private int mFingerDownY;
// 当前是否正在拖动
private boolean mCurrentDrag = false;
// 当前的状态
private int mCurrentLoadStatus;
// 默认状态
public int LOAD_STATUS_NORMAL = 0x0011;
// 上拉加载更多状态
public static int LOAD_STATUS_PULL_DOWN_REFRESH = 0x0022;
// 松开加载更多状态
public static int LOAD_STATUS_LOOSEN_LOADING = 0x0033;
// 正在加载更多状态
public static int LOAD_STATUS_LOADING = 0x0044;
public LoadRefreshRecyclerView(Context context) {
super(context);
}
public LoadRefreshRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LoadRefreshRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 先处理上拉加载更多,同时考虑加载列表的不同风格样式,确保这个项目还是下一个项目都能用
// 所以我们不能直接添加View,需要利用辅助类
public void addLoadViewCreator(LoadViewCreator loadCreator) {
this.mLoadCreator = loadCreator;
addRefreshView();
}
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
addRefreshView();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录手指按下的位置 ,之所以写在dispatchTouchEvent那是因为如果我们处理了条目点击事件,
// 那么就不会进入onTouchEvent里面,所以只能在这里获取
mFingerDownY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if (mCurrentDrag) {
restoreLoadView();
}
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 重置当前加载更多状态
*/
private void restoreLoadView() {
int currentBottomMargin = ((MarginLayoutParams) mLoadView.getLayoutParams()).bottomMargin;
int finalBottomMargin = 0;
if (mCurrentLoadStatus == LOAD_STATUS_LOOSEN_LOADING) {
mCurrentLoadStatus = LOAD_STATUS_LOADING;
if (mLoadCreator != null) {
mLoadCreator.onLoading();
}
if (mListener != null) {
mListener.onLoad(); // 加载更多
}
}
int distance = currentBottomMargin - finalBottomMargin;
// 回弹到指定位置
ValueAnimator animator = ObjectAnimator.ofFloat(currentBottomMargin, finalBottomMargin).setDuration(distance);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentTopMargin = (float) animation.getAnimatedValue();
setLoadViewMarginBottom((int) currentTopMargin);
}
});
animator.start();
mCurrentDrag = false;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
// 如果是在最底部才处理,否则不需要处理
if (canScrollDown() || mCurrentLoadStatus == LOAD_STATUS_LOADING) {
// 如果没有到达最顶端,也就是说还可以向上滚动就什么都不处理
return super.onTouchEvent(e);
}
if (mLoadCreator != null) {
mLoadViewHeight = mLoadView.getMeasuredHeight();
}
// 解决上拉加载更多自动滚动问题
if (mCurrentDrag) {
scrollToPosition(getAdapter().getItemCount() - 1);
}
// 获取手指触摸拖拽的距离
int distanceY = (int) ((e.getRawY() - mFingerDownY) * mDragIndex);
// 如果是已经到达头部,并且不断的向下拉,那么不断的改变refreshView的marginTop的值
if (distanceY < 0) {
setLoadViewMarginBottom(-distanceY);
updateLoadStatus(-distanceY);
mCurrentDrag = true;
return true;
}
break;
}
return super.onTouchEvent(e);
}
/**
* 更新加载的状态
*/
private void updateLoadStatus(int distanceY) {
if (distanceY <= 0) {
mCurrentLoadStatus = LOAD_STATUS_NORMAL;
} else if (distanceY < mLoadViewHeight) {
mCurrentLoadStatus = LOAD_STATUS_PULL_DOWN_REFRESH;
} else {
mCurrentLoadStatus = LOAD_STATUS_LOOSEN_LOADING;
}
if (mLoadCreator != null) {
mLoadCreator.onPull(distanceY, mLoadViewHeight, mCurrentLoadStatus);
}
}
/**
* 添加底部加载更多View
*/
private void addRefreshView() {
Adapter adapter = getAdapter();
if (adapter != null && mLoadCreator != null) {
// 添加底部加载更多View
View loadView = mLoadCreator.getLoadView(getContext(), this);
if (loadView != null) {
addFooterView(loadView);
this.mLoadView = loadView;
}
}
}
/**
* 设置加载View的marginBottom
*/
public void setLoadViewMarginBottom(int marginBottom) {
MarginLayoutParams params = (MarginLayoutParams) mLoadView.getLayoutParams();
if (marginBottom < 0) {
marginBottom = 0;
}
params.bottomMargin = marginBottom;
mLoadView.setLayoutParams(params);
}
/**
* @return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
* 判断是不是滚动到了最顶部,这个是从SwipeRefreshLayout里面copy过来的源代码
*/
public boolean canScrollDown() {
return ViewCompat.canScrollVertically(this, 1);
}
/**
* 停止加载更多
*/
public void onStopLoad() {
mCurrentLoadStatus = LOAD_STATUS_NORMAL;
restoreLoadView();
if (mLoadCreator != null) {
mLoadCreator.onStopLoad();
}
}
// 处理加载更多回调监听
private OnLoadMoreListener mListener;
public void setOnLoadMoreListener(OnLoadMoreListener listener) {
this.mListener = listener;
}
public interface OnLoadMoreListener {
void onLoad();
}
}
五. RefreshRecyclerView
package com.example.recyclerview01.day04;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
/**
* 下拉刷新的RecyclerView
* 监听手指下拉的距离,改变头部刷新控件view的marginTop值。
* 通用:
*/
public class RefreshRecyclerView extends WrapRecyclerView {
// 下拉刷新的辅助类
private RefreshViewCreator mRefreshCreator;
// 下拉刷新头部的高度
private int mRefreshViewHeight = 0;
// 下拉刷新的头部View
private View mRefreshView;
// 手指按下的Y位置
private int mFingerDownY;
// 手指拖拽的阻力指数
protected float mDragIndex = 0.35f;
// 当前是否正在拖动
private boolean mCurrentDrag = false;
// 当前的状态
private int mCurrentRefreshStatus;
// 默认状态
public int REFRESH_STATUS_NORMAL = 0x0011;
// 下拉刷新状态
public int REFRESH_STATUS_PULL_DOWN_REFRESH = 0x0022;
// 松开刷新状态
public int REFRESH_STATUS_LOOSEN_REFRESHING = 0x0033;
// 正在刷新状态
public int REFRESH_STATUS_REFRESHING = 0x0033;
public RefreshRecyclerView(Context context) {
super(context);
}
public RefreshRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RefreshRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 先处理下拉刷新,同时考虑刷新列表的不同风格样式,确保这个项目还是下一个项目都能用
// 所以我们不能直接添加View,需要利用辅助类
public void addRefreshViewCreator(RefreshViewCreator refreshCreator) {
this.mRefreshCreator = refreshCreator;
addRefreshView();
}
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
addRefreshView();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录手指按下的位置 ,之所以写在dispatchTouchEvent那是因为如果我们处理了条目点击事件,
// 那么就不会进入onTouchEvent里面,所以只能在这里获取
mFingerDownY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if (mCurrentDrag) {
restoreRefreshView();
}
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 重置当前刷新状态状态
*/
private void restoreRefreshView() {
int currentTopMargin = ((MarginLayoutParams) mRefreshView.getLayoutParams()).topMargin;
int finalTopMargin = -mRefreshViewHeight + 1;
if (mCurrentRefreshStatus == REFRESH_STATUS_LOOSEN_REFRESHING) {
finalTopMargin = 0;
mCurrentRefreshStatus = REFRESH_STATUS_REFRESHING;
if (mRefreshCreator != null) {
mRefreshCreator.onRefreshing();
}
if (mListener != null) {
mListener.onRefresh();
}
}
int distance = currentTopMargin - finalTopMargin;
// 回弹到指定位置
ValueAnimator animator = ObjectAnimator.ofFloat(currentTopMargin, finalTopMargin).setDuration(distance);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentTopMargin = (float) animation.getAnimatedValue();
setRefreshViewMarginTop((int) currentTopMargin);
}
});
animator.start();
mCurrentDrag = false;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
// 如果是在最顶部才处理,否则不需要处理
if (canScrollUp() || mCurrentRefreshStatus == REFRESH_STATUS_REFRESHING) {
// 如果没有到达最顶端,也就是说还可以向上滚动就什么都不处理
return super.onTouchEvent(e);
}
// 解决下拉刷新自动滚动问题
if (mCurrentDrag) {
scrollToPosition(0);
}
// 获取手指触摸拖拽的距离
int distanceY = (int) ((e.getRawY() - mFingerDownY) * mDragIndex);
// 如果是已经到达头部,并且不断的向下拉,那么不断的改变refreshView的marginTop的值
if (distanceY > 0) {
int marginTop = distanceY - mRefreshViewHeight;
setRefreshViewMarginTop(marginTop);
updateRefreshStatus(marginTop);
mCurrentDrag = true;
return false;
}
break;
}
return super.onTouchEvent(e);
}
/**
* 更新刷新的状态
*/
private void updateRefreshStatus(int marginTop) {
if (marginTop <= -mRefreshViewHeight) {
mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
} else if (marginTop < 0) {
mCurrentRefreshStatus = REFRESH_STATUS_PULL_DOWN_REFRESH;
} else {
mCurrentRefreshStatus = REFRESH_STATUS_LOOSEN_REFRESHING;
}
if (mRefreshCreator != null) {
mRefreshCreator.onPull(marginTop, mRefreshViewHeight, mCurrentRefreshStatus);
}
}
/**
* 添加头部的刷新View
*/
private void addRefreshView() {
RecyclerView.Adapter adapter = getAdapter();
if (adapter != null && mRefreshCreator != null) {
// 添加头部的刷新View
View refreshView = mRefreshCreator.getRefreshView(getContext(), this);
if (refreshView != null) {
addHeaderView(refreshView);
this.mRefreshView = refreshView;
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
if (mRefreshView != null && mRefreshViewHeight <= 0) {
// 获取头部刷新View的高度
mRefreshViewHeight = mRefreshView.getMeasuredHeight();
if (mRefreshViewHeight > 0) {
// 隐藏头部刷新的View marginTop 多留出1px防止无法判断是不是滚动到头部问题
setRefreshViewMarginTop(-mRefreshViewHeight + 1);
}
}
}
}
/**
* 设置刷新View的marginTop
*/
public void setRefreshViewMarginTop(int marginTop) {
MarginLayoutParams params = (MarginLayoutParams) mRefreshView.getLayoutParams();
if (marginTop < -mRefreshViewHeight + 1) {
marginTop = -mRefreshViewHeight + 1;
}
params.topMargin = marginTop;
mRefreshView.setLayoutParams(params);
}
/**
* @return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
* 判断是不是滚动到了最顶部,这个是从SwipeRefreshLayout里面copy过来的源代码
*/
public boolean canScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
return ViewCompat.canScrollVertically(this, -1) || this.getScrollY() > 0;
} else {
return ViewCompat.canScrollVertically(this, -1);
}
}
/**
* 停止刷新
*/
public void onStopRefresh() {
mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
restoreRefreshView();
if (mRefreshCreator != null) {
mRefreshCreator.onStopRefresh();
}
}
// 处理刷新回调监听
private OnRefreshListener mListener;
public void setOnRefreshListener(OnRefreshListener listener) {
this.mListener = listener;
}
public interface OnRefreshListener {
void onRefresh();
}
}
六. WrapRecyclerAdapter
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* RecyclerView头部和底部的包装类。
*/
public class WrapRecyclerAdapter extends RecyclerView.Adapter {
// 数据列表原始的适配器,它不包含头部。列表Adapter
private RecyclerView.Adapter mAdapter;
// 头部和底部的集合ArrayList不好区分是否已经添加过了;使用Map集合,进行标识是头部还是底部?
// 如果key是int,value是Object对象,建议使用SparseArray
private SparseArray mHeaders, mFooters;
private static int BASE_HEADER_KEY = 1000000;
private static int BASE_FOOTER_KEY = 2000000;
public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaders = new SparseArray();
mFooters = new SparseArray();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 根据viewType,来区分是头部/底部,还是列表中元素。返回对应的key。
if (mHeaders.indexOfKey(viewType) >= 0) {
// 有头部,获取到对应头部,并包装成ViewHolder.
return createHeaderFooterViewHolder(mHeaders.get(viewType));
} else if (mFooters.indexOfKey(viewType) >= 0) {
// 底部
return createHeaderFooterViewHolder(mFooters.get(viewType));
}
// 中间列表: 兼容多布局条目。
// return mAdapter.createViewHolder(parent, mAdapter.getItemViewType(adjPosition));
return mAdapter.createViewHolder(parent, viewType);
}
// 将header,footer包装成ViewHolder。
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {};
}
/**
* 是不是底部类型
*/
private boolean isFooterViewType(int viewType) {
int position = mFooters.indexOfKey(viewType);
return position >= 0;
}
/**
* 是不是头部类型
*/
private boolean isHeaderViewType(int viewType) {
int position = mHeaders.indexOfKey(viewType);
return position >= 0;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderPosition(position) || isFooterPosition(position)) {
return;
}
// mAdapter,绑定数据
final int adjPosition = position - mHeaders.size(); // 位置修正
mAdapter.onBindViewHolder(holder, adjPosition);
/*if (mAdapter != null) {
int adapterCount = mAdapter.getItemCount(); // 总数量
if (adjPosition < adapterCount) {
// 绑定数据,使用矫正后的位置adjPosition
mAdapter.onBindViewHolder(holder, adjPosition);
}
}*/
}
/**
* 是不是底部位置
*/
private boolean isFooterPosition(int position) {
return position >= (mHeaders.size() + mAdapter.getItemCount());
}
/**
* 是不是头部位置
*/
private boolean isHeaderPosition(int position) {
return position < mHeaders.size();
}
@Override
public int getItemCount() {
int totalCount = mAdapter.getItemCount() + mHeaders.size() + mFooters.size();
return totalCount;
}
@Override
public int getItemViewType(int position) {
if (isHeaderPosition(position)) {
// 直接返回position位置的key
return mHeaders.keyAt(position);
}
if (isFooterPosition(position)) {
// 直接返回position位置的key
position = position - mHeaders.size() - mAdapter.getItemCount();
return mFooters.keyAt(position);
}
// 返回列表Adapter的getItemViewType
position = position - mHeaders.size();
return mAdapter.getItemViewType(position);
}
/* @Override
public int getItemViewType(int position) {
// 根据当前位置position,返回当前viewType.
// 头部;mAdapter中间的数据;底部.
int numHeaders = mHeaders.size();
if (position < numHeaders) {
return mHeaders.keyAt(position);
}
// mAdapter
final int adjPosition = position - numHeaders; // 位置修正
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
// footer
int footerPosition = adjPosition - adapterCount;
return mFooters.keyAt(footerPosition);
}*/
/**
* 获取列表的Adapter
*/
private RecyclerView.Adapter getAdapter() {
return mAdapter;
}
// 添加移除,头部和底部
public void addHeaderView(View view) {
// 不能重复添加
int position = mHeaders.indexOfValue(view);
if (position < 0) { // 未添加过
mHeaders.put(BASE_HEADER_KEY++, view);
}
notifyDataSetChanged();
}
// 添加底部
public void addFooterView(View view) {
if (mFooters.indexOfValue(view) == -1) {
mFooters.put(BASE_FOOTER_KEY++, view);
notifyDataSetChanged();
}
}
// 移除头部
public void removeHeaderView(View view) {
if (mHeaders.indexOfValue(view) >= 0) {
mHeaders.removeAt(mHeaders.indexOfValue(view));
notifyDataSetChanged();
}
}
// 移除底部
public void removeFooterView(View view) {
if (mFooters.indexOfValue(view) >= 0) {
mFooters.removeAt(mFooters.indexOfValue(view));
notifyDataSetChanged();
}
}
/**
* 解决GridLayoutManager添加头部和底部不占用一行的问题
*
* @param recycler
* @version 1.0
*/
public void adjustSpanSize(RecyclerView recycler) {
if (recycler.getLayoutManager() instanceof GridLayoutManager) {
final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
boolean isHeaderOrFooter =
isHeaderPosition(position) || isFooterPosition(position);
return isHeaderOrFooter ? layoutManager.getSpanCount() : 1;
}
});
}
}
}
七. WrapRecyclerAdapter
package com.example.recyclerview01.day04;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* 自定义,包裹效果的RV:
* 每次使用的时候,不需要再包装一下。
* 观察者模式:数据改变了,通知mWrapRecyclerAdapter,让整个列表UI刷新。
*/
public class WrapRecyclerView extends RecyclerView {
// 增加一些通用功能
// 空列表数据应该显示的空View
// 正在加载数据页面,也就是正在获取后台接口页面
private View mEmptyView, mLoadingView;
// 这个是列表数据的Adapter: 中间数据的适配器
private RecyclerView.Adapter mAdapter;
// 包裹了一层的头部底部Adapter
private WrapRecyclerAdapter mWrapRecyclerAdapter;
public WrapRecyclerView(Context context) {
this(context, null);
}
public WrapRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setAdapter(Adapter adapter) {
// 为了防止多次设置Adapter
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mAdapterDataObserver);
mAdapter = null;
}
this.mAdapter = adapter;
if (adapter instanceof WrapRecyclerAdapter) {
mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
} else {
// 删除的bug: adapter中数据已经修改了。但是mWrapRecyclerAdapter中的数据没修改。
// 所以需要关联---观察者.
mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
}
super.setAdapter(mWrapRecyclerAdapter);
// 注册观察者
mAdapter.registerAdapterDataObserver(mAdapterDataObserver);
// 解决GridLayout添加头部和底部也要占据一行
mWrapRecyclerAdapter.adjustSpanSize(this);
dataChanged(); // 初次进来,无数据显示UI。
}
//region 添加header、footer的方法
public void addHeaderView(View view) {
// 要先setAdapter,mWrapRecyclerAdapter才不为空.
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addHeaderView(view);
}
}
public void addFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addFooterView(view);
}
}
public void removeHeaderView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeHeaderView(view);
}
}
public void removeFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeFooterView(view);
}
}
//endregion
//region 带注册的观察者. mAdapter数据发送了变化,通知mWrapRecyclerAdapter。
private AdapterDataObserver mAdapterDataObserver = new AdapterDataObserver() {
// 观察者的回调.
@Override
public void onChanged() {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyDataSetChanged();
dataChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRangeChanged(positionStart, itemCount);
dataChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
dataChanged();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRangeInserted(positionStart, itemCount);
dataChanged();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
if (mAdapter == null) return;
Log.d("TAG", "onItemRangeRemoved " + positionStart + " " + itemCount);
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRangeRemoved(positionStart, itemCount);
dataChanged();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
dataChanged();
}
};
//endregion
//region 正在加载中、空数据UI.
/**
* 添加一个空列表数据页面
*/
public void addEmptyView(View emptyView) {
this.mEmptyView = emptyView;
}
/**
* 添加一个正在加载数据的页面
*/
public void addLoadingView(View loadingView) {
this.mLoadingView = loadingView;
}
/**
* Adapter数据改变的方法
*/
private void dataChanged() {
if (mAdapter.getItemCount() == 0) {
// 没有数据
if (mEmptyView != null) {
mEmptyView.setVisibility(VISIBLE);
} else {
mEmptyView.setVisibility(GONE);
}
}
}
//endregion
}