Android自定義ViewGroup實現(xiàn)標簽浮動效果
前面在學習鴻洋大神的一些自定義的View文章,看到了自定義ViewGroup實現(xiàn)浮動標簽,初步看了下他的思路以及結(jié)合自己的思路完成了自己的浮動標簽的自定義ViewGroup。目前實現(xiàn)的可以動態(tài)添加標簽、可點擊。效果圖如下:
1、思路
首先在onMeasure方法中測量ViewGroup的寬和高,重點是處理當我們自定義的ViewGroup設(shè)置為wrap_content的情況下,如何去測量其大小的問題。當我們自定義的ViewGroup設(shè)置為wrap_content時,我們需要讓子View先去測量自己,當子View測量完后,再通過子View的getMeasuredWidth和getMeasureHeight方法獲得其子View的寬和高。每次在測量一個子View之前,都需要判斷如果加入該子View,當前行是否能夠容納下該子View,如果不能,則需要新開一行,并記錄下當前行的最大高度。
在onLayout方法中,核心人物是給每個子View擺放位置,也就是為該ViewGroup中每個子View找到盒子模型上面的兩個點也就是左上角和右下角,即點(l,t)和點(r,b),確定了兩個點,子View的位置也就確定了。
2、實現(xiàn)
基本思路有了就可以嘗試實現(xiàn)了,代碼如下:
自定義的ViewGroup:
/** * 流式標簽(動態(tài)的,根據(jù)傳入的數(shù)據(jù)動態(tài)添加標簽) */ public class DynamicTagFlowLayout extends ViewGroup { private List<String> mTags = new ArrayList<String>(); public DynamicTagFlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public DynamicTagFlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public DynamicTagFlowLayout(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //當前ViewGroup的總高度 int totalHeight= 0; //所有行中的最大寬度 int maxLineWidth = 0; //當前行的最大高度 int lineMaxHeight = 0; //當前行的總寬度 int currentLineWidth = 0; //每個childView所占用的寬度 int childViewWidthSpace = 0; //每個childView所占用的高度 int childViewHeightSpace = 0; int count = getChildCount(); MarginLayoutParams layoutParams; for(int i = 0; i < count; i++){ View child = getChildAt(i); if(child.getVisibility() != View.GONE){//只有當這個View能夠顯示的時候才去測量 //測量每個子View,以獲取子View的寬和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); layoutParams = (MarginLayoutParams) child.getLayoutParams(); childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; if(currentLineWidth + childViewWidthSpace > widthSize){//表示如果當前行再加上現(xiàn)在這個子View,就會超出總的規(guī)定寬度,需要另起一行 totalHeight += lineMaxHeight; if(maxLineWidth < currentLineWidth){//如果行的最長寬度發(fā)生了變化,更新保存的最長寬度 maxLineWidth = currentLineWidth; } currentLineWidth = childViewWidthSpace;//另起一行后,需要重置當前行寬 lineMaxHeight = childViewHeightSpace; }else{//表示當前行可以繼續(xù)添加子元素 currentLineWidth += childViewWidthSpace; if(lineMaxHeight < childViewHeightSpace){ lineMaxHeight = childViewHeightSpace; } } } } setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxLineWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //當前是第幾行 int currentLine = 1; //存放每一行的最大高度 List<Integer> lineMaxHeightList = new ArrayList<Integer>(); //每個childView所占用的寬度 int childViewWidthSpace = 0; //每個childView所占用的高度 int childViewHeightSpace = 0; //當前行的最大高度 int lineMaxHeight = 0; //當前行的總寬度 int currentLineWidth = 0; int count = getChildCount(); MarginLayoutParams layoutParams; for(int i = 0; i < count; i++){ int cl= 0, ct = 0, cr = 0, cb = 0; View child = getChildAt(i); if(child.getVisibility() != View.GONE){//只有當這個View能夠顯示的時候才去測量 layoutParams = (MarginLayoutParams) child.getLayoutParams(); childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; System.out.println("getWidth()---->"+getWidth()); if(currentLineWidth + childViewWidthSpace > getWidth()){//表示如果當前行再加上現(xiàn)在這個子View,就會超出總的規(guī)定寬度,需要另起一行 lineMaxHeightList.add(lineMaxHeight);//此時先將這一行的最大高度加入到集合中 //新的一行,重置一些參數(shù) currentLine++; currentLineWidth = childViewWidthSpace; lineMaxHeight = childViewHeightSpace; cl = layoutParams.leftMargin; if(currentLine > 1){ for(int j = 0; j < currentLine - 1; j++){ ct += lineMaxHeightList.get(j); } ct += layoutParams.topMargin ; }else{ ct = layoutParams.topMargin; } }else{//表示當前行可以繼續(xù)添加子元素 cl = currentLineWidth + layoutParams.leftMargin; if(currentLine > 1){ for(int j = 0; j < currentLine - 1; j++){ ct += lineMaxHeightList.get(j); } ct += layoutParams.topMargin; }else{ ct = layoutParams.topMargin; } currentLineWidth += childViewWidthSpace; if(lineMaxHeight < childViewHeightSpace){ lineMaxHeight = childViewHeightSpace; } } cr = cl + child.getMeasuredWidth(); cb = ct + child.getMeasuredHeight(); child.layout(cl, ct, cr, cb); } } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } public void setTags(List<String> tags){ if(tags!= null){ mTags.clear(); mTags.addAll(tags); for(int i = 0; i < mTags.size(); i++){ TextView tv = new TextView(getContext()); MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); lp.setMargins(15, 15, 15, 15); // lp.width = MarginLayoutParams.WRAP_CONTENT; // lp.height = MarginLayoutParams.WRAP_CONTENT; tv.setLayoutParams(lp); tv.setBackgroundResource(R.drawable.tv_bg); /* * setPadding一定要在setBackgroundResource后面使用才有效?。?! * http://stackoverflow.com/questions/18327498/setting-padding-for-textview-not-working */ tv.setPadding(15, 15, 15, 15); tv.setTextColor(Color.WHITE); tv.setText(mTags.get(i)); tv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(listener != null){ listener.onClick(v); } } }); addView(tv); } requestLayout(); } } private OnTagItemClickListener listener; public interface OnTagItemClickListener{ public void onClick(View v); } public void setOnTagItemClickListener(OnTagItemClickListener l){ listener = l; } }
MainActivity:
public class MainActivity extends Activity { private DynamicTagFlowLayout dynamicTagFlowLayout; List<String> tags = new ArrayList<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dynamic_tagflowlayout); dynamicTagFlowLayout = (DynamicTagFlowLayout) findViewById(R.id.dynamic_tag); dynamicTagFlowLayout.setOnTagItemClickListener(new OnTagItemClickListener() { @Override public void onClick(View v) { TextView tv = (TextView) v; Toast.makeText(MainActivity.this, tv.getText().toString(), Toast.LENGTH_SHORT).show(); } }); initData(); dynamicTagFlowLayout.setTags(tags); } private void initData() { tags.add("陽哥你好!"); tags.add("Android開發(fā)"); tags.add("新聞熱點"); tags.add("熱水進宿舍啦!"); tags.add("I love you"); tags.add("成都妹子"); tags.add("新余妹子"); tags.add("仙女湖"); tags.add("創(chuàng)新工廠"); tags.add("孵化園"); tags.add("神州100發(fā)射"); tags.add("有毒疫苗"); tags.add("頂你陽哥陽哥"); tags.add("Hello World"); tags.add("閑逛的螞蟻"); tags.add("閑逛的螞蟻"); tags.add("閑逛的螞蟻"); tags.add("閑逛的螞蟻"); tags.add("閑逛的螞蟻"); tags.add("閑逛的螞蟻"); } }
源碼下載:Android流式標簽可動態(tài)添加FlowLayout
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- Android自定義ViewGroup實現(xiàn)帶箭頭的圓角矩形菜單
- Android自定義ViewGroup實現(xiàn)堆疊頭像的點贊Layout
- Android自定義ViewGroup之實現(xiàn)FlowLayout流式布局
- Android App開發(fā)中自定義View和ViewGroup的實例教程
- 一篇文章弄懂Android自定義viewgroup的相關(guān)難點
- Android應(yīng)用開發(fā)中自定義ViewGroup的究極攻略
- Android自定義ViewGroup實現(xiàn)受邊界限制的滾動操作(3)
- Android動畫效果之自定義ViewGroup添加布局動畫(五)
- Android自定義ViewGroup的實現(xiàn)方法
- Android自定義ViewGroup實現(xiàn)朋友圈九宮格控件
相關(guān)文章
android開發(fā)教程之實現(xiàn)toast工具類
這篇文章主要介紹了android開發(fā)中需要的toast工具類,需要的朋友可以參考下2014-05-05Android 通過cmake的方式接入opencv的方法步驟
這篇文章主要介紹了Android 通過cmake的方式接入opencv的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04Android開發(fā)之判斷有無虛擬按鍵(導(dǎo)航欄)的實例
下面小編就為大家分享一篇Android開發(fā)之判斷有無虛擬按鍵(導(dǎo)航欄)的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01Android Studio / IDEA kotlin 顯示 var 真實類型操作
這篇文章主要介紹了Android Studio / IDEA kotlin 顯示 var 真實類型操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08