一款自定義的熱門搜索組件

自定義熱門組件

為了節(jié)約屏幕空間,盡可能的添加更多的熱門關鍵詞,該組件設計成可以收縮和擴大,并且可以通過上下滑動來選取熱門關鍵詞;先上圖:


show

設計過程思想:

  1. 首先,組件能填充許多小的字符組件如TextView,所以它必須是一個ViewGroup
  2. 其次, 關鍵詞的布局問題,諸多的組件能夠在viewgroup空間里面按照一定的格式布局出來,不出現(xiàn)排列混亂的情況,保證child之間的間隔的padding等;這點需要自行完成onMeasure和onLayout的位置問題,這點需要關注
  3. 滑動問題, 支持上下滑動,需要我們重寫onTouchEvent事件,與view的scrollTo和scrollBy來完成
  4. 擴張和縮放(上圖,點擊查看更多就會擴大或縮小熱門搜索大小)
  5. 最后一個是監(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容