特別說明
- 當(dāng)前博客平臺賬號已廢棄,如果有使用細(xì)節(jié)問題請前往我個人博客平臺 HuRuWo的技術(shù)小站進(jìn)行討論交流例驹。直接在其他平臺留言可能無法及時得到回復(fù)。
文章首發(fā)于個人博客HuRuWo的技術(shù)小站,如果本文非vip用戶無法完全瀏覽或者圖片無法打開帕棉,可前往個人博客文章地址查看文章并留言討論夫凸。
本篇文章個人博客文章地址EasyRecyclerView擴(kuò)展的自定義RecyclerView控件,點擊鏈接直接前往。
更多技術(shù)文章訪問本人博客HuRuWo的技術(shù)小站蚕愤,包括 Electron從零開發(fā) Android 逆向 app 微信數(shù)據(jù)抓取 抖音數(shù)據(jù)抓取 閑魚數(shù)據(jù)抓取 小紅書數(shù)據(jù)抓取 其他軟件爬蟲 等技術(shù)文章
介紹:
將開發(fā)中常用的RecyclerView的各種需求封裝進(jìn)庫。提升開發(fā)效率饺蚊。
重點在Adapter與viewholder的封裝萍诱。他們之間徹底解耦。
adapter工作更少污呼,僅負(fù)責(zé)業(yè)務(wù)邏輯裕坊。所以如果你使用mvp架構(gòu)可以放進(jìn)presenter層。
viewholder負(fù)責(zé)View展示與Adapter沒有任何耦合燕酷,將可以到處復(fù)用籍凝。并不會影響運行效率。
并且adapter支持?jǐn)?shù)據(jù)管理苗缩,Header與Footer添加饵蒂,加載更多。沒有更多酱讶。加載錯誤
使用了部分Malinskiy/SuperRecyclerView的代碼退盯,將更多功能交給了adapter實現(xiàn)。
項目地址:
https://github.com/Jude95/EasyRecyclerView
引用
compile 'com.jude:easyrecyclerview:4.4.0'
當(dāng)然還要添加:
compile 'com.android.support:recyclerview-v7:24.2.0'
具體使用
布局引用:
<com.jude.easyrecyclerview.EasyRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_empty="@layout/view_empty"
app:layout_progress="@layout/view_progress"
app:layout_error="@layout/view_error"
app:recyclerClipToPadding="true"
app:recyclerPadding="8dp"
app:recyclerPaddingTop="8dp"
app:recyclerPaddingBottom="8dp"
app:recyclerPaddingLeft="8dp"
app:recyclerPaddingRight="8dp"
app:scrollbarStyle="insideOverlay"http://insideOverlay or insideInset or outsideOverlay or outsideInset
app:scrollbars="none"http://none or vertical or horizontal
/>
所有屬性都不是必須泻肯。 注意EasyRecyclerView本質(zhì)并不是一個RecyclerView
設(shè)置空白View&加載View&錯誤View
xml中
app:layout_empty="@layout/view_empty"
app:layout_progress="@layout/view_progress"
app:layout_error="@layout/view_error"
代碼中
void setEmptyView(View emptyView)
void setProgressView(View progressView)
void setErrorView(View errorView)
然后可以隨時顯示他們
void showEmpty()
void showProgress()
void showError()
void showRecycler()
RecyclerArrayAdapter
這個Adapter與本RecyclerView沒有任何耦合渊迁。你可以使用其他adapter。也可以把本adapter用于其他RecyclerView
整合了數(shù)據(jù)增刪的功能
void add(T object);
void addAll(Collection<? extends T> collection);
void addAll(T ... items);
void insert(T object, int index);
void remove(T object)
void clear()
void sort(Comparator<? super T> comparator)
整合的Header與Footer的實現(xiàn)
void addHeader(ItemView view)
void addFooter(ItemView view)
ItemView不是view而是view生成器
對應(yīng)Adapter的onCreate與onBind方法,所以onCreate后會多次onBind灶挟。
建議數(shù)據(jù)加載完畢后再add琉朽。onCreate里初始化UI。不使用onBind稚铣。
public interface ItemView {
View onCreateView(ViewGroup parent);
void onBindView(View itemView);
}
## Header與Footer完美適配
LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager
在GridLayoutManager模式中需額外加一句
```java
//make adapter obtain a LookUp for LayoutManager箱叁,param is maxSpan。
gridLayoutManager.setSpanSizeLookup(adapter.obtainGridSpanSizeLookUp(2));
整合OnItemClickListener與OnItemLongClickListener
adapter.setOnItemClickListener(new RecyclerArrayAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
//position不包含Header
}
});
adapter.setOnItemLongClickListener(new RecyclerArrayAdapter.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(int position) {
return true;
}
});
與在ViewHolder中給itemView設(shè)置OnClickListener等效榛泛。若重復(fù)設(shè)置ViewHolder中的設(shè)置會被替換蝌蹂。
如果在RecyclerView布局以后再設(shè)置。需要再使用'notifyDataSetChange()'曹锨。
下面的功能是在adapter最后添加一個footer孤个。來顯示各種狀態(tài)。
加載更多
void setMore(final int res,OnMoreListener listener);
void setMore(final View view,OnMoreListener listener);
注意一定當(dāng)添加0條數(shù)據(jù)或null時,會結(jié)束加載更多,顯示沒有更多沛简。
也可以在最后一頁手動調(diào)用adapter.stopMore();
加載錯誤
void setError(final int res,OnErrorListener listener)
void setError(final View view,OnErrorListener listener)
- adapter.pauseMore()暫停加載更多齐鲤,顯示錯誤View斥废。
- 暫停時如果再次添加數(shù)據(jù)。自動恢復(fù)加載更多给郊。
- 當(dāng)錯誤View再次被顯示時牡肉。會恢復(fù)成加載更多view。并回掉加載更多;
- adapter.resumeMore()繼續(xù)加載更多淆九,顯示加載更多View统锤,并立即回調(diào)加載更多。
- 比如你可以給錯誤View設(shè)置點擊重試炭庙。點擊調(diào)用resumeMore饲窿。
沒有更多
在adapter里設(shè)置,當(dāng)停止加載后就會顯示在最后一個焕蹄。
void setNoMore(final int res,OnNoMoreListener listener)
void setNoMore(final View view,OnNoMoreListener listener)
BaseViewHolder<M>
這個ViewHolder將每個item與adapter解耦逾雄。adapter只管實例化對應(yīng)ViewHolder.每個Item的view生成,findviewbyid,UI修改都由viewHolder自己管理。
列如:
public class PersonViewHolder extends BaseViewHolder<Person> {
private TextView mTv_name;
private SimpleDraweeView mImg_face;
private TextView mTv_sign;
public PersonViewHolder(ViewGroup parent) {
super(parent,R.layout.item_person);
mTv_name = $(R.id.person_name);
mTv_sign = $(R.id.person_sign);
mImg_face = $(R.id.person_face);
}
@Override
public void setData(final Person person){
mTv_name.setText(person.getName());
mTv_sign.setText(person.getSign());
mImg_face.setImageURI(Uri.parse(person.getFace()));
}
}
-----------------------------------------------------------------------
public class PersonAdapter extends RecyclerArrayAdapter<Person> {
public PersonAdapter(Context context) {
super(context);
}
@Override
public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) {
return new PersonViewHolder(parent);
}
}
Decoration
這里提供了2種常用Decoration供大家使用腻脏。
DividerDecoration
通常用在LinearLayoutManager的情況下鸦泳。在item之間添加分割線。
DividerDecoration itemDecoration = new DividerDecoration(Color.GRAY, Util.dip2px(this,0.5f), Util.dip2px(this,72),0);//顏色 & 高度 & 左邊距 & 右邊距
itemDecoration.setDrawLastItem(true);//有時候你不想讓最后一個item有分割線,默認(rèn)true.
itemDecoration.setDrawHeaderFooter(false);//是否對Header于Footer有效,默認(rèn)false.
recyclerView.addItemDecoration(itemDecoration);
這是效果:
SpaceDecoration
通常用于GridLayoutManager和StaggeredGridLayoutManager永品。在View之間添加間距做鹰。
SpaceDecoration itemDecoration = new SpaceDecoration((int) Utils.convertDpToPixel(8,this));//參數(shù)是距離寬度
itemDecoration.setPaddingEdgeSide(true);//是否為左右2邊添加padding.默認(rèn)true.
itemDecoration.setPaddingStart(true);//是否在給第一行的item添加上padding(不包含header).默認(rèn)true.
itemDecoration.setPaddingHeaderFooter(false);//是否對Header于Footer有效,默認(rèn)false.
recyclerView.addItemDecoration(itemDecoration);
這是效果:
另外:
雖然與我的庫沒什么關(guān)系,但很多人在問就寫一下吧腐碱。item的水波紋效果
在你item的View加上這一條屬性:
android:foreground="?android:attr/selectableItemBackground"
就好了...
實戰(zhàn):
布局文件:
main_activity.xml
<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'DejaVu Sans Mono';font-size:13.5pt;"><?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.easyrecyviewtest.MainActivity">
<com.jude.easyrecyclerview.EasyRecyclerView android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:recyclerClipToPadding="true"
app:recyclerPadding="8dp"
app:recyclerPaddingBottom="8dp"
app:recyclerPaddingLeft="8dp"
app:recyclerPaddingRight="8dp"
app:recyclerPaddingTop="8dp"
app:scrollbarStyle="insideOverlay"
app:scrollbars="none" />
</RelativeLayout></pre>
view_more.xml
<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'DejaVu Sans Mono';font-size:13.5pt;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center">
<ProgressBar android:layout_width="28dp"
android:layout_height="28dp"
android:indeterminate="false"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="更多加載中……"/>
</LinearLayout></pre>
view_nomore.xml
<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'DejaVu Sans Mono';font-size:13.5pt;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="48dp">
<TextView android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="(* ̄ω ̄) 沒有更多了"/>
</LinearLayout></pre>
具體步驟:
mainactivity-->setadapter-->viewholder-->picture
mainactivity
public class MainActivity extends AppCompatActivity {
private EasyRecyclerView recyclerView;
private ImageAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (EasyRecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));
recyclerView.setAdapter(adapter = new ImageAdapter(this));
//添加headrd
// adapter.addHeader(new RecyclerArrayAdapter.ItemView() {
// @Override
// public View onCreateView(ViewGroup parent) {
// RollPagerView header = new RollPagerView(MainActivity.this);
// header.setHintView(new ColorPointHintView(MainActivity.this, Color.YELLOW,Color.GRAY));
// header.setHintPadding(0, 0, 0, (int) convertDpToPixel(8, MainActivity.this));
// header.setPlayDelay(2000);
// header.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) Utils.convertDpToPixel(200, StaggeredGridActivity.this)));
// header.setAdapter(new BannerAdapter(MainActivity.this));
// return header;
// }
//
// @Override
// public void onBindView(View headerView) {
//
// }
// });
//添加邊框
SpaceDecoration itemDecoration = new SpaceDecoration((int) convertDpToPixel(8,this));
itemDecoration.setPaddingEdgeSide(true);
itemDecoration.setPaddingStart(true);
itemDecoration.setPaddingHeaderFooter(false);
recyclerView.addItemDecoration(itemDecoration);
//更多加載
adapter.setMore(R.layout.view_more, new RecyclerArrayAdapter.OnMoreListener() {
@Override
public void onMoreShow() {
addData();
}
@Override
public void onMoreClick() {
}
});
//移除加載更多頁腳
//adapter.stopMore();
//沒有更多
adapter.setNoMore(R.layout.view_nomore);
//寫刷新事件
recyclerView.setRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
//adapter.clear();
// adapter.addAll(DataProvider.getPictures(0));
}
},1000);
}
});
//點擊事件
adapter.setOnItemClickListener(new RecyclerArrayAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
//position不包含Header
Toast.makeText(getApplicationContext(),
position+"", Toast.LENGTH_LONG).show();
}
});
adapter.setOnItemLongClickListener(new RecyclerArrayAdapter.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(int position) {
Toast.makeText(getApplicationContext(),
position+"", Toast.LENGTH_LONG).show();
return true;
}
});
addData();
}
private void addData(){
recyclerView.postDelayed(new Runnable() {
@Override
public void run() {
adapter.addAll(DataProvider.getPictures(0));
}
},1000);
}
public static float convertDpToPixel(float dp, Context context){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return px;
}
}
ImageAdapter
public class ImageAdapter extends RecyclerArrayAdapter<Picture> {
public ImageAdapter(Context context) {
super(context);
}
@Override
public BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) {
return new ImageViewHolder(parent);
}
}
ImageViewHolder
public class ImageViewHolder extends BaseViewHolder<Picture> {
ImageView imgPicture;
public ImageViewHolder(ViewGroup parent) {
super(new ImageView(parent.getContext()));
imgPicture = (ImageView) itemView;
imgPicture.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
imgPicture.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
@Override
public void setData(Picture data) {
ViewGroup.LayoutParams params = imgPicture.getLayoutParams();
DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
int width = dm.widthPixels/2;//寬度為屏幕寬度一半
int height = data.getHeight()*width/data.getWidth();//計算View的高度
params.height = height;
imgPicture.setLayoutParams(params);
//imgPicture.setImageResource(R.mipmap.ic_launcher);
Glide.with(getContext())
.load(data.getSrc()+"?imageView2/0/w/"+ width)
.into(imgPicture);
}
}
Picture
public class Picture {
int width;
int height;
String src;
public Picture(int width, int height, String src) {
this.width = width;
this.height = height;
this.src = src;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
}
DataProvider
public class DataProvider {
static final Picture[] VIRTUAL_PICTURE = {
new Picture(566,800,"http://o84n5syhk.bkt.clouddn.com/57154327_p0.png"),
new Picture(2126,1181,"http://o84n5syhk.bkt.clouddn.com/57180221_p0.jpg"),
new Picture(1142,800,"http://o84n5syhk.bkt.clouddn.com/57174070_p0.jpg"),
new Picture(550,778,"http://o84n5syhk.bkt.clouddn.com/57166531_p0.jpg"),
new Picture(1085,755,"http://o84n5syhk.bkt.clouddn.com/57151022_p0.jpg"),
new Picture(656,550,"http://o84n5syhk.bkt.clouddn.com/57172236_p0.jpg"),
new Picture(1920,938,"http://o84n5syhk.bkt.clouddn.com/57174564_p0.jpg"),
new Picture(1024,683,"http://o84n5syhk.bkt.clouddn.com/57156832_p0.jpg"),
new Picture(723,1000,"http://o84n5syhk.bkt.clouddn.com/57151474_p0.png"),
new Picture(2000,1667,"http://o84n5syhk.bkt.clouddn.com/57156623_p0.png"),
};
public static ArrayList<Picture> getPictures(int page){
ArrayList<Picture> arrayList = new ArrayList<>();
for (int i = 0; i < VIRTUAL_PICTURE.length; i++) {
arrayList.add(VIRTUAL_PICTURE[i]);
}
return arrayList;
}
}
測試:
使用注意事項:
由于ViewHolder使用的是ViewGroup作為parent
所以布局文件需要在最外面放置一個相對布局作為父容器誊垢。
類似:
<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'DejaVu Sans Mono';font-size:13.5pt;"><?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="72dp"
android:gravity="center_vertical"
android:foreground="?android:attr/selectableItemBackground"
android:background="@android:color/white">
<ImageView android:id="@+id/person_face"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginLeft="16dp"
android:layout_centerVertical="true"
/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/person_face"
android:gravity="center_vertical"
android:layout_marginLeft="16dp"
android:orientation="vertical">
<TextView android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:gravity="center_vertical"
/>
<TextView android:id="@+id/person_sign"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="@android:color/darker_gray"
/>
</LinearLayout></RelativeLayout></pre>