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

自定義熱門組件

為了節(jié)約屏幕空間座咆,盡可能的添加更多的熱門關(guān)鍵詞泛烙,該組件設(shè)計(jì)成可以收縮和擴(kuò)大店量,并且可以通過(guò)上下滑動(dòng)來(lái)選取熱門關(guān)鍵詞;先上圖:


show

設(shè)計(jì)過(guò)程思想:

  1. 首先,組件能填充許多小的字符組件如TextView玉雾,所以它必須是一個(gè)ViewGroup
  2. 其次翔试, 關(guān)鍵詞的布局問題,諸多的組件能夠在viewgroup空間里面按照一定的格式布局出來(lái)复旬,不出現(xiàn)排列混亂的情況垦缅,保證child之間的間隔的padding等;這點(diǎn)需要自行完成onMeasure和onLayout的位置問題驹碍,這點(diǎn)需要關(guān)注
  3. 滑動(dòng)問題壁涎, 支持上下滑動(dòng),需要我們重寫onTouchEvent事件志秃,與view的scrollTo和scrollBy來(lái)完成
  4. 擴(kuò)張和縮放(上圖怔球,點(diǎn)擊查看更多就會(huì)擴(kuò)大或縮小熱門搜索大小)
  5. 最后一個(gè)是監(jiān)聽的浮还,點(diǎn)擊關(guān)鍵詞竟坛,觸發(fā)相應(yīng)的事件,這個(gè)可以在外部使用的時(shí)候做钧舌,不需要過(guò)多關(guān)心
    下面就將上面的幾個(gè)關(guān)鍵點(diǎn):

child之間的布局問題

測(cè)量 -- onMeausre測(cè)量組件自身的大小

@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ù)改為擴(kuò)張高度

        measureChildren(widthMeasureSpec, heightMeasureSpec);

        setMeasuredDimension(sizeWidth, view_default_height);

    }

主要是給定當(dāng)前組件一個(gè)固定的高度view_default_height,并且這個(gè)高度在后續(xù)的擴(kuò)張和縮小會(huì)用到

布局放置 -- onLayout放置每個(gè)child的位置

布局思想就是:
寬度的布局: 依次測(cè)量每個(gè)child的寬度并累加延刘,如果寬度和大于viewgroup的寬度漫试,就把前面幾個(gè)child拿來(lái)進(jìn)行一行的布局,在此條件下還有可能出現(xiàn)剩余空間碘赖,將剩余空間分?jǐn)偟矫總€(gè)組件上去即可驾荣;如下圖:

布局

高度的布局: 記錄每一行的的高度布局位置,下次布局從上次的高度布局開始向下布局即可普泡,
實(shí)現(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;
            }

        }

        /**
         * 說(shuō)明還有一部分沒有布局的child
         */
        if(end_index != childCount){
            onLayoutChildView(start_index, childCount, l, startY, 0);
        }
        second_enter++;
    }

    /**
     * 布局一行的組件視圖
     * @param start_index  其實(shí)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;                                                                           //需要將每行的剩余空間分?jǐn)偟矫總€(gè)組件上去,減去30是一個(gè)選取的值,防止計(jì)算大小的精確問題撼班,超出右邊父的最長(zhǎng)寬度
        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){                                                                   //分?jǐn)傊恍枰谇皟纱芜M(jìn)行分?jǐn)偲缧伲罄m(xù)不在分?jǐn)偅灰驗(yàn)楹罄m(xù)布局都已經(jīng)完成砰嘁,再次分?jǐn)倳?huì)照成重新獲取padding值件炉,該值會(huì)累加的
                int paddingLR = child.getPaddingLeft() + sub_space / 2;
                int paddingTB = child.getPaddingBottom();
                child.setPadding(paddingLR, paddingTB, paddingLR, paddingTB);
            }
                                                                                                    //每排最后一個(gè)必須等于右邊限制的位置,對(duì)齊矮湘;除了最后一排單獨(dú)幾個(gè)那種
            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){                                                                        //最后一行時(shí)斟冕,計(jì)算父組件最大值和child最下面的Y值,計(jì)算差值作為向上滑動(dòng)的最大距離
            moveUpDistance = endY - parent_bottom + default_space + 40;
        }

    }

觸摸滑動(dòng)

設(shè)計(jì)思想:
看圖就明白了缅阳,主要是判斷向上和向下的滑動(dòng)距離磕蛇,要限制其滑動(dòng)的最大距離

這里寫圖片描述

代碼很簡(jiǎn)單,如下:

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        //只有在展開的情況下才能進(jìn)行滑動(dòng)操作

        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;                                                             //需要移動(dòng)的距離
                int need_move_y = getScrollY() + dy;                                                //getScrollY()會(huì)得到一個(gè)距離值,該距離值=原始組件位置和偏移后組件的差值
                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;
    }
}

完成到這里秀撇,組件就可以上下滑動(dòng)了超棺;但是在這兒有個(gè)問題,我也沒搞懂呵燕,當(dāng)你添加的child設(shè)置了setOnclick后滑動(dòng)就會(huì)受干擾棠绘,如果是addTouchListener的話就能正常的上下滑動(dòng),根據(jù)監(jiān)聽事件的傳遞機(jī)制是:dispatch -- onTouch -- intecptTouch -- onTouchEvent -- onClick,而且這又涉及了很多child我懷疑是某個(gè)child消費(fèi)了滑動(dòng)事件導(dǎo)致的再扭,但是還沒找到解決方法弄唧,哪位能解決了,還請(qǐng)告知

至此霍衫,組件就設(shè)計(jì)完成了候引,源碼在下面:
https://github.com/JackZhous/HotSearchViewGroup

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市敦跌,隨后出現(xiàn)的幾起案子澄干,更是在濱河造成了極大的恐慌,老刑警劉巖柠傍,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件麸俘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惧笛,警方通過(guò)查閱死者的電腦和手機(jī)从媚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)患整,“玉大人拜效,你說(shuō)我怎么就攤上這事「餮瑁” “怎么了紧憾?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)昌渤。 經(jīng)常有香客問我赴穗,道長(zhǎng),這世上最難降的妖魔是什么膀息? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任般眉,我火速辦了婚禮,結(jié)果婚禮上潜支,老公的妹妹穿的比我還像新娘甸赃。我一直安慰自己,他們只是感情好毁腿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布辑奈。 她就那樣靜靜地躺著,像睡著了一般已烤。 火紅的嫁衣襯著肌膚如雪鸠窗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天胯究,我揣著相機(jī)與錄音稍计,去河邊找鬼。 笑死裕循,一個(gè)胖子當(dāng)著我的面吹牛臣嚣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剥哑,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼硅则,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了株婴?” 一聲冷哼從身側(cè)響起怎虫,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎困介,沒想到半個(gè)月后大审,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡座哩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年徒扶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片根穷。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姜骡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屿良,到底是詐尸還是另有隱情溶浴,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布管引,位于F島的核電站士败,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏褥伴。R本人自食惡果不足惜谅将,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望重慢。 院中可真熱鬧饥臂,春花似錦、人聲如沸似踱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至囚戚,卻和暖如春酵熙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驰坊。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工匾二, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拳芙。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓察藐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親舟扎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子分飞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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