學習流程來自《第一行代碼》(第二版)
在之前的RecyclerView(1)開頭給出了對RecyclerView設置的幾個模塊。在書中只介紹了部分,這邊來擴展一下。
GridLayoutManager
RecyclerView能實現的佈局有很多,佈局都由LayoutManager來控制,可拓展性十分高。
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
把RecyclerView的LayoutManager設置成GridLayout並傳入上下文,和列數這兩個參數即可。
我們仍就靠隨機生成TextView的值,但是每個item的排列是網格類型的。
增加分隔線
可以明顯的看出之前我們的RecyclerView是沒有分隔線的。
addItemDecoration(ItemDecoration decoration) :方法就是用來設置分隔線的。
而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分隔明顯(ChildView,也就是平時用到的ListView,RecyclerView中的getChildAt(int position)這個返回的,這一部分指的是圖二中紅色的部分,白色的部分屬於ChildView的佈局。)
一個View對應一個佈局
可以明顯的看出我們要畫的就是黑色的這一條線。這條線是加在兩個佈局之間的。
所以我們可以知道我們畫分隔線的位置,是在每一個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+線高
可以看到,在這張圖片中兩個item之間有了一條系統默認(感覺是佈局空出來的背景色)的分割線。
用自定義的樣式來填充分隔線。在drawable文件夾下添加一個divider.xml文件。
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#00ff00" />
<size android:height="1dp" />
</shape>
定義一條綠線。
在values文件下的styles.xml文件中style標籤之間添加
<item name="android:listDivider">@drawable/divider</item>
把自定義的樣式引用進來。
MainActivity.java :
List<String> strings = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initStr();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
ItemAdapter adapter = new ItemAdapter(this,strings);
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new MyDecoration(this, MyDecoration.VERTICAL_LIST));
}
public void initStr() {
strings = new ArrayList<String>();
for (int i = 0; i < 50; i++)
strings.add("Item" + i);
}
大部分代碼與(1)類似 :
1. 初始化填入的數據
2. 定義一個LayoutManager,把垂直排列傳進去
3. 定義item的適配器
4. 定義分隔線(告知是垂直的佈局)
ItemAdapter.java
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {
private List<String> mList;
private Context mContext;
public ItemAdapter(Context context, List<String> list) {
this.mContext = context;
this.mList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
String str = mList.get(position);
holder.textView.setText(str);
}
@Override
public int getItemCount() {
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public ViewHolder(View view) {
super(view);
textView = (TextView) view.findViewById(R.id.list_item);
}
}
}
子項適配器的代碼。
可以看到item之間有一條淡淡的綠線。
效果不是特別好,可以選擇重新繪製一下divider.xml文件
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear" />
<size android:height="4dp"/>
</shape>
這個效果就是比較好的了。
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.addItemDecoration(new MyDecoration(this, MyDecoration.HORIZONTAL_LIST));
更改一下這兩行代碼
並且可以把item_layout.xml的layout_width屬性改成固定值,如 :200dp
item的排列爲水平時的豎直分隔線效果圖。
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;
public static final int[] ATRRS = new int[]{ android.R.attr.listDivider}; //通過獲取系統屬性中的listDivider來添加,在AndroidManifest.xml中的AppTheme中設置
public MyDecoration(Context context, int orientation) {
this.mContext = context;
final TypedArray ta = context.obtainStyledAttributes(ATRRS); // TypedArray 用於存放 android 自定義控件 把屬性集 和我們自己定義的屬性集合建立映射關係
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) { // 水平 畫豎線
drawVerricalLine(c, parent, state);
} else { // 子項佈局垂直排列時花在兩個佈局之間(上佈局的底部)
drawHorizontalLine(c, parent, state);
}
}
public void drawHorizontalLine(Canvas c, RecyclerView parent, RecyclerView.State state) { // 畫橫線 Android.graphics
int left = parent.getPaddingLeft(); // 得到佈局的左邊距
int right = parent.getWidth() - parent.getPaddingRight(); // 佈局右邊的終點 parent的寬度-(減去)佈局的右邊距(此程序得到的是一條佔滿屏幕寬度的線)
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); // 獲取child的佈局信息 params 參數
int top = child.getBottom() + params.bottomMargin; // item View(Textview)的底部+佈局中的(頁下空白)
int bottom = top +mDivider.getIntrinsicHeight(); // 高度 + Drawable的固有高度 dp爲單位
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVerricalLine(Canvas c, RecyclerView parent, RecyclerView.State state) { //畫豎線
int top = parent.getPaddingTop(); // 獲取parent的上邊距
int bottom = parent.getHeight() - parent.getPaddingBottom(); //parent的高度-parent的下邊距
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams(); // 獲得child的佈局信息
int left = child.getRight() + params.rightMargin ; // 從第一個item(item0)的尾部開始加入分隔線 View的最右邊 + 佈局的頁右空白
int right = left + mDivider.getIntrinsicHeight() ; // 兩個佈局之間的空隙 (此處應該與getItemOffsets()方法中的偏移數值相呼應)
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == HORIZONTAL_LIST) { // 畫豎線 往右偏移
Log.d("admin",""+mDivider.getIntrinsicWidth()+" "+mDivider.getIntrinsicHeight()); // 爲何輸出Width爲負數
outRect.set(0, 0,mDivider.getIntrinsicHeight(), 0); // mDivider.getIntrinsicHeight()
} else { ////畫橫線,往下偏移一個分割線的高度
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}
}
}
這就是添加分隔線的代碼,具體的代碼解釋含在註釋裏了,大致上是
1. 定義一個drawable,
2. 判斷屏幕方向,調用相應的畫線方法
3. 設定item之間的偏移
item的增加刪除
有時候item不是單一不變的,有時候會有添加和刪除的需求。
先在佈局中添加兩個button
<?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="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
>
<Button
android:id="@+id/add_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add"
android:layout_margin="15dp"/>
<Button
android:id="@+id/delete_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete"
android:layout_margin="15dp"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
在適配器代碼中添加增加和移除功能 :
public void addData(int position) { // 添加item
String str = "新增item";
mList.add(position, str);
notifyItemInserted(position);
notifyDataSetChanged();
notifyItemRangeChanged(position, mList.size());
}
public void removeData(int position) { // 刪除item
mList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mList.size());
}
在MainActivity.java中 調用 適配器實例的增加和移除的方法 :
Button addData = (Button) findViewById(R.id.add_btn);
Button deleteData = (Button) findViewById(R.id.delete_btn);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
LinearLayoutManager manager = new LinearLayoutManager(this);
final ItemAdapter adapter = new ItemAdapter(this,strings);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator()); // 設置默認動畫
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
adapter.addData(0);
}
});
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
adapter.removeData(0);
}
});
這邊沒有顯示我們增加的分隔線。
運行點擊Add按鈕,可以看到有新增item加入 :
點擊移除 :
移除的時候還會出現我們在配置RecyclerView時調用的setItemAnimator(new DefaultItemAnimator()); 使用的默認動畫。
參考
http://www.jianshu.com/p/4eff036360da
http://blog.csdn.net/lmj623565791/article/details/45059587
http://blog.csdn.net/qq_21071977/article/details/78001509
遺留問題
1.
在LinearLayoutManager爲垂直的時候,畫豎分隔線
public void drawVerricalLine(Canvas c, RecyclerView parent, RecyclerView.State state) { //畫豎線
int top = parent.getPaddingTop(); // 獲取parent的上邊距
int bottom = parent.getHeight() - parent.getPaddingBottom(); //parent的高度-parent的下邊距
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams(); // 獲得child的佈局信息
int left = child.getRight() + params.rightMargin ; // 從第一個item(item0)的尾部開始加入分隔線 View的最右邊 + 佈局的頁右空白
int right = left + mDivider.getIntrinsicHeight() ; // 兩個佈局之間的空隙 (此處爲何用mDivider.getIntrinsicHeight())
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
獲取了一下mDivider.getIntrinsicWidth()方法的數值爲負。
爲何此數值爲負數
2.
添加item時,position = 0,自定義的List可以列表更新,但是界面上卻沒有顯示出這個新增item(只是整個RecyclerView閃動了一下),刪除的時候也能發現需要多刪一下。就是不能在position=0 的位置顯示新增item。(position = 1之後一切正常)
增加notifyDataSetChanged();可以在position=0處顯示出新增item,但是不出現新增動畫。
刪除功能一切正常。
此博文爲個人學習筆記,僅供個人學習使用,希望對大家有幫助。