Android自定義ViewGroup實(shí)現(xiàn)堆疊頭像的點(diǎn)贊Layout
簡(jiǎn)介

這樣的點(diǎn)贊列表怎么樣?之前做社區(qū)的時(shí)候也有類(lèi)似的點(diǎn)贊列表,但是沒(méi)有這樣重疊,一個(gè)小小的改變,個(gè)人感覺(jué)逼格提高不少。
這個(gè)很有規(guī)則,就是后一個(gè)頭像會(huì)覆蓋一部分到前一個(gè)頭像上,頭像多了就像一串糖葫蘆了。
這個(gè)實(shí)現(xiàn)起來(lái)不難,自定義ViewGroup,關(guān)鍵重寫(xiě)onLayout方法。
關(guān)于自定義控件的基礎(chǔ)知識(shí)可以看一看這個(gè),整理的很詳細(xì): https://github.com/GcsSloop/AndroidNote
實(shí)現(xiàn)
自定義屬性
| 屬性名 | 說(shuō)明 | 默認(rèn)值 |
|---|---|---|
| vertivalSpace | 行距 | 4dp |
| pileWidth | 重疊寬度 | 10dp |
onMeasure方法,每行的寬度不再是child的寬度和了,而是要減掉重疊部分的寬度和
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
//AT_MOST
int width = 0;
int height = 0;
int rawWidth = 0;//當(dāng)前行總寬度
int rawHeight = 0;// 當(dāng)前行高
int rowIndex = 0;//當(dāng)前行位置
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if(child.getVisibility() == GONE){
if(i == count - 1){
//最后一個(gè)child
height += rawHeight;
width = Math.max(width, rawWidth);
}
continue;
}
//這里調(diào)用measureChildWithMargins 而不是measureChild
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
if(rawWidth + childWidth - (rowIndex > 0 ? pileWidth : 0)> widthSpecSize - getPaddingLeft() - getPaddingRight()){
//換行
width = Math.max(width, rawWidth);
rawWidth = childWidth;
height += rawHeight + vertivalSpace;
rawHeight = childHeight;
rowIndex = 0;
} else {
rawWidth += childWidth;
if(rowIndex > 0){
rawWidth -= pileWidth;
}
rawHeight = Math.max(rawHeight, childHeight);
}
if(i == count - 1){
width = Math.max(rawWidth, width);
height += rawHeight;
}
rowIndex++;
}
setMeasuredDimension(
widthSpecMode == MeasureSpec.EXACTLY ? widthSpecSize : width + getPaddingLeft() + getPaddingRight(),
heightSpecMode == MeasureSpec.EXACTLY ? heightSpecSize : height + getPaddingTop() + getPaddingBottom()
);
}
onLayout 每一行,第一個(gè)正常放,之后的重疊放
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int viewWidth = r - l;
int leftOffset = getPaddingLeft();
int topOffset = getPaddingTop();
int rowMaxHeight = 0;
int rowIndex = 0;//當(dāng)前行位置
View childView;
for( int w = 0, count = getChildCount(); w < count; w++ ){
childView = getChildAt(w);
if(childView.getVisibility() == GONE) continue;
MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
// 如果加上當(dāng)前子View的寬度后超過(guò)了ViewGroup的寬度,就換行
int occupyWidth = lp.leftMargin + childView.getMeasuredWidth() + lp.rightMargin;
if(leftOffset + occupyWidth + getPaddingRight() > viewWidth){
leftOffset = getPaddingLeft(); // 回到最左邊
topOffset += rowMaxHeight + vertivalSpace; // 換行
rowMaxHeight = 0;
rowIndex = 0;
}
int left = leftOffset + lp.leftMargin;
int top = topOffset + lp.topMargin;
int right = leftOffset+ lp.leftMargin + childView.getMeasuredWidth();
int bottom = topOffset + lp.topMargin + childView.getMeasuredHeight();
childView.layout(left, top, right, bottom);
// 橫向偏移
leftOffset += occupyWidth;
// 試圖更新本行最高View的高度
int occupyHeight = lp.topMargin + childView.getMeasuredHeight() + lp.bottomMargin;
if(rowIndex != count - 1){
leftOffset -= pileWidth;//這里控制重疊位置
}
rowMaxHeight = Math.max(rowMaxHeight, occupyHeight);
rowIndex++;
}
}
效果圖

因?yàn)檫@個(gè)一般只會(huì)顯示一行,所以暫時(shí)沒(méi)有通過(guò)setAdapter方式去設(shè)置數(shù)據(jù)源。
下載
https://github.com/LineChen/PileLayout
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android自定義ViewGroup實(shí)現(xiàn)帶箭頭的圓角矩形菜單
- Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽浮動(dòng)效果
- Android自定義ViewGroup之實(shí)現(xiàn)FlowLayout流式布局
- Android App開(kāi)發(fā)中自定義View和ViewGroup的實(shí)例教程
- 一篇文章弄懂Android自定義viewgroup的相關(guān)難點(diǎn)
- Android應(yīng)用開(kāi)發(fā)中自定義ViewGroup的究極攻略
- Android自定義ViewGroup實(shí)現(xiàn)受邊界限制的滾動(dòng)操作(3)
- Android動(dòng)畫(huà)效果之自定義ViewGroup添加布局動(dòng)畫(huà)(五)
- Android自定義ViewGroup的實(shí)現(xiàn)方法
- Android自定義ViewGroup實(shí)現(xiàn)朋友圈九宮格控件
相關(guān)文章
解決Android studio模擬器啟動(dòng)失敗的問(wèn)題
這篇文章主要介紹了Android studio模擬器啟動(dòng)失敗的問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
Android BottomSheetDialog實(shí)現(xiàn)底部對(duì)話框的示例
這篇文章主要介紹了Android BottomSheetDialog實(shí)現(xiàn)底部對(duì)話框的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
Android學(xué)習(xí)筆記之應(yīng)用單元測(cè)試實(shí)例分析
這篇文章主要介紹了Android學(xué)習(xí)筆記之應(yīng)用單元測(cè)試,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android單元測(cè)試的實(shí)現(xiàn)原理與具體步驟,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
Android中常用的三個(gè)Dialog彈窗總結(jié)解析
自己雖然一直使用過(guò)dialog,但是一直都是復(fù)制、粘貼;不清楚dialog的具體用途,這次趁著有時(shí)間,總結(jié)一下具體用法,感興趣的朋友跟著小編來(lái)看看吧2021-10-10
Android仿泡泡窗實(shí)現(xiàn)下拉菜單條實(shí)例代碼
最近參與android的項(xiàng)目開(kāi)發(fā),其中遇到這樣的需求:點(diǎn)擊下拉按鈕,顯示出所有的條目,有刪除和點(diǎn)擊功能,點(diǎn)擊后將條目顯示。下面通過(guò)實(shí)例代碼給大家介紹下Android仿泡泡窗實(shí)現(xiàn)下拉菜單條效果,需要的朋友參考下吧2017-05-05
Android自定義Dialog實(shí)現(xiàn)文字動(dòng)態(tài)加載效果
這篇文章主要為大家詳細(xì)介紹了Android自定義Dialog實(shí)現(xiàn)文字動(dòng)態(tài)加載效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08
Android Service判斷設(shè)備聯(lián)網(wǎng)狀態(tài)詳解
本文主要介紹Android Service判斷聯(lián)網(wǎng)狀態(tài),這里提供了相關(guān)資料并附有示例代碼,有興趣的小伙伴可以參考下,幫助開(kāi)發(fā)相關(guān)應(yīng)用功能2016-08-08
怎樣刪除android的gallery中的圖片實(shí)例說(shuō)明
長(zhǎng)按gallery中的圖片進(jìn)行刪除該圖片的操作,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06
Diycode開(kāi)源項(xiàng)目實(shí)例搭建上拉加載和下拉刷新的Fragment
這篇文章主要介紹了Diycode開(kāi)源項(xiàng)目實(shí)例搭建上拉加載和下拉刷新的Fragment以及相關(guān)的代碼分享。2017-11-11

