自定義熱門組件
為了節(jié)約屏幕空間,盡可能的添加更多的熱門關鍵詞,該組件設計成可以收縮和擴大,并且可以通過上下滑動來選取熱門關鍵詞;先上圖:
show
設計過程思想:
- 首先,組件能填充許多小的字符組件如TextView,所以它必須是一個ViewGroup
- 其次, 關鍵詞的布局問題,諸多的組件能夠在viewgroup空間里面按照一定的格式布局出來,不出現(xiàn)排列混亂的情況,保證child之間的間隔的padding等;這點需要自行完成onMeasure和onLayout的位置問題,這點需要關注
- 滑動問題, 支持上下滑動,需要我們重寫onTouchEvent事件,與view的scrollTo和scrollBy來完成
- 擴張和縮放(上圖,點擊查看更多就會擴大或縮小熱門搜索大小)
- 最后一個是監(jiān)聽的,點擊關鍵詞,觸發(fā)相應的事件,這個可以在外部使用的時候做,不需要過多關心
下面就將上面的幾個關鍵點:
child之間的布局問題
測量 -- onMeausre測量組件自身的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
childCount = getChildCount();
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
view_default_height = (view_default_height == 0) ? sizeHeight : view_default_height; //view_default_height用于保存組件的原始高度,后續(xù)改為擴張高度
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(sizeWidth, view_default_height);
}
主要是給定當前組件一個固定的高度view_default_height,并且這個高度在后續(xù)的擴張和縮小會用到
布局放置 -- onLayout放置每個child的位置
布局思想就是:
寬度的布局: 依次測量每個child的寬度并累加,如果寬度和大于viewgroup的寬度,就把前面幾個child拿來進行一行的布局,在此條件下還有可能出現(xiàn)剩余空間,將剩余空間分攤到每個組件上去即可;如下圖:
布局
高度的布局: 記錄每一行的的高度布局位置,下次布局從上次的高度布局開始向下布局即可,
實現(xiàn)代碼如下:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
parent_bottom = b;
parent_width = r;
int childViewWidth = l; //一行child寬度和,用于判斷是否超出父的寬度
int start_index = 0;
int end_index = 0;
int startX = l;
int startY = t;
int space;
for(int i = 0; i < childCount; i++){
View child = getChildAt(i);
int sizeWidth = child.getMeasuredWidth();
int sizeHeight = child.getMeasuredHeight();
space = r - childViewWidth; //一行里面的剩余空間
childViewWidth = childViewWidth + default_space + sizeWidth; //一行view的寬度和 = 組件寬度 + 間隔
if(childViewWidth > r - 30){
end_index = i;
onLayoutChildView(start_index, end_index, startX, startY, space);
startY = startY + default_space + sizeHeight;
childViewWidth = l + sizeWidth;
start_index = i;
}
}
/**
* 說明還有一部分沒有布局的child
*/
if(end_index != childCount){
onLayoutChildView(start_index, childCount, l, startY, 0);
}
second_enter++;
}
/**
* 布局一行的組件視圖
* @param start_index 其實child
* @param end_index 結(jié)束child
* @param start_x x開始的位置
* @param start_y y開始的位置
* @param space 一行剩余的空間
*/
private void onLayoutChildView(int start_index, int end_index, int start_x, int start_y, int space){
int endX = 0;
int endY = 0;
int sub_space = 0; //需要將每行的剩余空間分攤到每個組件上去,減去30是一個選取的值,防止計算大小的精確問題,超出右邊父的最長寬度
if(space - 30 > 0){
int view_numbers = end_index - start_index + 1;
sub_space = (space - 30)/ view_numbers;
}
int i;
for(i = start_index; i < end_index; i++){
View child = getChildAt(i);
endX = start_x + child.getMeasuredWidth() + sub_space;
endY = start_y + child.getMeasuredHeight();
if(second_enter < 2){ //分攤只需要在前兩次進行分攤,后續(xù)不在分攤;因為后續(xù)布局都已經(jīng)完成,再次分攤會照成重新獲取padding值,該值會累加的
int paddingLR = child.getPaddingLeft() + sub_space / 2;
int paddingTB = child.getPaddingBottom();
child.setPadding(paddingLR, paddingTB, paddingLR, paddingTB);
}
//每排最后一個必須等于右邊限制的位置,對齊;除了最后一排單獨幾個那種
if(i == end_index - 1 && space != 0){
endX = parent_width - 30;
}
child.layout(start_x, start_y, endX, endY);
start_x = endX + default_space;
}
if(i == childCount){ //最后一行時,計算父組件最大值和child最下面的Y值,計算差值作為向上滑動的最大距離
moveUpDistance = endY - parent_bottom + default_space + 40;
}
}
觸摸滑動
設計思想:
看圖就明白了,主要是判斷向上和向下的滑動距離,要限制其滑動的最大距離
這里寫圖片描述
代碼很簡單,如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
//只有在展開的情況下才能進行滑動操作
if(!isExpand){
return super.onTouchEvent(event);
}
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
downY = (int)event.getY();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int)event.getY();
int dy = downY - moveY; //需要移動的距離
int need_move_y = getScrollY() + dy; //getScrollY()會得到一個距離值,該距離值=原始組件位置和偏移后組件的差值
if(need_move_y < 0){
scrollTo(0, 0);
}else if (need_move_y > moveUpDistance){
scrollTo(0, moveUpDistance);
}else{
scrollBy(0, dy);
}
downY = moveY;
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
}
完成到這里,組件就可以上下滑動了;但是在這兒有個問題,我也沒搞懂,當你添加的child設置了setOnclick后滑動就會受干擾,如果是addTouchListener的話就能正常的上下滑動,根據(jù)監(jiān)聽事件的傳遞機制是:dispatch -- onTouch -- intecptTouch -- onTouchEvent -- onClick,而且這又涉及了很多child我懷疑是某個child消費了滑動事件導致的,但是還沒找到解決方法,哪位能解決了,還請告知
至此,組件就設計完成了,源碼在下面:
https://github.com/JackZhous/HotSearchViewGroup