<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android使用LabelsView實(shí)現(xiàn)標(biāo)簽列表控件功能

          共 1224字,需瀏覽 3分鐘

           ·

          2021-11-23 20:46

          無(wú)論是在移動(dòng)端的App,還是在前端的網(wǎng)頁(yè),我們經(jīng)常會(huì)看到下面這種標(biāo)簽的列表效果:


          標(biāo)簽從左到右擺放,一行顯示不下時(shí)自動(dòng)換行。這樣的效果用Android源生的控件很不好實(shí)現(xiàn),所以往往需要我們自己去自定義控件。我在開(kāi)發(fā)中就遇到過(guò)幾次要實(shí)現(xiàn)這樣的標(biāo)簽列表效果,所以就自己寫了個(gè)控件,下面我將為大家介紹該控件的具體實(shí)現(xiàn)和使用。

          要實(shí)現(xiàn)這樣一個(gè)標(biāo)簽列表其實(shí)并不難,列表中的item可以直接用TextView來(lái)實(shí)現(xiàn),我們只需要關(guān)心列表控件的大小和標(biāo)簽的擺放就可以了。也就是說(shuō)我們需要做的只要兩件事:測(cè)量布局(onMeasure)和擺放標(biāo)簽(onLayout)。這是自定義ViewGroup的基本步驟,相信對(duì)自定義View有所了解的同學(xué)都不會(huì)陌生。下面我們就來(lái)看看具體的代碼實(shí)現(xiàn)。

          控件的測(cè)量:
              @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int count = getChildCount(); int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
          int contentHeight = 0; //記錄內(nèi)容的高度 int lineWidth = 0; //記錄行的寬度 int maxLineWidth = 0; //記錄最寬的行寬 int maxItemHeight = 0; //記錄一行中item高度最大的高度 boolean begin = true; //是否是行的開(kāi)頭
          //循環(huán)測(cè)量item并計(jì)算控件的內(nèi)容寬高 for (int i = 0; i < count; i++) { View view = getChildAt(i); measureChild(view, widthMeasureSpec, heightMeasureSpec);
          if(!begin) { lineWidth += mWordMargin; }else { begin = false; } //當(dāng)前行顯示不下item時(shí)換行。 if (maxWidth <= lineWidth + view.getMeasuredWidth()) { contentHeight += mLineMargin; contentHeight += maxItemHeight; maxItemHeight = 0; maxLineWidth = Math.max(maxLineWidth, lineWidth); lineWidth = 0; begin = true; } maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
          lineWidth += view.getMeasuredWidth(); }
          contentHeight += maxItemHeight; maxLineWidth = Math.max(maxLineWidth, lineWidth);
          //測(cè)量控件的最終寬高 setMeasuredDimension(measureWidth(widthMeasureSpec,maxLineWidth), measureHeight(heightMeasureSpec, contentHeight));
          }
          //測(cè)量控件的寬 private int measureWidth(int measureSpec, int contentWidth) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);
          if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = contentWidth + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } //這一句是為了支持minWidth屬性。 result = Math.max(result, getSuggestedMinimumWidth()); return result; }
          //測(cè)量控件的高 private int measureHeight(int measureSpec, int contentHeight) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);
          if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = contentHeight + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } //這一句是為了支持minHeight屬性。 result = Math.max(result, getSuggestedMinimumHeight()); return result; }

          標(biāo)簽的擺放:

              @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          int x = getPaddingLeft(); int y = getPaddingTop();
          int contentWidth = right - left; int maxItemHeight = 0;
          int count = getChildCount(); //循環(huán)擺放item for (int i = 0; i < count; i++) { View view = getChildAt(i);
          //當(dāng)前行顯示不下item時(shí)換行。 if (contentWidth < x + view.getMeasuredWidth() + getPaddingRight()) { x = getPaddingLeft(); y += mLineMargin; y += maxItemHeight; maxItemHeight = 0; } view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight()); x += view.getMeasuredWidth(); x += mWordMargin; maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight()); } }


          onMeasure和onLayout的實(shí)現(xiàn)代碼基本是一樣的,不同的只是一個(gè)是測(cè)量寬高,一個(gè)是擺放位置而已。實(shí)現(xiàn)起來(lái)非常的簡(jiǎn)單。

          以上是LabelsView的核心代碼,LabelsView除了實(shí)現(xiàn)了item的測(cè)量和擺放以外,還提供了一系列的方法讓使用者可以方便設(shè)置標(biāo)簽的樣式(包括標(biāo)簽被選中的樣式)和標(biāo)簽點(diǎn)擊、選中的監(jiān)聽(tīng)等。

          下面LabelsView的使用介紹。


          1、引入依賴


          在Project的build.gradle在添加以下代碼
          allprojects {    repositories {        ...        maven { url 'https://jitpack.io' }    }}


          在Module的build.gradle在添加以下代碼

          dependencies {    compile 'com.github.donkingliang:LabelsView:1.4.1'}


          2、編寫布局:

                    xmlns:app="http://schemas.android.com/apk/res-auto"       android:id="@+id/labels"       android:layout_width="match_parent"       android:layout_height="wrap_content"       app:labelBackground="@drawable/label_bg"     //標(biāo)簽的背景       app:labelTextColor="@drawable/label_text_color" //標(biāo)簽的字體顏色 可以是一個(gè)顏色值       app:labelTextSize="14sp"      //標(biāo)簽的字體大小       app:labelTextPaddingBottom="5dp"   //標(biāo)簽的上下左右邊距       app:labelTextPaddingLeft="10dp"       app:labelTextPaddingRight="10dp"       app:labelTextPaddingTop="5dp"       app:lineMargin="10dp"   //行與行的距離       app:wordMargin="10dp"   //標(biāo)簽與標(biāo)簽的距離       app:selectType="SINGLE"   //標(biāo)簽的選擇類型 有單選(可反選)、單選(不可反選)、多選、不可選四種類型       app:maxSelect="5" />  //標(biāo)簽的最大選擇數(shù)量,只有多選的時(shí)候才有用,0為不限數(shù)量


          這里有兩個(gè)地方需要說(shuō)明一下:

          1)、標(biāo)簽的正常樣式和選中樣式是通過(guò)drawable來(lái)實(shí)現(xiàn)的。比如下面兩個(gè)drawable。
                                                                                                                                          


                          

          TextView的textColor屬性除了可以設(shè)置一個(gè)顏色值以外,也可以通過(guò)資源來(lái)設(shè)置的,這一點(diǎn)很多同學(xué)都不知道。

          2)標(biāo)簽的選擇類型有四種:

          NONE :標(biāo)簽不可選中,也不響應(yīng)選中事件監(jiān)聽(tīng),這是默認(rèn)值。

          SINGLE:?jiǎn)芜x(可反選)。這種模式下,可以一個(gè)也不選。

          SINGLE_IRREVOCABLY:?jiǎn)芜x(不可反選)。這種模式下,有且只有一個(gè)是選中的。默認(rèn)是第一個(gè)。

          MULTI:多選,可以通過(guò)設(shè)置maxSelect限定選擇的最大數(shù)量,0為不限數(shù)量。

          maxSelect只有在多選的時(shí)候才有效。多選模式下可以設(shè)置一些標(biāo)簽為必選項(xiàng)。必選項(xiàng)的標(biāo)簽?zāi)J(rèn)選中,且不能取消。


          3、設(shè)置標(biāo)簽:

          labelsView = (LabelsView) findViewById(labels);ArrayList label = new ArrayList<>();label.add("Android");label.add("IOS");label.add("前端");label.add("后臺(tái)");label.add("微信開(kāi)發(fā)");label.add("游戲開(kāi)發(fā)");labelsView.setLabels(label); //直接設(shè)置一個(gè)字符串?dāng)?shù)組就可以了。
          //LabelsView可以設(shè)置任何類型的數(shù)據(jù),而不僅僅是String。ArrayList testList = new ArrayList<>();testList.add(new TestBean("Android",1));testList.add(new TestBean("IOS",2));testList.add(new TestBean("前端",3));testList.add(new TestBean("后臺(tái)",4));testList.add(new TestBean("微信開(kāi)發(fā)",5));testList.add(new TestBean("游戲開(kāi)發(fā)",6));labelsView.setLabels(testList, new LabelsView.LabelTextProvider() { @Override public CharSequence getLabelText(TextView label, int position, TestBean data) { //根據(jù)data和position返回label需要顯示的數(shù)據(jù)。 return data.getName(); }});

          4、設(shè)置事件監(jiān)聽(tīng):(如果需要的話)
          //標(biāo)簽的點(diǎn)擊監(jiān)聽(tīng)labelsView.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {    @Override    public void onLabelClick(TextView label, Object data, int position) {         //label是被點(diǎn)擊的標(biāo)簽,data是標(biāo)簽所對(duì)應(yīng)的數(shù)據(jù),position是標(biāo)簽的位置。    }});//標(biāo)簽的選中監(jiān)聽(tīng)labelsView.setOnLabelSelectChangeListener(new LabelsView.OnLabelSelectChangeListener() {    @Override    public void onLabelSelectChange(TextView label, Object data, boolean isSelect, int position) {        //label是被選中的標(biāo)簽,data是標(biāo)簽所對(duì)應(yīng)的數(shù)據(jù),isSelect是是否選中,position是標(biāo)簽的位置。    }});

          5、常用方法
          //設(shè)置選中標(biāo)簽。//positions是個(gè)可變類型,表示被選中的標(biāo)簽的位置。//比喻labelsView.setSelects(1,2,5);選中第1,3,5個(gè)標(biāo)簽。如果是單選的話,只有第一個(gè)參數(shù)有效。public void setSelects(int... positions);public void setSelects(List positions);
          //獲取選中的標(biāo)簽(返回的是所有選中的標(biāo)簽的位置)。返回的是一個(gè)Integer的數(shù)組,表示被選中的標(biāo)簽的下標(biāo)。如果沒(méi)有選中,數(shù)組的size等于0。public ArrayList getSelectLabels();//獲取選中的label(返回的是所有選中的標(biāo)簽的數(shù)據(jù))。如果沒(méi)有選中,數(shù)組的size等于0。T表示標(biāo)簽的數(shù)據(jù)類型。public List getSelectLabelDatas();
          //取消所有選中的標(biāo)簽。public void clearAllSelect();
          //設(shè)置標(biāo)簽的選擇類型,有NONE、SINGLE、SINGLE_IRREVOCABLY和MULTI四種類型。public void setSelectType(SelectType selectType);
          //設(shè)置最大的選擇數(shù)量,只有selectType等于MULTI是有效。public void setMaxSelect(int maxSelect);
          //設(shè)置必選項(xiàng),只有在多項(xiàng)模式下,這個(gè)方法才有效public void setCompulsorys(int... positions)public void setCompulsorys(List positions)
          //清空必選項(xiàng),只有在多項(xiàng)模式下,這個(gè)方法才有效public void clearCompulsorys()
          //設(shè)置標(biāo)簽背景public void setLabelBackgroundResource(int resId);
          //設(shè)置標(biāo)簽的文字顏色public void setLabelTextColor(int color);public void setLabelTextColor(ColorStateList color);
          //設(shè)置標(biāo)簽的文字大小(單位是px)public void setLabelTextSize(float size);
          //設(shè)置標(biāo)簽內(nèi)邊距public void setLabelTextPadding(int left, int top, int right, int bottom);
          //設(shè)置行間隔public void setLineMargin(int margin);
          //設(shè)置標(biāo)簽的間隔public void setWordMargin(int margin);

          所有的set方法都有對(duì)應(yīng)的get方法,這里就不說(shuō)了。

          效果圖:


          源碼地址:

          https://github.com/donkingliang/LabelsView

          到這里就結(jié)束啦。
          瀏覽 86
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产久久婷婷 | 一区二三区三区四区五区视频 | 美女高潮喷水视频 | 久久综合88 | 免费久久大香蕉 |