TagView的用法及源碼解析

YFAndroidLibs之TagView的用法及源碼解析

關於(About)

TagView的實現思路相對簡單,主要是繼承ViewGroup,重新相應的onLayout和onMeasure方法:

效果圖如下:

不同樣式下的tag
這裏寫圖片描述

設計思路及主要接口(Features)

ViewGroup中主要的方法包括:onLayout和onMeasure方法,
調用的順序爲:onMeasure ——> onLayout ——> onDraw,一般不重寫onDraw方法,主要是隱示的調用View中的onDraw方法。

1.onMeasur方法:用來測量view的實際寬高:

        for(int i=0;i<count;i++){
            final View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);  
            childWidth = child.getMeasuredWidth();
            childHeight = child.getMeasuredHeight();
            curLeft += (childWidth+VIEW_MARGIN);            
            rowChildCount++;
            if(curLeft>=parentWidth){
                if(rowChildCount<2){
                    rowList.add(new RowInfo(rowChildCount, parentWidth-VIEW_MARGIN-childWidth-VIEW_MARGIN, childHeight));  //第一個子控件就超過父控件寬度時,直接縮小到同樣寬度
                    curLeft = VIEW_MARGIN;  
                    rowChildCount =0;
                    rowMaxHeight = 0;
                    totalHeight += childHeight+VIEW_MARGIN;
                }else{
                    rowList.add(new RowInfo(rowChildCount-1, parentWidth-(curLeft-childWidth-VIEW_MARGIN), rowMaxHeight));  //保存每一行的子控件數量,與父控件的空隙,最高的子控件高度
                    curLeft = VIEW_MARGIN+childWidth+VIEW_MARGIN;   
                    rowChildCount =1;
                    totalHeight += rowMaxHeight+VIEW_MARGIN;
                    rowMaxHeight = childHeight;
                }
            }else{
                rowMaxHeight = Math.max(rowMaxHeight, childHeight);  //這個取值要保證在判斷寬度的後面,防止把下一行的第一個的高度取到
            }
        }
        if(rowChildCount>0){
            rowList.add(new RowInfo(rowChildCount,parentWidth-curLeft, rowMaxHeight));   //此處減去二次View_Margin,不知道原理。需要再研究
            totalHeight +=rowMaxHeight+VIEW_MARGIN;
        }
         setMeasuredDimension(resolveSize(parentWidth, widthMeasureSpec),resolveSize(totalHeight, heightMeasureSpec));  

        Log.e("autobreak", "changed---------> measure");
    }else{
        Log.e("autobreak", "no changed ,no measure");
         setMeasuredDimension(resolveSize(parentWidth, widthMeasureSpec),resolveSize(getRowTotalHeight(), heightMeasureSpec));  //此處要再設置一下高度。

    }

2.onLayout方法:該方法主要用來擺放子view的位置:核心代碼如下

     for(int i=0;i<count;i++){

        final View child = getChildAt(i);  //得到子控件及寬、高值
        int childWidth = child.getMeasuredWidth();
        int childHeight = child.getMeasuredHeight();  

        if(row<rowList.size()){  //獲取到當前行的排版信息
            curRowHeight = rowList.get(row).maxHeight;  //獲取到當前行的最大高度值
            curCol =  rowList.get(row).cols;
            if(curCol>0){
                curSpace = rowList.get(row).space / curCol;
            }
        }
        curRight = curLeft + childWidth + VIEW_MARGIN;  //得到當前子控件的右邊位置
        if(curRight>parentWidth){  //如果當前控件超出了父控件寬度。則換行排放
            if(curCol==1&&i==0){ //如果第一行同時只是一個子控件超過父控件了,則縮小放置
                child.layout(curLeft, curTop+((curRowHeight-childHeight)/2), curLeft+childWidth, curTop+((curRowHeight-childHeight)/2)+childHeight); //讓高度小於行最大高度的控件,垂直居中顯
                curLeft = VIEW_MARGIN;  //復位左側放置點到下一行最左邊
                curTop += curRowHeight+VIEW_MARGIN;  //更新高度定位點爲下一行的高度
                row++;
            }else{
                curLeft = VIEW_MARGIN;  //復位左側放置點到下一行最左邊
                curTop += curRowHeight+VIEW_MARGIN;  //更新高度定位點爲下一行的高度
                curRight = curLeft + childWidth + VIEW_MARGIN;  //得到當前子控件的右邊位置
                row++;
                if(row<rowList.size()){  //獲取到當前行的排版信息
                    curRowHeight = rowList.get(row).maxHeight;  //獲取到當前行的最大高度值
                    curCol =  rowList.get(row).cols;
                    if(curCol>0){
                        curSpace = rowList.get(row).space / curCol;
                    }
                }
                if(curRight>parentWidth){  //如果換行後,直接大於父控件寬度,說明當前子控件自己就寬過父控件,則直接按一行一個控件處理
                    child.layout(curLeft, curTop+((curRowHeight-childHeight)/2), curLeft+childWidth, curTop+((curRowHeight-childHeight)/2)+childHeight); //讓高度小於行最大高度的控件,垂直居中顯
                    curLeft = VIEW_MARGIN;  //復位左側放置點到下一行最左邊
                    curTop += curRowHeight+VIEW_MARGIN;  //更新高度定位點爲下一行的高度
                    row++;
                }else{
                    if(mTagViewStyle.getmTagStyle()==TagStyle.wrap){
                        child.layout(curLeft, curTop+((curRowHeight-childHeight)/2), curLeft+childWidth, curTop+((curRowHeight-childHeight)/2)+childHeight); //讓高度小於行最大高度的控件,垂直居中顯
                        curLeft = curLeft+childWidth+VIEW_MARGIN;  //更新左側放置點到新放控件後面
                    }else {
                        child.layout(curLeft, curTop+((curRowHeight-childHeight)/2), curLeft+childWidth+curSpace, curTop+((curRowHeight-childHeight)/2)+childHeight); //讓高度小於行最大高度的控件,垂直居中顯
                        curLeft = curLeft+childWidth+curSpace+VIEW_MARGIN;  //更新左側放置點到新放控件後面
                    }

                }
            }               
        }else{ //未超出,則直接排放位置

            if(mTagViewStyle.getmTagStyle()==TagStyle.wrap){
                child.layout(curLeft, curTop+((curRowHeight-childHeight)/2), curLeft+childWidth, curTop+((curRowHeight-childHeight)/2)+childHeight); //讓高度小於行最大高度的控件,垂直居中顯
                curLeft = curLeft+childWidth+VIEW_MARGIN;  //更新左側放置點到新放控件後面
            }else {

                child.layout(curLeft, curTop+((curRowHeight-childHeight)/2), curLeft+childWidth+curSpace, curTop+((curRowHeight-childHeight)/2)+childHeight); //讓高度小於行最大高度的控件,垂直居中顯
                curLeft = curLeft+childWidth+curSpace+VIEW_MARGIN;  //更新左側放置點到新放控件後面

            }


        }

     }

3.在代碼中使用了curSpace參數,來計算每一行的空餘寬度,用戶可以選用兩種不同的樣式:

/*
 * 樣式
 */
public enum TagStyle{
        wrap,match
}

private TagStyle  mTagStyle=TagStyle.wrap;

private int tagBg;
/* 
 * 字體樣式
 */
private int textApperance;

其中wrap樣式是指:每一個tag包裹內容,match是值:將多餘的空白填補到每一個控件,使得每一行的總長度一樣。

4.主要方法有:

    mTagViewStyle=mTagView.getTagViewStyle();
    mTagViewStyle.setmTagStyle(TagStyle.wrap);
    mTagViewStyle.setTagBg(R.drawable.flag_01);
    mTagViewStyle.setTextApperance(R.drawable.flag_01);
    mTagView.setTags(str);
    mTagView.setItemClick(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            Toast.makeText(getApplicationContext(), v.getTag().toString()+"", Toast.LENGTH_SHORT).show();
           //v.setBackgroundResource(R.drawable.flag_01);

        }
    });

使用

導入包:

詳見我的另外一篇博文:http://blog.csdn.net/u011072613/article/details/53889596

在佈局文件中添加

<com.github.yf_library.tag.TagView
    android:id="@+id/tab_view"
    android:layout_below="@+id/hot_txt"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    >

代碼設置:

樣式設置:

        mTagViewStyle=mTagView.getTagViewStyle();
        mTagViewStyle.setmTagStyle(TagStyle.wrap);

源碼中設置了一個TagEntity實體類,用來裝載數據。

    private String txt;//顯示的tag文字
    private String url;//點擊的鏈接
    private boolean isSelected;//是否被選中
    private Intent mIntent;//點擊跳轉的activity
    private int bGId;//背景id
    private int singleFlag=0;//是否單行

如下所示:

        data_list=new ArrayList<TagEntity>();
        TagEntity tagEntity01=new TagEntity();
        tagEntity01.setTxt("我的我的");
        data_list.add(tagEntity01);

        tagEntity01=new TagEntity();
        tagEntity01.setTxt("我的我捉住");
        data_list.add(tagEntity01);

        tagEntity01=new TagEntity();
        tagEntity01.setTxt("2333333");
        data_list.add(tagEntity01);

然後用傳遞源數據:

mTagView.setTagsData(data_list, new OnTagClickListener() {

            @Override
            public void setTagClick(TagEntity entity) {
                // TODO Auto-generated method stub
                Toast.makeText(getApplicationContext(),       entity.getTxt(), Toast.LENGTH_SHORT).show();

            }
        });

TagView的用處較多,屬於比較常見的控件,需要擴展和自定義的地方也比較多,該TagView基本能滿足用戶的需求,目前match模式任然在擴展中,歡迎大家一起交流。

郵箱:[email protected]

github:https://github.com/commutescript

歡迎交流,歡迎star。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章