Android 自定义标签列表控件 LabelsView 解析

AleMasten 8年前
   <p>无论是在移动端的App,还是在前端的网页,我们经常会看到下面这种标签的列表效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/586c9d532da06899059499c42df067c8.png"> <img src="https://simg.open-open.com/show/b97ec889abed27092edb2ef7fd34236f.png"></p>    <p>标签从左到右摆放,一行显示不下时自动换行。这样的效果用Android源生的控件很不好实现,所以往往需要我们自己去自定义控件。我在开发中就遇到过几次要实现这样的标签列表效果,所以就自己写了个控件,放到 我的GitHub ,方便以后使用。有兴趣的同学也欢迎访问我的GitHub、查看源码实现和使用该控件。下面我将为大家介绍该控件的具体实现和使用。</p>    <p>要实现这样一个标签列表其实并不难,列表中的item可以直接用TextView来实现,我们只需要关心列表控件的大小和标签的摆放就可以了。也就是说我们需要做的只要两件事:测量布局( <strong>onMeasure</strong> )和摆放标签( <strong>onLayout</strong> )。这是自定义ViewGroup的基本步骤,相信对自定义View有所了解的同学都不会陌生。下面我们就来看看具体的代码实现。</p>    <p>控件的测量:</p>    <pre>  <code class="language-java">@Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {            int count = getChildCount();          int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();            int contentHeight = 0; //记录内容的高度          int lineWidth = 0; //记录行的宽度          int maxLineWidth = 0; //记录最宽的行宽          int maxItemHeight = 0; //记录一行中item高度最大的高度          boolean begin = true; //是否是行的开头            //循环测量item并计算控件的内容宽高          for (int i = 0; i < count; i++) {              View view = getChildAt(i);              measureChild(view, widthMeasureSpec, heightMeasureSpec);                //当前行显示不下item时换行。              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());              if(!begin) {                  lineWidth += mWordMargin;              }else {                  begin = false;              }              lineWidth += view.getMeasuredWidth();          }            contentHeight += maxItemHeight;          maxLineWidth = Math.max(maxLineWidth, lineWidth);            //测量控件的最终宽高          setMeasuredDimension(measureWidth(widthMeasureSpec,maxLineWidth),                  measureHeight(heightMeasureSpec, contentHeight));        }        //测量控件的宽      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;      }        //测量控件的高      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;      }</code></pre>    <p>标签的摆放:</p>    <pre>  <code class="language-java">@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();          //循环摆放item          for (int i = 0; i < count; i++) {              View view = getChildAt(i);                //当前行显示不下item时换行。              if (contentWidth < x + view.getMeasuredWidth()) {                  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());          }      }</code></pre>    <p>onMeasure和onLayout的实现代码基本是一样的,不同的只是一个是测量宽高,一个是摆放位置而已。实现起来非常的简单。</p>    <p>控件的使用就更加的简单了,只需要三步:</p>    <p>1、编写布局:</p>    <pre>  <code class="language-java"><com.donkingliang.labels.LabelsView          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/pink_frame_bg"  //标签的背景          app:labelTextColor="#fb435b"  //标签的字体颜色          app:labelTextSize="14sp"  //标签的字体大小          app:labelTextPaddingBottom="5dp"  //标签的上下左右边距          app:labelTextPaddingLeft="10dp"          app:labelTextPaddingRight="10dp"          app:labelTextPaddingTop="5dp"          app:lineMargin="10dp"  //行与行的距离          app:wordMargin="10dp" />  //标签与标签的距离</code></pre>    <p>2、设置标签:</p>    <pre>  <code class="language-java">ArrayList<String> list = new ArrayList<>();          list.add("Android");          list.add("IOS");          list.add("前端");          list.add("后台");          list.add("微信开发");          list.add("Java");          list.add("JavaScript");          list.add("C++");          list.add("PHP");          list.add("Python");      labelsView.setLabels(list);</code></pre>    <p>3、设置点击监听:(如果需要的话)</p>    <pre>  <code class="language-java">labels.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {              @Override              public void onLabelClick(TextView label, int position) {                  //label就是被点击的标签,position就是标签的位置。              }          });</code></pre>    <p style="text-align:center">效果图: <img src="https://simg.open-open.com/show/5423d7d87f063b89ea103e1ba8165454.png"></p>    <p>使用前不要忘了引入依赖:</p>    <pre>  <code class="language-java">allprojects {          repositories {              ...              maven { url 'https://jitpack.io' }          }      }    dependencies {          compile 'com.github.donkingliang:LabelsView:1.0.0'      }</code></pre>    <p> </p>    <p> </p>    <p>来自:http://blog.csdn.net/u010177022/article/details/60324117</p>    <p> </p>