Android 自定義標簽列表控件 LabelsView 解析

AleMasten 7年前發布 | 6K 次閱讀 安卓開發 Android開發 移動開發

無論是在移動端的App,還是在前端的網頁,我們經常會看到下面這種標簽的列表效果:

標簽從左到右擺放,一行顯示不下時自動換行。這樣的效果用Android源生的控件很不好實現,所以往往需要我們自己去自定義控件。我在開發中就遇到過幾次要實現這樣的標簽列表效果,所以就自己寫了個控件,放到 我的GitHub ,方便以后使用。有興趣的同學也歡迎訪問我的GitHub、查看源碼實現和使用該控件。下面我將為大家介紹該控件的具體實現和使用。

要實現這樣一個標簽列表其實并不難,列表中的item可以直接用TextView來實現,我們只需要關心列表控件的大小和標簽的擺放就可以了。也就是說我們需要做的只要兩件事:測量布局( onMeasure )和擺放標簽( onLayout )。這是自定義ViewGroup的基本步驟,相信對自定義View有所了解的同學都不會陌生。下面我們就來看看具體的代碼實現。

控件的測量:

@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;
    }

標簽的擺放:

@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());
        }
    }

onMeasure和onLayout的實現代碼基本是一樣的,不同的只是一個是測量寬高,一個是擺放位置而已。實現起來非常的簡單。

控件的使用就更加的簡單了,只需要三步:

1、編寫布局:

<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" />  //標簽與標簽的距離

2、設置標簽:

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);

3、設置點擊監聽:(如果需要的話)

labels.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {
            @Override
            public void onLabelClick(TextView label, int position) {
                //label就是被點擊的標簽,position就是標簽的位置。
            }
        });

效果圖:

使用前不要忘了引入依賴:

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

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

 

 

來自:http://blog.csdn.net/u010177022/article/details/60324117

 

 本文由用戶 AleMasten 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!