paging使用:https://huangxiaoguo.blog.csdn.net/article/details/106567399

  • 效果

  • 封装可添加Header和Footer的BaseAdapter

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;/*** 一个能够添加HeaderView,FooterView的PagedListAdapter。* 解决了添加HeaderView和FooterView时 RecyclerView定位不准确的问题** @param <T>  Java Bean* @param <VH>*/
public abstract class AbsPagedListAdapter<T, VH extends RecyclerView.ViewHolder> extends PagedListAdapter<T, VH> {private SparseArray<View> mHeaders = new SparseArray<>();private SparseArray<View> mFooters = new SparseArray<>();private int BASE_ITEM_TYPE_HEADER = 100000;private int BASE_ITEM_TYPE_FOOTER = 200000;protected AbsPagedListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {super(diffCallback);}public void addHeaderView(View view) {//判断给View对象是否还没有处在mHeaders数组里面if (mHeaders.indexOfValue(view) < 0) {mHeaders.put(BASE_ITEM_TYPE_HEADER++, view);notifyDataSetChanged();}}public void addFooterView(View view) {//判断给View对象是否还没有处在mFooters数组里面if (mFooters.indexOfValue(view) < 0) {mFooters.put(BASE_ITEM_TYPE_FOOTER++, view);notifyDataSetChanged();}}// 移除头部public void removeHeaderView(View view) {int index = mHeaders.indexOfValue(view);if (index < 0) return;mHeaders.removeAt(index);notifyDataSetChanged();}// 移除底部public void removeFooterView(View view) {int index = mFooters.indexOfValue(view);if (index < 0) return;mFooters.removeAt(index);notifyDataSetChanged();}public int getHeaderCount() {return mHeaders.size();}public int getFooterCount() {return mFooters.size();}@Overridepublic int getItemCount() {int itemCount = super.getItemCount();return itemCount + mHeaders.size() + mFooters.size();}/*** 得到列表中正常item数量** @return*/public int getOriginalItemCount() {return getItemCount() - mHeaders.size() - mFooters.size();}@Overridepublic int getItemViewType(int position) {if (isHeaderPosition(position)) {//返回该position对应的headerview的  viewTypereturn mHeaders.keyAt(position);}if (isFooterPosition(position)) {//footer类型的,需要计算一下它的position实际大小position = position - getOriginalItemCount() - mHeaders.size();return mFooters.keyAt(position);}position = position - mHeaders.size();return getItemViewType2(position);}protected int getItemViewType2(int position) {return 0;}private boolean isFooterPosition(int position) {return position >= getOriginalItemCount() + mHeaders.size();}private boolean isHeaderPosition(int position) {return position < mHeaders.size();}@NonNull@Overridepublic VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {if (mHeaders.indexOfKey(viewType) >= 0) {View view = mHeaders.get(viewType);return (VH) new RecyclerView.ViewHolder(view) {};}if (mFooters.indexOfKey(viewType) >= 0) {View view = mFooters.get(viewType);return (VH) new RecyclerView.ViewHolder(view) {};}return onCreateViewHolder2(parent, viewType);}protected abstract VH onCreateViewHolder2(ViewGroup parent, int viewType);@Overridepublic void onBindViewHolder(@NonNull VH holder, int position) {if (isHeaderPosition(position) || isFooterPosition(position))return;//列表中正常类型的itemView的 position 咱们需要减去添加headerView的个数position = position - mHeaders.size();onBindViewHolder2(holder, position);}protected abstract void onBindViewHolder2(VH holder, int position);@Overridepublic void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {if (!isHeaderPosition(holder.getAdapterPosition()) && !isFooterPosition(holder.getAdapterPosition())) {this.onViewAttachedToWindow2((VH) holder);}}public void onViewAttachedToWindow2(VH holder) {}@Overridepublic void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) {if (!isHeaderPosition(holder.getAdapterPosition()) && !isFooterPosition(holder.getAdapterPosition())) {this.onViewDetachedFromWindow2((VH) holder);}}public void onViewDetachedFromWindow2(VH holder) {}@Overridepublic void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {super.registerAdapterDataObserver(new AdapterDataObserverProxy(observer));}//如果我们先添加了headerView,而后网络数据回来了再更新到列表上//由于Paging在计算列表上item的位置时 并不会顾及我们有没有添加headerView,就会出现列表定位的问题//实际上 RecyclerView#setAdapter方法,它会给Adapter注册了一个AdapterDataObserver//咱么可以代理registerAdapterDataObserver()传递进来的observer。在各个方法的实现中,把headerView的个数算上,再中转出去即可private class AdapterDataObserverProxy extends RecyclerView.AdapterDataObserver {private RecyclerView.AdapterDataObserver mObserver;public AdapterDataObserverProxy(RecyclerView.AdapterDataObserver observer) {mObserver = observer;}public void onChanged() {mObserver.onChanged();}public void onItemRangeChanged(int positionStart, int itemCount) {mObserver.onItemRangeChanged(positionStart + mHeaders.size(), itemCount);}public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {mObserver.onItemRangeChanged(positionStart + mHeaders.size(), itemCount, payload);}public void onItemRangeInserted(int positionStart, int itemCount) {mObserver.onItemRangeInserted(positionStart + mHeaders.size(), itemCount);}public void onItemRangeRemoved(int positionStart, int itemCount) {mObserver.onItemRangeRemoved(positionStart + mHeaders.size(), itemCount);}public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {mObserver.onItemRangeMoved(fromPosition + mHeaders.size(), toPosition + mHeaders.size(), itemCount);}}
}
  • AbsPageListFragment

import com.paging.study.viewmodel.AbsViewModel;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import com.scwang.smartrefresh.layout.constant.RefreshState;
import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener;
import com.scwang.smartrefresh.layout.listener.OnRefreshListener;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;import cn.yumakeji.jetpackroomstudy.databinding.LayoutRefreshViewBinding;
import cn.yumakeji.lib_common.adapter.AbsPagedListAdapter;public abstract class AbsPageListFragment<T, M extends AbsViewModel<T>> extends Fragment implements OnRefreshListener, OnLoadMoreListener {protected Context mContext;protected Activity mActivity;private LayoutRefreshViewBinding binding;protected RecyclerView mRecyclerView;protected SmartRefreshLayout mRefreshLayout;protected EmptyView mEmptyView;protected AbsPagedListAdapter<T, RecyclerView.ViewHolder> adapter;protected M mViewModel;@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {mContext = getContext();mActivity = getActivity();/*** DataBindingUtil.inflate(inflater, getLayoutId(), container, false);* 区别* LayoutRefreshViewBinding直接可以拿到当前布局的填充器*/binding = LayoutRefreshViewBinding.inflate(inflater, container, false);mRecyclerView = binding.recyclerView;mRefreshLayout = binding.refreshLayout;mEmptyView = binding.emptyView;//配置mRefreshLayout.setEnableRefresh(true);mRefreshLayout.setEnableLoadMore(true);mRefreshLayout.setOnRefreshListener(this);mRefreshLayout.setOnLoadMoreListener(this);adapter = getAdapter();mRecyclerView.setAdapter(adapter);mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));mRecyclerView.setItemAnimator(null);onCreateViewFrame(inflater, container, savedInstanceState);genericViewModel();return binding.getRoot();}protected abstract void onCreateViewFrame(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);private void genericViewModel() {//利用 子类传递的 泛型参数实例化出absViewModel 对象。ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();Type[] arguments = type.getActualTypeArguments();if (arguments.length > 1) {Type argument = arguments[1];Class modelClaz = ((Class) argument).asSubclass(AbsViewModel.class);mViewModel = (M) new ViewModelProvider(this).get(modelClaz);//触发页面初始化数据加载的逻辑mViewModel.getPageData().observe(getViewLifecycleOwner(), pagedList -> submitList(pagedList));//监听分页时有无更多数据,以决定是否关闭上拉加载的动画mViewModel.getBoundaryPageData().observe(getViewLifecycleOwner(), hasData -> finishRefresh(hasData));}}/*** 添加数据到adapter** @param result*/public void submitList(PagedList<T> result) {//只有当新数据集合大于0 的时候,才调用adapter.submitList//否则可能会出现 页面----有数据----->被清空-----空布局if (result.size() > 0) {adapter.submitList(result);}finishRefresh(result.size() > 0);}/*** 刷新完成** @param hasData*/public void finishRefresh(boolean hasData) {PagedList<T> currentList = adapter.getCurrentList();hasData = hasData || currentList != null && currentList.size() > 0;RefreshState state = mRefreshLayout.getState();if (state.isFooter && state.isOpening) {mRefreshLayout.finishLoadMore();} else if (state.isHeader && state.isOpening) {mRefreshLayout.finishRefresh();}if (hasData) {mEmptyView.setVisibility(View.GONE);} else {mEmptyView.setVisibility(View.VISIBLE);}}public abstract AbsPagedListAdapter getAdapter();
}
  • 头布局和脚布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data><variablename="url"type="java.lang.String" /><import type="com.paging.study.fragemnt.ClickPresenter" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="20dp"android:text="我是列表尾布局" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:onClick="@{()->ClickPresenter.clickFooterBtn(url)}"android:text="尾布局上的按钮" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:scaleType="centerCrop"app:image_url="@{url}" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"app:image_url="@{url}"app:isCircle="@{true}" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:scaleType="centerInside"app:image_url="@{url}"app:radius="@{10}" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:scaleType="centerCrop"app:clip_radius="10dp"app:clip_side="left"app:image_url="@{url}" /></LinearLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data><variablename="url"type="java.lang.String" /><import type="com.paging.study.fragemnt.ClickPresenter" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="20dp"android:text="我是列表头布局" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:onClick="@{()->ClickPresenter.clickHeaderBtn(url)}"android:text="头布局上的按钮" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:scaleType="centerCrop"app:image_url="@{url}" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"app:image_url="@{url}"app:isCircle="@{true}" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:scaleType="centerInside"app:image_url="@{url}"app:radius="@{10}" /><cn.yumakeji.lib_common.view.PPImageViewandroid:layout_width="150dp"android:layout_height="150dp"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:scaleType="centerCrop"app:clip_radius="10dp"app:clip_side="left"app:image_url="@{url}" /></LinearLayout>
</layout>
  • HeaderAdapter

import com.paging.study.bean.Teacher;import cn.yumakeji.jetpackroomstudy.databinding.LayoutTeacherTypeBinding;
import cn.yumakeji.lib_common.adapter.AbsPagedListAdapter;
import cn.yumakeji.lib_common.global.AppGlobals;public class HeaderAdapter extends AbsPagedListAdapter<Teacher.RecordsBean, HeaderAdapter.ViewHolder> {protected Context mContext;public HeaderAdapter(Context context) {super(new DiffUtil.ItemCallback<Teacher.RecordsBean>() {@Overridepublic boolean areItemsTheSame(@NonNull Teacher.RecordsBean oldItem, @NonNull Teacher.RecordsBean newItem) {//判断item是否相等(这里根据id来判断)return oldItem.getPaintingId() == newItem.getPaintingId();}@Overridepublic boolean areContentsTheSame(@NonNull Teacher.RecordsBean oldItem, @NonNull Teacher.RecordsBean newItem) {//判断内容是否相等(重写equals方法)return oldItem.equals(newItem);}});this.mContext = context;}@Overrideprotected ViewHolder onCreateViewHolder2(ViewGroup parent, int viewType) {LayoutTeacherTypeBinding binding = LayoutTeacherTypeBinding.inflate(LayoutInflater.from(mContext), parent, false);return new ViewHolder(binding.getRoot(), binding);}@Overrideprotected void onBindViewHolder2(ViewHolder holder, int position) {holder.binData(getItem(position));holder.itemView.setOnClickListener(v -> {Toast.makeText(AppGlobals.getApplication(), getItem(position).getPaintingName(), Toast.LENGTH_LONG).show();});}public class ViewHolder extends RecyclerView.ViewHolder {private LayoutTeacherTypeBinding mBinding;public ViewHolder(@NonNull View itemView, LayoutTeacherTypeBinding binding) {super(itemView);this.mBinding = binding;}public void binData(Teacher.RecordsBean item) {mBinding.setItem(item);}}
}
  • ViewModel

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;public class HeaderViewModel extends AbsViewModel<Teacher.RecordsBean> {public HeaderViewModel(@NonNull Application application) {super(application);}/*** 同步位置标记,防止paging和我们自己的分页重复*/private AtomicBoolean loadAfter = new AtomicBoolean();@Overrideprotected DataSource createDataSource() {return new TeacherDataSource();}/*** DataSource<Key,Value>数据源:* key 对应加载数据的条件信息,value对应数据实体类* <p>* PageKeyedDataSource<Key,Value>:* 适用于目标数据根据页面信息请求数据的场景* <p>* ItemKeyedDataSource<Key,Value>:* 适用于目标数据的加载依赖特定item的信息* <p>* PositionalDataSource<Key,Value>:* 适用于目标数据总数固定,通过特定的位置加载数据*/class TeacherDataSource extends ItemKeyedDataSource<String, Teacher.RecordsBean> {@Overridepublic void loadInitial(@NonNull LoadInitialParams<String> params,@NonNull LoadInitialCallback<Teacher.RecordsBean> callback) {pageCurrent = 1;//加载初始化数据的loadData(params.requestedInitialKey, callback);}@Overridepublic void loadAfter(@NonNull LoadParams<String> params, @NonNull LoadCallback<Teacher.RecordsBean> callback) {//向后加载分页数据的callback.onResult(Collections.<Teacher.RecordsBean>emptyList());}@Overridepublic void loadBefore(@NonNull LoadParams<String> params, @NonNull LoadCallback<Teacher.RecordsBean> callback) {callback.onResult(Collections.<Teacher.RecordsBean>emptyList());//能够向前加载数据的(例如初始化进入加载的是第3页,向上翻的时候加载第二第一页)}@NonNull@Overridepublic String getKey(@NonNull Teacher.RecordsBean item) {//通过最后一条item的信息加载数据return item.getPaintingId();}}/*** 进行网络请求** @param key* @param callback*/private void loadData(String key, ItemKeyedDataSource.LoadCallback<Teacher.RecordsBean> callback) {if (!key.equals("0")) {loadAfter.set(true);}/*** 当前线程为子线程** 这里有坑,一定要使用同步请求,因为方法执行完毕是,监听者就收到监听*/Map<String, Object> map = new ArrayMap<>();map.put("searchContent", "");map.put("size", config.pageSize);map.put("current", pageCurrent);PagingHttpClient.getInstance().get("home/paintingList", map, new PagingHttpCallback<Teacher>() {@Overrideprotected void onNext(Teacher data) {//标记分页页数if (data.getRecords() != null && data.getRecords().size() > 0) {pageCurrent = data.getCurrent() + 1;}callback.onResult(data.getRecords());if (!key.equals("0")) {//通过LiveData发送数据,告诉UI层 是否应该主动关闭上拉加载分页的动画getBoundaryPageData().postValue(data.getRecords().size() > 0);loadAfter.set(false);}Log.e("TTT", "data===>" + data.toString());}@Overrideprotected void onFail(int code, String message) {}});}
}
  • 调用
public class HeaderAndFooterFragment extends AbsPageListFragment<Teacher.RecordsBean, HeaderViewModel> {private HeaderViewBinding mHeaderViewBinding;private FooterViewBinding mFooterViewBinding;private HeaderAndFooterFragment() {}public static HeaderAndFooterFragment newInstance() {HeaderAndFooterFragment fragment = new HeaderAndFooterFragment();return fragment;}@Overrideprotected void onCreateViewFrame(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {mRefreshLayout.setEnableLoadMore(false);initHeader();initFooter();}private void initFooter() {mFooterViewBinding = FooterViewBinding.inflate(LayoutInflater.from(mActivity), mRecyclerView, false);mFooterViewBinding.setUrl("http://img5.imgtn.bdimg.com/it/u=3416930540,2666479333&fm=26&gp=0.jpg");adapter.addFooterView(mFooterViewBinding.getRoot());}private void initHeader() {mHeaderViewBinding = HeaderViewBinding.inflate(LayoutInflater.from(mActivity), mRecyclerView, false);mHeaderViewBinding.setUrl("http://n.sinaimg.cn/front/379/w750h429/20181218/eElr-hqhtqsp9656052.jpg");adapter.addHeaderView(mHeaderViewBinding.getRoot());}@Overridepublic AbsPagedListAdapter getAdapter() {return new HeaderAdapter(mContext);}@Overridepublic void onLoadMore(@NonNull RefreshLayout refreshLayout) {}//invalidate 之后Paging会重新创建一个DataSource 重新调用它的loadInitial方法加载初始化数据//详情见:LivePagedListBuilder#compute方法@Overridepublic void onRefresh(@NonNull RefreshLayout refreshLayout) {mViewModel.getDataSource().invalidate();}
}
  • ClickPresenter
public class ClickPresenter {public static void clickHeaderBtn(String text) {Toast.makeText(AppGlobals.getApplication(), text, Toast.LENGTH_LONG).show();}public static void clickFooterBtn(String text) {Toast.makeText(AppGlobals.getApplication(), text, Toast.LENGTH_LONG).show();}
}

自定义ImageView
  • 引入
//图片加载api 'com.github.bumptech.glide:glide:4.11.0'api 'jp.wasabeef:glide-transformations:4.0.0'annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
  • PPImageView

public class PPImageView extends AppCompatImageView {public PPImageView(Context context) {this(context, null);}public PPImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public PPImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);ViewHelper.setViewOutline(this, attrs, defStyleAttr, 0);}public void setImageUrl(String imageUrl) {setImageUrl(this, imageUrl, false);}@BindingAdapter(value = {"image_url", "isCircle"})public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle) {view.setImageUrl(view, imageUrl, isCircle, 0);}@BindingAdapter(value = {"image_url", "isCircle", "radius"}, requireAll = false)public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle, int radius) {RequestBuilder<Drawable> builder = Glide.with(view).load(imageUrl);if (isCircle) {builder.transform(new CircleCrop());} else if (radius > 0) {builder.transform(new RoundedCornersTransformation(PixUtils.dp2px(radius), 0));}ViewGroup.LayoutParams layoutParams = view.getLayoutParams();if (layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0) {builder.override(layoutParams.width, layoutParams.height);}builder.into(view);}public void bindData(int widthPx, int heightPx, int marginLeft, String imageUrl) {bindData(widthPx, heightPx, marginLeft, PixUtils.getScreenWidth(), PixUtils.getScreenWidth(), imageUrl);}public void bindData(int widthPx, int heightPx, final int marginLeft, final int maxWidth, final int maxHeight, String imageUrl) {if (TextUtils.isEmpty(imageUrl)) {setVisibility(GONE);return;} else {setVisibility(VISIBLE);}if (widthPx <= 0 || heightPx <= 0) {Glide.with(this).load(imageUrl).into(new SimpleTarget<Drawable>() {@Overridepublic void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {int height = resource.getIntrinsicHeight();int width = resource.getIntrinsicWidth();setSize(width, height, marginLeft, maxWidth, maxHeight);setImageDrawable(resource);}});return;}setSize(widthPx, heightPx, marginLeft, maxWidth, maxHeight);setImageUrl(this, imageUrl, false);}private void setSize(int width, int height, int marginLeft, int maxWidth, int maxHeight) {int finalWidth, finalHeight;if (width > height) {finalWidth = maxWidth;finalHeight = (int) (height / (width * 1.0f / finalWidth));} else {finalHeight = maxHeight;finalWidth = (int) (width / (height * 1.0f / finalHeight));}ViewGroup.LayoutParams params = getLayoutParams();params.width = finalWidth;params.height = finalHeight;if (params instanceof FrameLayout.LayoutParams) {((FrameLayout.LayoutParams) params).leftMargin = height > width ? PixUtils.dp2px(marginLeft) : 0;} else if (params instanceof LinearLayout.LayoutParams) {((LinearLayout.LayoutParams) params).leftMargin = height > width ? PixUtils.dp2px(marginLeft) : 0;}setLayoutParams(params);}@BindingAdapter(value = {"blur_url", "radius"})public static void setBlurImageUrl(ImageView imageView, String blurUrl, int radius) {Glide.with(imageView).load(blurUrl).override(radius).transform(new BlurTransformation()).dontAnimate().into(new SimpleTarget<Drawable>() {@Overridepublic void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {imageView.setBackground(resource);}});}
}
  • ViewHelper

public class ViewHelper {public static final int RADIUS_ALL = 0;public static final int RADIUS_LEFT = 1;public static final int RADIUS_TOP = 2;public static final int RADIUS_RIGHT = 3;public static final int RADIUS_BOTTOM = 4;public static void setViewOutline(View view, AttributeSet attributes, int defStyleAttr, int defStyleRes) {TypedArray array = view.getContext().obtainStyledAttributes(attributes, R.styleable.viewOutLineStrategy, defStyleAttr, defStyleRes);int radius = array.getDimensionPixelSize(R.styleable.viewOutLineStrategy_clip_radius, 0);int hideSide = array.getInt(R.styleable.viewOutLineStrategy_clip_side, 0);array.recycle();setViewOutline(view, radius, hideSide);}public static void setViewOutline(View owner, final int radius, final int radiusSide) {owner.setOutlineProvider(new ViewOutlineProvider() {@Override@TargetApi(21)public void getOutline(View view, Outline outline) {int w = view.getWidth(), h = view.getHeight();if (w == 0 || h == 0) {return;}if (radiusSide != RADIUS_ALL) {int left = 0, top = 0, right = w, bottom = h;if (radiusSide == RADIUS_LEFT) {right += radius;} else if (radiusSide == RADIUS_TOP) {bottom += radius;} else if (radiusSide == RADIUS_RIGHT) {left -= radius;} else if (radiusSide == RADIUS_BOTTOM) {top -= radius;}outline.setRoundRect(left, top, right, bottom, radius);return;}int top = 0, bottom = h, left = 0, right = w;if (radius <= 0) {outline.setRect(left, top, right, bottom);} else {outline.setRoundRect(left, top, right, bottom, radius);}}});owner.setClipToOutline(radius > 0);owner.invalidate();}
}
  • viewOutLineStrategy
<resources><declare-styleable name="viewOutLineStrategy"><attr name="clip_radius" format="dimension"></attr><attr name="clip_side" format="enum"><enum name="all" value="0"></enum><enum name="left" value="1"></enum><enum name="top" value="2"></enum><enum name="right" value="3"></enum><enum name="bottom" value="4"></enum></attr></declare-styleable>
</resources>

Demo地址:https://gitee.com/huangxiaoguo/jetpackandrxjava

第七章:Paging添加header和footer相关推荐

  1. RecyclerView添加header与footer

    前言 这次主要关于RecyclerView添加header和footer的实现方法,我们都知道,在使用ListView的时候我们能自由的给自己的ListView添加头部与尾部.使用addHeaderV ...

  2. StroyBoard中UICollectionView中添加Header和footer

    到Storyboard中,选择collection view controller中的"Collection View".在Attributes inspector中,选择&quo ...

  3. zabbix专题:第七章 添加图像Graphs,添加聚合图形Screens

    zabbix专题:第七章 添加图像Graphs,添加展示屏Screens 对Linux有兴趣的朋友加入QQ群:476794643 在线交流 本文防盗链:http://zhang789.blog.51c ...

  4. 第七章 Web开发实战2——商品详情页

    本章以京东商品详情页为例,京东商品详情页虽然仅是单个页面,但是其数据聚合源是非常多的,除了一些实时性要求比较高的如价格.库存.服务支持等通过AJAX异步加载加载之外,其他的数据都是在后端做数据聚合然后 ...

  5. OpenResty学习——第七章 Web开发实战2——商品详情页

    本文转自https://blog.csdn.net/jinnianshilongnian/article/details/84704211,好文要顶,感谢博主分享! 本章以京东商品详情页为例,京东商品 ...

  6. Linux内核分析 读书笔记 (第七章)

    第七章 链接 1.链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储器并执行. 2.链接可以执行于编译时,也就是在源代码被翻译成机器代码时:也可以执行于 ...

  7. 实现带header和footer功能的RecyclerView——完善篇

    在上一篇文章中我们实现了实现带header和footer功能的RecyclerView,见  实现带header和footer功能的RecyclerView 但是由于加入了header,item的po ...

  8. 实现带header和footer功能的RecyclerView

    这个项目很简单,其实一年前就开发完成了,但是一直没闲下来去整理. RecyclerView是Android 5.0版本引入的一个新的组件,目的是在一些场景中取代之前ListView和GridView, ...

  9. 《深入理解计算机系统》第七章 链接

    <深入理解计算机系统>第七章 链接 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(货被拷贝)到存储器并执行. 链接的时机 编译时,也就是在源代码被翻译成 ...

最新文章

  1. 幸运数字Ⅱ(树型结构构造答案,打表)难度⭐⭐
  2. Py之pywin32:Python库之pywin32的简介、安装、使用方法之详细攻略
  3. 快速谱峭度matlab,一种基于快速谱峭度分析的泵潜在空化故障检测方法与流程
  4. C#数据同步中基本步骤和用到的相关函数
  5. 详细解析Raid0、Raid0+1、Raid1、Raid5四者的区别
  6. BZOJ4293 Siano
  7. java 无锁框架_高性能无锁并发框架 Disruptor,太强了!
  8. 一年级学python_你是如何自学 Python 的?
  9. win10虚拟桌面使用方法-提高工作效率
  10. HDU——T 1573 X问题
  11. 两轮差速驱动机器人运动模型及应用分析(图片版)
  12. Docker容器中运行Ubuntu系统
  13. .NET 判断进程是否存在
  14. 路由器连接、静态路由配置实例
  15. matlab中plot3,mesh,grid三者画图的区别
  16. Markdown的下载与应用
  17. Hibernate VS iBATIS (转自ITEYE davy)
  18. tga缩略图预览_甜蜜的缩略图预览库
  19. 老年手机英文改中文_老年手机设置成英文怎么办
  20. 【Java】文如何制作帮助文档

热门文章

  1. 分享抖音账号初期运营技巧,直接可复制的运营流程
  2. 面向对象之对象的多态性
  3. 第2章 多维数据结构与运算答案
  4. [BZOJ1513]Tet-Tetris 3D
  5. 【校招VIP】产品设计分析之文案功底考察
  6. 量子计算机当游戏服务器,为何需要量子计算机
  7. 如何使用eclipse开发android
  8. anacnda 子环境管理
  9. Product Quantization for Nearest Neighbor Search论文实验
  10. 活码生成器是什么?怎么用活码生成器制作活码?有没有免费的活码生成器?