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模式任然在擴展中,歡迎大家一起交流。
github:https://github.com/commutescript
歡迎交流,歡迎star。