PS:好長時間不寫博客了,起初是不知道寫些什么,后來接觸了到了很多東西,原本看似簡單的東西,背后都隱藏着巨大的秘密,想handler的使用,一般情況下會引起內存泄漏問題,想着找到方法結局不就得了嗎,可是誰想查閱資料發現,這個東西沒想到牽扯的這么深, Activity -> handler -> message -> queue -> UI線程作為GC Root引用鏈,看都看懵逼了,趕緊看一些稍微簡單的UI 實現,突發奇想,RecyclerView的混合布局界面的實現。如下圖
。。。
像這些布局,可以用listview來實現,也可以RecyclerView來實現,每個布局文件都是不一樣的,第一張圖:上面是一行三個圖,下面是一行四個圖。第二張圖一行分左右。作為一個新手來說,估計就有點難以招架了,不過用recyclerview就比較好實現了,只要指定Type,來加載不同的布局就可以。下面來簡單實現一個簡單點的。有一行兩個數據,有一行一個數據界面。

我們可以看到,這個RecyclerView中有多種item顯示出來,那么具體怎么實現呢,其實在RecyclerView中,我們可以重寫方法getItemViewType(),這個方法會傳進一個參數position表示當前是第幾個Item,然后我們可以通過position拿到當前的Item對象,然后判斷這個item對象需要那種視圖,返回一個int類型的視圖標志,然后在onCreatViewHolder方法中給引入布局,這樣就能夠實現多種item顯示了,我們先來看一下,一共要實現多少方法,他們分別是什么,我都加了注釋。
/**
* 加載視圖
* */
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
/**
* 加載數據
* */
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
/**
* 返回數據數量
* */
@Override
public int getItemCount() {
return 0;
}
/**
* 返回數據類型
* */
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
1:Item xml布局文件和Bean類,ViewHolder創建。
注:bean因demo簡潔,並沒有用到bean類。一共兩個布局,這里只給出一個,類似寫出即可。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:background="@color/colorPrimary"
android:id="@+id/id_one_tv1"/>
</LinearLayout>
public class TypeViewHolderOne extends RecyclerView.ViewHolder {
public TextView textView;
public TypeViewHolderOne(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.id_one_tv1);
}
}
2:方法講解
注意:一般情況下后天會給移動端返回一串JSON字符串,里面有一些數據是需要自己來專門跳出來的,
方法:
- 你可以寫多個list來存放不同的數據,但是如果是多個的話,返回的數據就一串字符串,你不僅要挑出來,還要存放在多個list里面,下次下拉刷新時還要在多個list中累加數據,這樣就有點麻煩了
- 不挑出來直接就只判斷type,然后根據type去調用不同的layout,然后在綁定數據的方法中再次判斷是不是自己想要的ViewHolder,最后賦值顯示。
返回type類型方法
//返回類型,有單列顯示還是雙列顯示
@Override
public int getItemViewType(int position) {
return mList.get(position).getType();
}
我們重寫了getItemViewType()方法后,就要寫不同的item(布局文件),然后在onCreatViewHolder方法引入布局。這里的類型就簡單直接寫的1和2。
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//視圖顯示
//創建view視圖
switch (viewType) {
case 1:
return new TypeViewHolderOne(layoutInflater.inflate(R.layout.model_view1, parent, false));
case 2:
return new TypeViewHolderTwo(layoutInflater.inflate(R.layout.model_view2, parent, false));
}
return null;
}
我們看到的TypeViewHolderOne(View view);這個方法是自定義的需要繼承RecyclerView.ViewHolder
public class TypeViewHolderOne extends RecyclerView.ViewHolder {
public TextView textView;
public TypeViewHolderOne(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.id_one_tv1);
}
}
寫到這里,布局有了,viewholder有了,數據的判斷類型有了,就差展示了,所以重寫onBindViewHolder方法
/**
* 方法作用:綁定數據,
* 方法描述:根據holder對控件進行賦值,同時如果有回調接口,在該方法中寫。
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//優化 綁定數據
if(holder instanceof TypeViewHolderOne){//判斷是哪個對象
TypeViewHolderOne viewHolderOne= (TypeViewHolderOne) holder;
viewHolderOne.textView.setText(mList.get(position).getName());
}else if(holder instanceof TypeViewHolderTwo){
TypeViewHolderTwo viewHolderTwo= (TypeViewHolderTwo) holder;
viewHolderTwo.textView1.setText(mList.get(position).getName());
viewHolderTwo.textView2.setText("類型"+mList.get(position).getType());
}
}
所有預備工作已經做完了,開始賦值。
recyclerViewAdapter = new RecyclerViewAdapter2(this, list);
recyclerView.setAdapter(recyclerViewAdapter);
//本地模擬數據
public void addData() {
int a=(int)(Math.random()*10);
TypeBean typeBean;
for (int i = 0; i < 10; i++) {
typeBean = new TypeBean();
typeBean.setName("樣式:" + (i + 1));
typeBean.setType(1);
if (i == a) {
typeBean.setType(2);
}
list.add(typeBean);
}
recyclerViewAdapter.notifyDataSetChanged();
}
現在還不能運行,因為還沒有給recyclerview指定一個布局格式,下面是指定了布局格式,一共兩列,獲取一開始咱們設置的type,如果type=2,則要讓他獨自占2列,也就是說,他自己一行。
final GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int itemViewType = recyclerView.getAdapter().getItemViewType(position);
if (itemViewType == 2) {
return 2;
}
return 1;
}
});
裝飾可寫可不寫
//設置各個item的裝飾,如間距,大小等,可寫可不寫,不寫可以在xml文件中設置。
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
//super.getItemOffsets(outRect, view, parent, state);
//拿到Grid管理器
GridLayoutManager.LayoutParams layoutParams = (GridLayoutManager.LayoutParams) view.getLayoutParams();
//拿到grid管理器所設置的總列數
int spanSize = layoutParams.getSpanSize();
//拿到當前所在列數
int spanIndex = layoutParams.getSpanIndex();
//設置頂部間距
outRect.top = 20;
if (spanSize != gridLayoutManager.getSpanCount()) {
if (spanIndex == 1) {
outRect.left = 10;
} else {
outRect.right = 10;
}
}
}
});
recyclerView.setLayoutManager(gridLayoutManager);
現在運行試試吧
總結:
1:使用RecyclerView必須要寫的就是適配器要繼承RecyclerView.Adapter<RecyclerView.ViewHolder>,當然這里面你也可以自定義。
2:加載多個布局文件時(item)需要加入Type字段去判斷是哪一個item。在activity中加載recyclerview時,要加入布局樣式,比如說,普通的LinearLayoutManager,或者GridLayoutManager,StaggeredGridLayoutManager。不加是不顯示的。
