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。

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