YFAndroidLibs之TagView的用法及源码解析
关于(About)
TagView的实现思路相对简单,主要是继承ViewGroup,重新相应的onLayout和onMeasure方法:
效果图如下:
设计思路及主要接口(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模式任然在扩展中,欢迎大家一起交流。
邮箱:yhcommute@outlook.com
github:https://github.com/commutescript
欢迎交流,欢迎star。