Android自定義九宮格控件 [復制鏈接]

2018-12-20 10:22
ceshishangchuan 閱讀:416 評論:0 贊:0
Tag:  

最近公司在做個類似朋友圈的功能,需要一個九宮格控件,因為算是個常用控件,所以自己擼了一個。

職責分解



NinePhotoView


上圖是一個九宮格控件承載圖片的顯示情況,九宮格控件因為其需要承載其內部的View,所以應該是一個ViewGroup。每個子View基本上是相同的,但顯示的內容不同,正好最近在看RecyclerView,因此也就想到了可以使用Adapter來個性化每個子View,正好通過Adapter模式來適配了子View與ViewGroup。最后為了防止在創建、顯示子View時的冗余操作,應該給子View增加一些屬性,選用ViewHolder正好可以完成這個要求,這樣也更方便以后的擴展。

ViewGroup、Adapter、ViewHolder三者的具體職責: 

功能
ViewGroupAdapterViewHolder
Measure子View創建子View布局子View的獲取
Layout子View子View的內容displaydisplay子View標志
add子View子view數量
回收緩存數據變化通知


ChildView的測量

ChildView測量所需要的數據主要來自于ParentView的測量數據,這里就是我們之前說的ViewGroup的onMeasure方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 在這之前已經對childView的數量小于等于0或大于9的情況進行了處理
* 這里講顯示情況分成三種進行測量:
* 1、ChildView數量為1
* 2、ChildView數量為2或4
* 3、ChildView數量的剩余類型
*/
if (adapter.getItemCount() > 1) {
childSize = (width - border * 2) / 3;
height = (int) (childSize * (int) Math.ceil(adapter.getItemCount() / 3.0) + border * (int) Math.ceil(adapter.getItemCount() / 3.0
if (adapter.getItemCount() == 4 || adapter.getItemCount() == 2) {
int currentWidth = childSize*2 + border;
setMeasuredDimension(currentWidth + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom());
}else {
int currentWidth = childSize*3 + border*2;
setMeasuredDimension(currentWidth + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom());
}
} else {
childSize = width/3;
height = width/3;
setMeasuredDimension(width + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom());
}

ChildView的添加

在測量ChildView之后,將使用addView方法將其添加到ViewGroup中。

1
2
3
4
5
6
7
/**
* 在增加ChildView之前,需要先行清除已經添加的所有ChildView
*/
removeAllViews();
for (int i = 0; i < adapter.getItemCount(); i++) {
addView(generateViewHolder(i).getItemView(),generateDefaultLayoutParams());
}

ChildView的回收

generateViewHolder是用于獲取ChildView的方法,其中有一個簡單的緩存列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 當需要添加的ChildView的position小于緩存列表大小時,直接從緩存列表中獲取ChildView
* 否則,則調用adapter.createView方法,創建一個新的ViewHolder,同時將其添加到緩存列表中
*/
private IKNinePhotoViewHolder generateViewHolder(int position){
if (position < mRecyclerList.size()) {
return mRecyclerList.get(position);
} else {
if (adapter != null){
IKNinePhotoViewHolder holder = adapter.createView(IKNinePhotoView.this);
if (holder == null){
return null;
}
mRecyclerList.add(holder);
return holder;
} else
return null;
}
}

ChildView的布局

在完成ChildView的測量和添加之后,需要對ChildView的位置進行確定。而這些數值來自ParentView的onLayout方法。

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
/**
* 先對ChildView數量為4的特殊情況,進行處理,將其的列數確定為2。
*/
int count = adapter.getItemCount();
int colNum = 3;
if (count == 4){
colNum = 2;
}
/**
* 便利每個ChildView,對其進行布局
*/
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
if (childView == null){
return;
}
if (adapter != null && mRecyclerList.get(i) != null &&!mRecyclerList.get(i).getFlag()) {
adapter.displayView(generateViewHolder(i), i);
// 設置這個標志,表示此ViewHolder中包含的ChildView已經完成了布局,防止多余的操作
mRecyclerList.get(i).setFlag(true);
}
// 之前設置的列數
int rows = i / colNum;
int cols = i % colNum;
int childLeft = getPaddingLeft() + (childSize + border) * (cols);
int childTop = getPaddingTop() + (childSize + border) * (rows);
int childRight = childLeft + childSize;
int childBottom = childTop + childSize;
childView.layout(childLeft, childTop, childRight, childBottom);
}

ChildView數據的更新

到這里已經基本上完成了九宮格控件的編寫,最后需要再添加一個數據更新的功能,以完備其功能。這里使用觀察者模式的來實現這個功能,Adapter繼承Observable類、ViewGroup實現Observer接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 在Adapter中
public void notifyChanged(){
setChanged();
notifyObservers();
}
// 在ViewGroup中
@Override
public void update(Observable o, Object arg) {
if (o instanceof IKNinePhotoViewAdapter){
this.adapter = (IKNinePhotoViewAdapter) o;
adapter.addObserver(this);
for(IKNinePhotoViewHolder holder: mRecyclerList){
holder.setFlag(false);
}
requestLayout();
invalidate();
}
}

總結

本文詳細說明了九宮格控件的實現方法,并對其功能進行了分解。分解成多個組件之后,不僅符合程序的單一性原則,更加易于程序的功能擴展,比如添加監聽事件,使用不同的圖片庫去顯示圖片,同時九宮格的ChildView也不僅限于ImageView,而可以擴展到全部的View。

源碼:http://www.iddrgr.tw/thread-604662-1-1.html


我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

两码中特期期