前段時(shí)間寫過一個(gè)Android自定義View的文章瓷产,是高仿的QQ健康,還沒有看過的可以去看看柳洋,所以一直都計(jì)劃著在寫個(gè)自定義ViewGroup相關(guān)的文章傻挂。我知道網(wǎng)上關(guān)于這方面的文章已經(jīng)是一搜一大堆的那種了,所以我想一定要找個(gè)與網(wǎng)上的不同的來做Demo检碗,還是要稍微有點(diǎn)新意据块,由于一直找不到所以就一直拖著了,最近也是突然間想到Windows的桌面不錯(cuò)折剃,我可以拿這個(gè)作為一個(gè)自定ViewGroup的Demo啊另假,于是就著手寫了這么一個(gè)。本文比較簡單怕犁,適合那些還不太會自定義ViewGroup的同學(xué)看看边篮。先來看看最后的效果圖
自定義一個(gè)ViewGroup,其實(shí)真的并不太難奏甫,主要就在onMeasure和onLayout這兩個(gè)方法上下功夫戈轿,無非就是去把子View的大小測量出來然后再把子View按照一定的規(guī)則放置到相應(yīng)的位置上,只要把測量和布局的邏輯和思想弄懂了阵子,自定義ViewGroup就是小Case了思杯。
下面就來說說上面的這個(gè)自定義ViewGroup怎么做吧。
先來說說思路挠进,整個(gè)ViewGroup分為兩個(gè)區(qū)域色乾,上面是內(nèi)容區(qū),下面是底部狀態(tài)欄领突,我這個(gè)自定義ViewGroup的做法就是默認(rèn)的把最后添加的那一個(gè)View來作為底部狀態(tài)欄暖璧。至于彈出來的那個(gè)就是個(gè)PopWondow,與自定義ViewGroup是沒有什么關(guān)系的攘须,是在使用的時(shí)候添加的漆撞。
來看看代碼實(shí)現(xiàn)
1.onMeasure方法
與自定義View唯一不同的就是,在ViewGroup的onMeasure方法里還需對他所包含的View的大小進(jìn)行測量于宙,里面的measureChildren是調(diào)用的系統(tǒng)的測量方法浮驳,他是將父布局的測量規(guī)格作為參數(shù)傳遞到這個(gè)方法中,然后根據(jù)父布局的測量規(guī)格去測量子View的大欣炭(感興趣可以去源碼查看至会,還是比較簡單的,這里我們完全可以自己去做這個(gè)測量谱俭,既然系統(tǒng)已經(jīng)提供了奉件,就不用去那些重復(fù)的事了)。最后就是setMeasuredDimension設(shè)置ViewGroup本身的大小昆著,這里我對ViewGroup是大小是wrap_content的情況县貌,就直接將其大小設(shè)置為0了,在這個(gè)自定義ViewGroup中wrap_content也沒多大的意義凑懂。
2.onLayout方法
當(dāng)測量完成之后就是對ViewGroup中的子View進(jìn)行放置了煤痕,View的layout方法就是用來去將View放到某個(gè)位置用的,來看看onLayout方法的實(shí)現(xiàn)接谨。
這里簡單說一下摆碉,onLayout方法中的那幾個(gè)參數(shù)的含義
- changed:默認(rèn)這個(gè)值都是為false,如果這個(gè)ViewGroup的位置改變了脓豪,導(dǎo)致了被重新布局巷帝,這個(gè)值就為true,
- l:ViewGroup left的位置
- t:ViewGroup top的位置
- r:ViewGroup right的位置
- b:ViewGroup bottom的位置
我們都知道扫夜,一般知道了一個(gè)View的left楞泼,top,right历谍,bottom就決定了這個(gè)View的位置现拒。
代碼中大部分我注釋的還是比較清楚了,這里只來看看for循環(huán)里面的實(shí)現(xiàn),里面的邏輯就是去計(jì)算將要放置的View的位置的望侈。
注意到印蔬,if(cb>contentHeight) 這句,就是如果當(dāng)前將要放置的View的bottom位置已經(jīng)超過了內(nèi)容區(qū)的高度脱衙,那么我們就將列數(shù)加1侥猬,將之放到下一列中去。
整個(gè)自定義ViewGroup就完了捐韩,就是這么簡單退唠。我們來看看怎么在布局文件中去使用。
<ren.solid.materialdesigndemo.view.WindowsLayout
android:id="@+id/windows_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#6b6b6b">
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/icon_folder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文件夾" />
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/icon_folder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文件夾" />
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/icon_folder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文件夾" />
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/icon_folder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文件夾" />
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/icon_folder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文件夾" />
</LinearLayout>
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/icon_folder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="文件夾" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#e0000000"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="10dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/icon_windows" />
</LinearLayout>
</ren.solid.materialdesigndemo.view.WindowsLayout>
在這個(gè)自定義的ViewGroup中荤胁,前面放置的都是內(nèi)容區(qū)的瞧预,最后一個(gè)是底部狀態(tài)欄。就這樣就可以實(shí)現(xiàn)最開始那個(gè)動態(tài)圖中的效果。對于點(diǎn)擊左下角的徽標(biāo)垢油,可以彈出一個(gè)層盆驹,我使用的是PopWindow,代碼如下:
LinearLayout ll_bottom = customFindViewById(R.id.ll_bottom);
ll_bottom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] location = new int[2];
v.getLocationOnScreen(location);
View popupView = LayoutInflater.from(getMContext()).inflate(R.layout.pop_windows, null);
PopupWindow popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, true);
popupView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#e0000000")));
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
int height=popupView.getMeasuredHeight();
popupWindow.showAtLocation(v, Gravity.NO_GRAVITY, location[0], location[1]-height);
}
});
源碼附上:源碼