FormLayoutManager首頁,裏面有github地址
目錄
前言
之前能實現的表格都必須每個格子的寬高一樣,現在所說的多類型就是,像平常的adapter一樣,通過getItemViewType獲取不同的類型,然後使用不同的佈局。下面的講解,你可知道,我只允許你根據行或根據列來獲取不同類型(getRowItemViewType和getColumnItemViewType),這裏不允許同時根據行和列來獲取類型。接下來大家可以結合demo的HForm2Activity來理解。
代碼
HForm2Activity跟其他demo裏的界面代碼沒什麼差別,主要還是要看Adapter和FormLayoutManager的代碼。
MonsterHAdapterByType
@Override
protected int getColumnItemViewType(int column) {
if (column == 1) {
return TYPE_ATTRIBUTE;
}
if (column == 6 || column == 7) {
return TYPE_MONSTER_TYPE;
}
return super.getColumnItemViewType(column);
}
@Override
protected View createView(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.adapter_monster_form_item, viewGroup, false);
if (viewType == TYPE_ATTRIBUTE) {
view = LayoutInflater.from(mContext).inflate(R.layout.adapter_monster_form_item_attribute, viewGroup, false);
}
if (viewType == TYPE_MONSTER_TYPE) {
view = LayoutInflater.from(mContext).inflate(R.layout.adapter_monster_form_item_type, viewGroup, false);
}
return view;
}
可以看到這個adapter重寫了一個getColumnItemViewType,根據不同列來返回類型,一共有兩種類型和默認類型。createView也是對應有三個佈局。
看看例子的界面,可以看到第2列和最後兩列跟其他格子的寬有點不一樣。
我們看一下基類BaseFormAdapter幹了什麼。
@Override
public int getItemViewType(int position) {
int rowIndex = position / getColumnCount();
int columnIndex = position % getColumnCount();
int rowType = getRowItemViewType(rowIndex);
int columnType = getColumnItemViewType(columnIndex);
if (rowType != TYPE_DEFAULT){
return rowType;
}
if (columnType != TYPE_DEFAULT){
return columnType;
}
return TYPE_DEFAULT;
}
/**
* 根據不同行獲取itemViewType(注:與getColumnItemViewType只能重寫其中一個)
* @param row
* @return
*/
protected int getRowItemViewType(int row){
return TYPE_DEFAULT;
}
/**
* 根據不同列獲取itemViewType (注:與getRowItemViewType只能重寫其中一個)
* @param column
* @return
*/
protected int getColumnItemViewType(int column){
return TYPE_DEFAULT;
}
其實只是重寫一般adapter的getItemViewType,而getRowItemViewType和getColumnItemViewType默認返回一個默認類型,自己可以重寫。而getItemViewType主要做的就是通過position獲取該item對應的行row和列column。然後根據row獲取行的類型,根據column獲取列的類型,然後返回。
注意:
getRowItemViewType和getColumnItemViewType只能重寫其中一個,我的庫不允許你的表格,列的寬可以不同,同時行的高可以不同。你的表格要麼就是根據列獲取不同佈局,或根據行獲取不同佈局。爲什麼?看上面那個demo例子,這個表格根據列返回三種類型,用了三個佈局。如果現在這個表格有一行的高與其他行不一樣,你不是要寫多一個佈局,而是要寫多三個佈局,因爲與這行交叉的那個多類型的列,對應的itemview它的寬高跟其他itemview也不一樣。所以你看多複雜,你要實現這麼複雜的表格,你倒不如打開一張Excel表算了。
FormLayoutManager
接下來看看要實現多類型,FormLayoutManager要做出什麼修改。當時FormLayoutManager -- 解說(1)裏面有說過這樣一段代碼,在handleLayoutChildren裏面的。
for (int i = 0; i < getItemCount(); i++) {
// item所在的行和列的index
int row = i / mColumnCount;
int column = i % mColumnCount;
View itemView = recycler.getViewForPosition(i);
Integer itemViewType = getItemViewType(itemView);
int itemWidth;
int itemHeight;
if (mItemViewSizeMap.containsKey(itemViewType)){
itemWidth = mItemViewSizeMap.get(itemViewType).width;
itemHeight = mItemViewSizeMap.get(itemViewType).height;
}else{
measureChildWithMargins(itemView, 0, 0);
itemWidth = getDecoratedMeasuredWidth(itemView);
itemHeight = getDecoratedMeasuredHeight(itemView);
mItemViewSizeMap.put(itemViewType, new ItemViewSize(itemWidth, itemHeight));
}
Rect rect = getViewRect(row, column, itemWidth, itemHeight);
mItemRects.add(rect);
mHasAttachedItems.add(false);
}
我們的mItemRects列表就是用來保存表格所有格子的位置大小的,而解說(1)已經有說過怎麼計算每個rect的left,top,right,bottom了,這裏就不再說,我們主要看獲取item類型的地方。
LayoutManager自帶getItemViewType的方法,但要傳入的是對應的view。這也沒難度,拿到itemViewType的我們要幹什麼呢?
我們用一個mItemViewSizeMap來保存不同類型的view的寬高,類型作爲key。如果map裏面有該key,我們就可以從map裏面拿出對應的寬高。如果沒這個key,我們就用measureChildWithMargins測量這個itemView的寬高並保存。
只要拿到這個view準確的寬和高,通過getViewRect方法就可以獲取對應的Rect。而這個getViewRect方法幹了什麼,解說(1)也有說,可以回看一下。
注意
1、只能根據行獲取多類型或列獲取多類型,不能同時重寫兩個獲取類型的方法。
2、如果你的表格根據列獲取多類型,那你提供給表格的itemview的佈局必須高都一樣;如果你的表格根據行獲取多類型,那你提供給表格的itemview的表格必須寬一樣。要不你的表格會不齊,錯位的。
要符合上面這兩點,才能正確使用多類型。後期我會再修改一下這個庫,如果不符合上面的原則,會讓你的程序拋異常。