開始逐漸領略到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 ""; } })); |
這樣就完成了~
再看一眼最終效果感受一下: