Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 如何為RecyclerView添加分隔線

如何為RecyclerView添加分隔線

編輯:關於Android編程

我在簡書上發布了我個人的第一篇技術文檔:RecyclerView系列之: RecyclerView系列之(1)為RecyclerView添加Header和Footer,也很有幸,能夠得到那麼多人的支持,這讓我迫不及待的趕緊寫第二篇文章。今天我將談談:為RecyclerView添加分隔線。

一. 理解ListView和RecyclerView中的ChildView
在講為Item加入分割線本質的前,先來介紹,認識一下ChildView,也就是平時我們用到的ListView,RecyclerView中的getChildAt(int position)這個返回的ChildView是哪一部分?到底是哪一部分呢?一開始的時候,我理解錯了,但是經過下面兩張圖這麼一比較,你就明白了:


Item布局layout_margin == 0

Item布局Layout_margin == 16dp

下面看代碼的區別:
第一張圖的代碼, 也就是每一個list_item的布局文件(下同)如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    
android:layout_width="match_parent"    
android:layout_height="50dp"> 
  <TextView  
  android:id="@+id/list_item"  
  android:layout_width="match_parent" 
  android:layout_height="match_parent"  
  android:gravity="center"  
  android:textSize="20sp"  
  android:textColor="#262526"  
  android:background="#08da1d"/>
</LinearLayout>

第二張圖的代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    
android:layout_width="match_parent"    
android:layout_height="50dp"
android:layout_margin="16dp"> 
  <TextView  
  android:id="@+id/list_item"  
  android:layout_width="match_parent" 
  android:layout_height="match_parent"  
  android:gravity="center"  
  android:textSize="20sp"  
  android:textColor="#262526"  
  android:background="#08da1d"/>
</LinearLayout>

仔細看一下,它們的不同之處, 就是第二個圖的代碼中多了:

android:layout_margin = "16dp"
就多這一句而已。

所以到這裡我們應該知道了ChildView是哪一部分了,就是圖二中綠色這一部分,邊距這一部分並不屬於ChildView, 而是屬於ChildView的布局。

這樣我們了解ChildView之後,下面再來理解加入分隔線的原理就簡單多了。

二. 理解加入分隔線的原理

在ListView中,Google為我們提供了SetDivider(Drawable divider)這樣的方法來設置分隔線,那麼在RecyclerView中,Google又為我們提供了什麼樣的方法去添加分隔線呢?通過查看官方文檔,它,提供了:addItemDecoration(RecyclerView.ItemDecoration decor)這個方法了設置分隔線,那問題又來了,RecyclerView.ItemDecoration是什麼東西呢?繼續查:然後發現如下:它原來是一個類,裡面封裝了三個方法:
(1)void getItemOffsets ()
(2)void onDraw ()
(3)void onDrawOver ()

通過上面的三個方法,可以看出,這是要自己直接畫上去,准確的說這幾個方法是:添加Divider,主要是找到添加Divider的位置, 而Divider是在drawable文件中寫好了的。 利用onDraw和onDrawOver都差不多,我們在創建自己的Decoration類繼承RecyclerView.ItemDecoration的時候,我們只要重寫getItemOffsets(),還有onDraw()和onDrawOver兩者其中之一就可以了.

那getItemOffsets()方法有什麼用呢?從字面意思就是Item要偏移, 由於我們在Item和Item之間加入了分隔線,線其實本質就是一個長方形,也是用戶自定義的,既然線也有長寬高,就畫橫線來說,上面的Item加入了分隔線,那下面的Item就要往下平移,平移的量就是分隔線的高度。不理解每關系,後面看代碼就容易理解了。

現在我們知道了如何添加了,就是通過畫,那到底是畫在哪裡呢?畫的位置又怎麼確定呢?下面看圖:


分隔線的位置圖

我現在拿畫橫線來說,從上面這個圖中,我們很容易就可以看到,我們畫分隔線的位置,是在每一個Item的布局之間,注意:是布局之間。

好了,我們確定了畫在哪裡,那我們怎麼確定畫線的具體的坐標位置呢?也就是我們要確定:分隔線的left, top, right, Bottom. 在Adapter中,我們很容易通過parent(這個parent它其實就是我們能看到的部分)獲取每一個childView:
(1)left:parent.getPaddingLeft()
(2)right: parent. getWidth()-parent.getPaddingRight();
(3)top : 就是紅線的上面:我們通過ChildView.getBottom()來得到這個Item的底部的高度,也就是藍線位置,藍線和紅線之間間距:就是這個Item布局文件的:layout_marginBottom, 然後top的位置就是兩者之和。
(4)bttom: 就是top加上分隔線的高度:top+線高

通過上面的解析,你也許知道了加入分隔線的原理,不理解也沒有關系,說也不是說得很清楚,下面直接上代碼,通過代碼來理解。

三. Talk is cheap, show you the code.

(1)首先,先來看主布局文件:activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:fitsSystemWindows="true"
 tools:context="com.study.wnw.recyclerviewdivider.MainActivity">

 <android.support.v7.widget.RecyclerView
  android:id="@+id/recyclerview"
  android:layout_width="match_parent"
  android:layout_height="match_parent"> 
 </android.support.v7.widget.RecyclerView>
</android.support.design.widget.CoordinatorLayout>

我在這裡面僅僅加入了一個RecyclerView

(2)RecyclerView裡面每個Item的布局文件:item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent" 
    android:layout_height="50dp"
    android:layout_margin="16sp">
 <TextView
  android:id="@+id/list_item"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:textSize="20sp"
  android:textColor="#f7f4f7"
  android:background="#08da1d"/>
</LinearLayout>

這也沒有什麼可講的,就是在裡面添加一個TextView用來顯示文本

(3)我們RecyclerView的適配器MyAdapater.java:

package com.study.wnw.recyclerviewdivider;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/** * Created by wnw on 16-5-22. */
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

 //定義一個集合,接收從Activity中傳遞過來的數據和上下文
 private List<String> mList;
 private Context mContext;

 MyAdapter(Context context, List<String> list){
  this.mContext = context;
  this.mList = list;
 }

 //得到child的數量
 @Override
 public int getItemCount() {
  return mList.size();
 }

 //創建ChildView
 @Override
 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  View layout = LayoutInflater.from(mContext).inflate(R.layout.item_view, parent, false);
  return new MyHolder(layout);
 }

 //將數據綁定到每一個childView中
 @Override
 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
  if (holder instanceof MyHolder){
   final String itemText = mList.get(position);
   ((MyHolder)holder).tv.setText(itemText);
  }
 }

 // 通過holder的方式來初始化每一個ChildView的內容
 class MyHolder extends RecyclerView.ViewHolder{
  TextView tv;
  public MyHolder(View itemView) {
   super(itemView);
   tv = (TextView)itemView.findViewById(R.id.list_item);
  }
 }
}

好了,這裡也沒有什麼好講的,也不是我們這篇文章的重點,下面重點來了。

(4)我們自定義的MyDecoration.java:(繼承RecyclerView.ItemDecoration)

package com.study.wnw.recyclerviewdivider;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

/** * Created by wnw on 16-5-22. */

public class MyDecoration extends RecyclerView.ItemDecoration{

 private Context mContext;
 private Drawable mDivider;
 private int mOrientation;
 public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
 public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

 //我們通過獲取系統屬性中的listDivider來添加,在系統中的AppTheme中設置
 public static final int[] ATRRS = new int[]{
   android.R.attr.listDivider
 };

 public MyDecoration(Context context, int orientation) {
  this.mContext = context;
  final TypedArray ta = context.obtainStyledAttributes(ATRRS);
  this.mDivider = ta.getDrawable(0);
  ta.recycle();
  setOrientation(orientation);
 }

 //設置屏幕的方向
 public void setOrientation(int orientation){
  if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
   throw new IllegalArgumentException("invalid orientation");  }  mOrientation = orientation;
 } 

 @Override
 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
  if (mOrientation == HORIZONTAL_LIST){
   drawVerticalLine(c, parent, state);
  }else {
   drawHorizontalLine(c, parent, state);
  }
 }

 //畫橫線, 這裡的parent其實是顯示在屏幕顯示的這部分
 public void drawHorizontalLine(Canvas c, RecyclerView parent, RecyclerView.State state){
  int left = parent.getPaddingLeft();
  int right = parent.getWidth() - parent.getPaddingRight();
  final int childCount = parent.getChildCount();
  for (int i = 0; i < childCount; i++){
   final View child = parent.getChildAt(i);

   //獲得child的布局信息
   final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
   final int top = child.getBottom() + params.bottomMargin;
   final int bottom = top + mDivider.getIntrinsicHeight();
   mDivider.setBounds(left, top, right, bottom);
   mDivider.draw(c);
   //Log.d("wnw", left + " " + top + " "+right+" "+bottom+" "+i);
  }
 }

 //畫豎線
 public void drawVerticalLine(Canvas c, RecyclerView parent, RecyclerView.State state){
  int top = parent.getPaddingTop();
  int bottom = parent.getHeight() - parent.getPaddingBottom();
  final int childCount = parent.getChildCount();
  for (int i = 0; i < childCount; i++){
   final View child = parent.getChildAt(i); 

   //獲得child的布局信息
   final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
   final int left = child.getRight() + params.rightMargin;
   final int right = left + mDivider.getIntrinsicWidth();
   mDivider.setBounds(left, top, right, bottom);
   mDivider.draw(c);
  }
 }

 //由於Divider也有長寬高,每一個Item需要向下或者向右偏移
 @Override
 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
  if(mOrientation == HORIZONTAL_LIST){
   //畫橫線,就是往下偏移一個分割線的高度
   outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
  }else {
   //畫豎線,就是往右偏移一個分割線的寬度
   outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
  }
 }
}

從上面的代碼中,我們還通過系統屬性來適應屏幕的橫屏和豎屏,然後確定畫橫的,還是豎的Divider,其實在裡面我們做了三件事,第一件是:獲取到系統中的listDivider, 我們就是通過它在主題中去設置的,下面第(6)小點看一下代碼就知道了。第二件事:就是找到我們需要添加Divider的位置,從onDraw方法中去找到,並將Divider添加進去。第三個是:得到Item的偏移量。

(5)看看我們的MainActivity.java

package com.study.wnw.recyclerviewdivider;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
 //定義RecyclerView
 private RecyclerView mRecyclerView = null;

 //定義一個List集合,用於存放RecyclerView中的每一個數據
 private List<String> mData = null;

 //定義一個Adapter
 private MyAdapter mAdapter; 

 //定義一個LinearLayoutManager
 private LinearLayoutManager mLayoutManager;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  //RecyclerView三步曲+LayoutManager
  initView();
  initData();
  mAdapter = new MyAdapter(this,mData);
  mRecyclerView.setLayoutManager(mLayoutManager);
  mRecyclerView.setAdapter(mAdapter); 

  //這句就是添加我們自定義的分隔線
  mRecyclerView.addItemDecoration(new MyDecoration(this, MyDecoration.VERTICAL_LIST));
 }

 //初始化View
 private void initView(){
  mLayoutManager = new LinearLayoutManager(this);
  mRecyclerView = (RecyclerView)findViewById(R.id.recyclerview);
 }

 //初始化加載到RecyclerView中的數據, 我這裡只是給每一個Item添加了String類型的數據
 private void initData(){
  mData = new ArrayList<String>();
  for (int i = 0; i < 20; i++){
   mData.add("Item" + i);
  }
 }
}

(6)分隔線Divider的drawable文件:divider..xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle">
 <solid android:color="#7b7a7a"/>
 <size android:height="1dp"/>
</shape>

我們在這裡面,畫了一個:rectangle, 給它填充顏色,還有高度,這樣就搞定了,高度小,顯示出來也是一條線:其實線的本質就是長方形。這裡可以根據個人需要,畫不同類型的divider

(7)在styles.xml的AppTheme中,設置listDivider為我們的divider.xml文件:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
 <item name="android:listDivider">@drawable/divider</item>
</style>

這樣,我們將系統的listDivider設置成我們自定義的divider. 還記得我們在MyDecoration中獲取系統的listDivider這個屬性嗎,這樣通過這個屬性,我們就可以將我們的divider.xml文件和MyDecoration.java進行關聯了。

到這裡所有的工作就完成了,下面展示一下運行結果:


豎屏效果圖

橫屏效果圖

經過幾個小時的寫作,終於搞定了,雖然僅僅是一個添加分隔線的功能,但是還是想盡可能的通過自己的語言去理解,去認知它的原理,這樣做起來就簡單多了。一開始的時候,我夜不知道怎麼去用,也參考了別人寫的文章,特別是鴻洋大神的:Android RecyclerView 使用完全解析 體驗藝術般的控件, 寫得特別的棒,從中也學到了一些知識。

好了,這篇文章暫時寫到這裡了,簡單的介紹了一些RecyclerView分隔線的原理和添加方法,希望大家能夠多多交流,過幾天我會繼續寫下一篇文章,RecyclerView系列之(3):為RecyclerView添加下拉刷新和上拉加載的功能。最後還是要感謝大家,感謝這個平台,能夠讓我們一起交流,一切學習。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved