原文出處: 李詩雨
開始逐漸領略到ItemDecoration的美~
今天讓我 使用 ItemDecoration 來完成 可推動的懸浮導航欄的效果,最終實現的效果如下圖:
具體實現步驟如下:
根據我前面的文章所講的RecyclerView的基本使用,我們先來完成基本的recyclerView:
第一步:布局里寫一個RecyclerView
第二步:實例化
1
|
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
|
第三步:獲取所需的數據 (這里我們來個真實點的情景,去聯網請求數據)
1
2
3
4
|
/**
* 聯網請求所需的url
*/
public String url="http://api.meituan.com/mmdb/movie/v2/list/rt/order/coming.json?ci=1&limit=12&token=&__vhost=api.maoyan.com&utm_campaign=AmovieBmovieCD-1&movieBundleVersion=6801&utm_source=xiaomi&utm_medium=android&utm_term=6.8.0&utm_content=868030022327462&net=255&dModel=MI%205&uuid=0894DE03C76F6045D55977B6D4E32B7F3C6AAB02F9CEA042987B380EC5687C43&lat=40.100673&lng=116.378619&__skck=6a375bce8c66a0dc293860dfa83833ef&__skts=1463704714271&__skua=7e01cf8dd30a179800a7a93979b430b2&__skno=1a0b4a9b-44ec-42fc-b110-ead68bcc2824&__skcy=sXcDKbGi20CGXQPPZvhCU3%2FkzdE%3D";
|
1
2
|
//聯網獲取數據
getDataFromNet();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/**
* 使用okhttpUtils進行聯網請求數據
*/
private void getDataFromNet() {
OkHttpUtils.
get()
.url(url)
.build()
.execute(new StringCallback() {
@Override
public void onError(okhttp3.Call call, Exception e, int id) {
Log.e("TAG", "聯網失敗" + e.getMessage());
}
@Override
public void onResponse(String response, int id) {
Log.e("TAG", "聯網成功==" + response);
//聯網成功后使用fastjson解析
processData(response);
}
});
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* 使用fastjson進行解析
*
* @param json
*/
private void processData(String json) {
//這里使用GsonFormat生成對應的bean類
JSONObject jsonObject = parseObject(json);
String data = jsonObject.getString("data");
JSONObject dataObj = JSON.parseObject(data);
String coming = dataObj.getString("coming");
List<WaitMVBean.DataBean.ComingBean> comingslist = parseArray(coming, WaitMVBean.DataBean.ComingBean.class);
//測試是否解析數據成功
// String strTest = comingslist.get(0).getCat();
// Log.e("TAG", strTest + "222");
//解析數據成功,設置適配器-->
}
}
|
第四步:解析數據成功后,創建並設置適配器,並傳遞相關數據
1
2
3
|
//解析數據成功,設置適配器
MyRecyclerAdapter adapter = new MyRecyclerAdapter( mContext,comingslist);
recyclerView.setAdapter(adapter);
|
適配器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
public class MyRecyclerAdapter extends RecyclerView.Adapter {
private final List<WaitMVBean.DataBean.ComingBean> comingslist;
private final Context mContext;
private final LayoutInflater mLayoutInflater;
public MyRecyclerAdapter(Context mContext, List<WaitMVBean.DataBean.ComingBean> comingslist) {
this.mContext = mContext;
this.comingslist = comingslist;
mLayoutInflater = LayoutInflater.from(mContext);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MyViewHolder(mLayoutInflater.inflate(R.layout.date_item, null));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
MyViewHolder myholder = (MyViewHolder) holder;
myholder.setData(position);
}
@Override
public int getItemCount() {
return comingslist.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView mv_name;
private TextView mv_dec;
private TextView mv_date;
private ImageView imageView;
public MyViewHolder(View itemView) {
super(itemView);
mv_name = (TextView) itemView.findViewById(R.id.mv_name);
mv_dec = (TextView) itemView.findViewById(R.id.mv_dec);
mv_date = (TextView) itemView.findViewById(R.id.mv_date);
imageView = (ImageView) itemView.findViewById(R.id.image);
}
public void setData(int position) {
WaitMVBean.DataBean.ComingBean coming = comingslist.get(position);
String name = coming.getNm();
mv_name.setText(name);
String date = coming.getShowInfo();
mv_date.setText(date);
String dec = coming.getScm();
mv_dec.setText(dec);
//注:當你發下圖片無法打開是,做個字符串替換即可
String imagUrl = coming.getImg();
String newImagUrl = imagUrl.replaceAll("w.h", "50.80");
//使用Glide加載圖片
Glide.with(mContext)
.load(newImagUrl)
.into(imageView);
}
}
}
|
item的布局:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffff"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="70dp"
android:layout_height="110dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="8dp"
android:layout_marginTop="5dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/mv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="神奇動物在哪裏"
android:textColor="#000000"
android:textSize="15sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="觀眾"
android:textColor="#55000000"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_people"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="9.0 "
android:textColor="#FFCE42"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" | 專業"
android:textColor="#55000000"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_professional"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="6.7"
android:textColor="#FFCE42"
android:textSize="18sp" />
</LinearLayout>
<TextView
android:id="@+id/mv_dec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="神奇動物城,法師顯超能"
android:textColor="#99000000"
android:textSize="11sp" />
<TextView
android:id="@+id/mv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="今天165家影院放映2088場"
android:textColor="#99000000"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
|
第五步:一定不能忘!!!
recycleView不僅要設置適配器還要設置布局管理者,否則圖片不顯示
1
2
|
GridLayoutManager manager = new GridLayoutManager(this, 1);
recyclerView.setLayoutManager(manager);
|
此時RecyclerView簡單的完成效果如下:
下面開始做 可推動的 懸浮導航欄:
第一步:首先我們來寫一個類,它起標記的作用,來放每一個item的對應的懸浮欄的字符串
1
2
3
4
5
6
7
8
9
10
11
|
public class NameBean {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
|
第二步:自定義一個SectionDecoration 類 繼承 RecyclerView的ItemDecoration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
public class SectionDecoration extends RecyclerView.ItemDecoration {
private static final String TAG = "SectionDecoration";
private List<NameBean> dataList;
private DecorationCallback callback;
private TextPaint textPaint;
private Paint paint;
private int topGap;
private int alignBottom;
private Paint.FontMetrics fontMetrics;
public SectionDecoration(List<NameBean> dataList, Context context, DecorationCallback decorationCallback) {
Resources res = context.getResources();
this.dataList = dataList;
this.callback = decorationCallback;
//設置懸浮欄的畫筆---paint
paint = new Paint();
paint.setColor(res.getColor(R.color.colorGray));
//設置懸浮欄中文本的畫筆
textPaint = new TextPaint();
textPaint.setAntiAlias(true);
textPaint.setTextSize(DensityUtil.dip2px(context, 14));
textPaint.setColor(Color.DKGRAY);
textPaint.setTextAlign(Paint.Align.LEFT);
fontMetrics = new Paint.FontMetrics();
//決定懸浮欄的高度等
topGap = res.getDimensionPixelSize(R.dimen.sectioned_top);
//決定文本的顯示位置等
alignBottom = res.getDimensionPixelSize(R.dimen.sectioned_alignBottom);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int pos = parent.getChildAdapterPosition(view);
Log.i(TAG, "getItemOffsets:" + pos);
String groupId = callback.getGroupId(pos);
if (groupId.equals("-1")) return;
//只有是同一組的第一個才顯示懸浮欄
if (pos == 0 || isFirstInGroup(pos)) {
outRect.top = topGap;
if (dataList.get(pos).getName() == "") {
outRect.top = 0;
}
} else {
outRect.top = 0;
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
String groupId = callback.getGroupId(position);
if (groupId.equals("-1")) return;
String textLine = callback.getGroupFirstLine(position).toUpperCase();
if (textLine == "") {
float top = view.getTop();
float bottom = view.getTop();
c.drawRect(left, top, right, bottom, paint);
return;
} else {
if (position == 0 || isFirstInGroup(position)) {
float top = view.getTop() - topGap;
float bottom = view.getTop();
//繪制懸浮欄
c.drawRect(left, top - topGap, right, bottom, paint);
//繪制文本
c.drawText(textLine, left, bottom, textPaint);
}
}
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int itemCount = state.getItemCount();
int childCount = parent.getChildCount();
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
float lineHeight = textPaint.getTextSize() + fontMetrics.descent;
String preGroupId = "";
String groupId = "-1";
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
preGroupId = groupId;
groupId = callback.getGroupId(position);
if (groupId.equals("-1") || groupId.equals(preGroupId)) continue;
String textLine = callback.getGroupFirstLine(position).toUpperCase();
if (TextUtils.isEmpty(textLine)) continue;
int viewBottom = view.getBottom();
float textY = Math.max(topGap, view.getTop());
//下一個和當前不一樣移動當前
if (position + 1 < itemCount) {
String nextGroupId = callback.getGroupId(position + 1);
//組內最后一個view進入了header
if (nextGroupId != groupId && viewBottom < textY) {
textY = viewBottom;
}
}
//textY - topGap決定了懸浮欄繪制的高度和位置
c.drawRect(left, textY - topGap, right, textY, paint);
//left+2*alignBottom 決定了文本往左偏移的多少(加-->向左移)
//textY-alignBottom 決定了文本往右偏移的多少 (減-->向上移)
c.drawText(textLine, left + 2 * alignBottom, textY - alignBottom, textPaint);
}
}
/**
* 判斷是不是組中的第一個位置
*
* @param pos
* @return
*/
private boolean isFirstInGroup(int pos) {
if (pos == 0) {
return true;
} else {
// 因為是根據 字符串內容的相同與否 來判斷是不是同意組的,所以此處的標記id 要是String類型
// 如果你只是做聯系人列表,懸浮框里顯示的只是一個字母,則標記id直接用 int 類型就行了
String prevGroupId = callback.getGroupId(pos - 1);
String groupId = callback.getGroupId(pos);
//判斷前一個字符串 與 當前字符串 是否相同
if (prevGroupId.equals(groupId)) {
return false;
} else {
return true;
}
}
}
//定義一個借口方便外界的調用
interface DecorationCallback {
String getGroupId(int position);
String getGroupFirstLine(int position);
}
}
|
第三步:在向list集合中先把每一個item的 起“標記”作用的字符串都加進去
1
|
setPullAction(comingslist);
|
1
2
3
4
5
6
7
8
9
10
|
private void setPullAction(List<WaitMVBean.DataBean.ComingBean> comingslist) {
dataList = new ArrayList<>();
for (int i = 0; i < comingslist.size(); i++) {
NameBean nameBean = new NameBean();
String name0 = comingslist.get(i).getComingTitle();
nameBean.setName(name0);
dataList.add(nameBean);
}
}
|
第四步:在setAdapter() 前,為RecyclerView添加ItemDecoration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
recyclerView.addItemDecoration(new SectionDecoration(dataList,mContext, new SectionDecoration.DecorationCallback() {
//返回標記id (即每一項對應的標志性的字符串)
@Override
public String getGroupId(int position) {
if(dataList.get(position).getName()!=null) {
return dataList.get(position).getName();
}
return "-1";
}
//獲取同組中的第一個內容
@Override
public String getGroupFirstLine(int position) {
if(dataList.get(position).getName()!=null) {
return dataList.get(position).getName();
}
return "";
}
}));
|
這樣就完成了~
再看一眼最終效果感受一下: