Android實(shí)現(xiàn)熱門標(biāo)簽的流式布局
一、概述:
在日常的app使用中,我們會(huì)在android 的app中看見 熱門標(biāo)簽等自動(dòng)換行的流式布局,今天,我們就來看看如何
自定義一個(gè)類似熱門標(biāo)簽?zāi)菢拥牧魇讲季职桑ㄔ创a下載在下面最后給出)
類似的自定義布局。下面我們就來詳細(xì)介紹流式布局的應(yīng)用特點(diǎn)以及用的的技術(shù)點(diǎn):
1.流式布局的特點(diǎn)以及應(yīng)用場景
特點(diǎn):當(dāng)上面一行的空間不夠容納新的TextView時(shí)候,
才開辟下一行的空間
原理圖:
場景:主要用于關(guān)鍵詞搜索或者熱門標(biāo)簽等場景
2.自定義ViewGroup,重點(diǎn)重寫下面兩個(gè)方法
1)、onMeasure:測量子view的寬高,設(shè)置自己的寬和高
2)、onLayout:設(shè)置子view的位置
onMeasure:根據(jù)子view的布局文件中屬性,來為子view設(shè)置測量模式和測量值
測量=測量模式+測量值;
測量模式有3種:
EXACTLY:表示設(shè)置了精確的值,一般當(dāng)childView設(shè)置其寬、高為精確值、match_parent時(shí),ViewGroup會(huì)將其設(shè)置為EXACTLY;
AT_MOST:表示子布局被限制在一個(gè)最大值內(nèi),一般當(dāng)childView設(shè)置其寬、高為wrap_content時(shí),ViewGroup會(huì)將其設(shè)置為AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出現(xiàn)在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此種模式比較少見。
3.LayoutParams
ViewGroup LayoutParams :每個(gè) ViewGroup 對應(yīng)一個(gè) LayoutParams; 即 ViewGroup -> LayoutParams
getLayoutParams 不知道轉(zhuǎn)為哪個(gè)對應(yīng)的LayoutParams ,其實(shí)很簡單,就是如下:
子View.getLayoutParams 得到的LayoutParams對應(yīng)的就是 子View所在的父控件的LayoutParams;
例如,LinearLayout 里面的子view.getLayoutParams ->LinearLayout.LayoutParams
所以 咱們的FlowLayout 也需要一個(gè)LayoutParams,由于上面的效果圖是子View的 margin,
所以應(yīng)該使用MarginLayoutParams。即FlowLayout->MarginLayoutParams
4.最后來看看實(shí)現(xiàn)的最終效果圖:
二、熱門標(biāo)簽的流式布局的實(shí)現(xiàn):
1. 自定義熱門標(biāo)簽的ViewGroup實(shí)現(xiàn)
根據(jù)上面的技術(shù)分析,自定義類繼承于ViewGroup,并重寫 onMeasure和onLayout等方法。具體實(shí)現(xiàn)代碼如下:
<font color="#362e2b"><font style="background-color:rgb(255, 255, 255)"><font face="Arial"><font style="font-size:14px">package com.czm.flowlayout; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * * @author caizhiming * @created on 2015-4-13 */ public class XCFlowLayout extends ViewGroup{ //存儲所有子View private List<List<View>> mAllChildViews = new ArrayList<>(); //每一行的高度 private List<Integer> mLineHeight = new ArrayList<>(); public XCFlowLayout(Context context) { this(context, null); // TODO Auto-generated constructor stub } public XCFlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); // TODO Auto-generated constructor stub } public XCFlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub //父控件傳進(jìn)來的寬度和高度以及對應(yīng)的測量模式 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); //如果當(dāng)前ViewGroup的寬高為wrap_content的情況 int width = 0;//自己測量的 寬度 int height = 0;//自己測量的高度 //記錄每一行的寬度和高度 int lineWidth = 0; int lineHeight = 0; //獲取子view的個(gè)數(shù) int childCount = getChildCount(); for(int i = 0;i < childCount; i ++){ View child = getChildAt(i); //測量子View的寬和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); //得到LayoutParams MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); //子View占據(jù)的寬度 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; //子View占據(jù)的高度 int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; //換行時(shí)候 if(lineWidth + childWidth > sizeWidth){ //對比得到最大的寬度 width = Math.max(width, lineWidth); //重置lineWidth lineWidth = childWidth; //記錄行高 height += lineHeight; lineHeight = childHeight; }else{//不換行情況 //疊加行寬 lineWidth += childWidth; //得到最大行高 lineHeight = Math.max(lineHeight, childHeight); } //處理最后一個(gè)子View的情況 if(i == childCount -1){ width = Math.max(width, lineWidth); height += lineHeight; } } //wrap_content setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width, modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub mAllChildViews.clear(); mLineHeight.clear(); //獲取當(dāng)前ViewGroup的寬度 int width = getWidth(); int lineWidth = 0; int lineHeight = 0; //記錄當(dāng)前行的view List<View> lineViews = new ArrayList<View>(); int childCount = getChildCount(); for(int i = 0;i < childCount; i ++){ View child = getChildAt(i); MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); //如果需要換行 if(childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width){ //記錄LineHeight mLineHeight.add(lineHeight); //記錄當(dāng)前行的Views mAllChildViews.add(lineViews); //重置行的寬高 lineWidth = 0; lineHeight = childHeight + lp.topMargin + lp.bottomMargin; //重置view的集合 lineViews = new ArrayList(); } lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } //處理最后一行 mLineHeight.add(lineHeight); mAllChildViews.add(lineViews); //設(shè)置子View的位置 int left = 0; int top = 0; //獲取行數(shù) int lineCount = mAllChildViews.size(); for(int i = 0; i < lineCount; i ++){ //當(dāng)前行的views和高度 lineViews = mAllChildViews.get(i); lineHeight = mLineHeight.get(i); for(int j = 0; j < lineViews.size(); j ++){ View child = lineViews.get(j); //判斷是否顯示 if(child.getVisibility() == View.GONE){ continue; } MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int cLeft = left + lp.leftMargin; int cTop = top + lp.topMargin; int cRight = cLeft + child.getMeasuredWidth(); int cBottom = cTop + child.getMeasuredHeight(); //進(jìn)行子View進(jìn)行布局 child.layout(cLeft, cTop, cRight, cBottom); left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; } left = 0; top += lineHeight; } } /** * 與當(dāng)前ViewGroup對應(yīng)的LayoutParams */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { // TODO Auto-generated method stub return new MarginLayoutParams(getContext(), attrs); } }</font></font></font></font>
2.相關(guān)的布局文件:
引用自定義控件:
<font color="#362e2b"><font style="background-color:rgb(255, 255, 255)"><font face="Arial"><font style="font-size:14px"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" > <com.czm.flowlayout.XCFlowLayout android:id="@+id/flowlayout" android:layout_width="match_parent" android:layout_height="match_parent" > </com.czm.flowlayout.XCFlowLayout> </RelativeLayout></font></font></font></font>
TextView的樣式文件:
<font color="#362e2b"><font style="background-color:rgb(255, 255, 255)"><font face="Arial"><font style="font-size:14px"><?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#666666" /> <corners android:radius="10dp" /> <padding android:left="5dp" android:right="5dp" android:top="5dp" android:bottom="5dp" /> </shape></font></font></font></font>
三、使用該自定義布局控件類
最后,如何使用該自定義的熱門標(biāo)簽控件類呢?很簡單,請看下面實(shí)例代碼:
<font color="#362e2b"><font style="background-color:rgb(255, 255, 255)"><font face="Arial"><font style="font-size:14px">package com.czm.flowlayout; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.widget.TextView; /** * * @author caizhiming * @created on 2015-4-13 */ public class MainActivity extends Activity { private String mNames[] = { "welcome","android","TextView", "apple","jamy","kobe bryant", "jordan","layout","viewgroup", "margin","padding","text", "name","type","search","logcat" }; private XCFlowLayout mFlowLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initChildViews(); } private void initChildViews() { // TODO Auto-generated method stub mFlowLayout = (XCFlowLayout) findViewById(R.id.flowlayout); MarginLayoutParams lp = new MarginLayoutParams( LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); lp.leftMargin = 5; lp.rightMargin = 5; lp.topMargin = 5; lp.bottomMargin = 5; for(int i = 0; i < mNames.length; i ++){ TextView view = new TextView(this); view.setText(mNames[i]); view.setTextColor(Color.WHITE); view.setBackgroundDrawable(getResources().getDrawable(R.drawable.textview_bg)); mFlowLayout.addView(view,lp); } } }</font></font></font></font>
以上就是本文的全部內(nèi)容,下面在給大家一個(gè)小福利:
// 流式布局 話不多說,比較簡單,注釋都寫的很清楚 import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * * @author Mr.Himan * @version 1.0<br> * 2015年11月4日 11:12:06 <br> * 流式布局 設(shè)置MarginTop 和MarginLeft有效 MarginRight 暫未實(shí)現(xiàn) */ public class FlowLayout extends ViewGroup { /** * 存儲所有的子View */ private List<List<View>> mAllChildViews = new ArrayList<List<View>>(); /** * 存儲每一行的高度 */ private List<Integer> mLineHeight = new ArrayList<Integer>(); public FlowLayout(Context context) { this(context, null); } public FlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mAllChildViews.clear(); mLineHeight.clear(); // 獲取當(dāng)前ViewGroup的寬度 int width = getWidth(); int lineWidth = 0; int lineHeight = 0; // 記錄當(dāng)前行的view List<View> lineViews = new ArrayList<View>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // 如果需要換行 if (childWidth + lineWidth + lp.leftMargin + lp.rightMargin > width) { // 記錄LineHeight mLineHeight.add(lineHeight); // 記錄當(dāng)前行的Views mAllChildViews.add(lineViews); // 重置行的寬高 lineWidth = 0; lineHeight = childHeight + lp.topMargin + lp.bottomMargin; // 重置view的集合 lineViews = new ArrayList(); } lineWidth += childWidth + lp.leftMargin + lp.rightMargin; lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin); lineViews.add(child); } // 處理最后一行 mLineHeight.add(lineHeight); mAllChildViews.add(lineViews); MarginLayoutParams params = (MarginLayoutParams) this.getLayoutParams(); // 設(shè)置子View的位置 int left = 0; // 添加marginTop int top = 0 + params.topMargin; // 獲取行數(shù) int lineCount = mAllChildViews.size(); for (int i = 0; i < lineCount; i++) { // 當(dāng)前行的views和高度 lineViews = mAllChildViews.get(i); lineHeight = mLineHeight.get(i); for (int j = 0; j < lineViews.size(); j++) { // 為每一列設(shè)置marginLeft if (j == 0) { left = 0 + params.leftMargin; } View child = lineViews.get(j); // 判斷是否顯示 if (child.getVisibility() == View.GONE) { continue; } MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); int cLeft = left + lp.leftMargin; int cTop = top + lp.topMargin; int cRight = cLeft + child.getMeasuredWidth(); int cBottom = cTop + child.getMeasuredHeight(); // 進(jìn)行子View進(jìn)行布局 child.layout(cLeft, cTop, cRight, cBottom); left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; } left = 0; top += lineHeight; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 父控件傳進(jìn)來的寬度和高度以及對應(yīng)的測量模式 int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int modeWidth = MeasureSpec.getMode(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); int modeHeight = MeasureSpec.getMode(heightMeasureSpec); // 如果當(dāng)前ViewGroup的寬高為wrap_content的情況 int width = 0;// 自己測量的 寬度 int height = 0;// 自己測量的高度 // 記錄每一行的寬度和高度 int lineWidth = 0; int lineHeight = 0; // 獲取子view的個(gè)數(shù) int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); // 測量子View的寬和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); // 得到LayoutParams MarginLayoutParams params = (MarginLayoutParams) child .getLayoutParams(); // 子View占據(jù)的寬度 int childWidth = child.getMeasuredWidth() + params.leftMargin + params.rightMargin; // 子View占據(jù)的高度 int childHeight = child.getMeasuredHeight() + params.bottomMargin + params.topMargin; // 換行時(shí)候 if (lineWidth + childWidth > sizeWidth) { // 對比得到最大的寬度 width = Math.max(width, lineWidth); // 重置lineWidth lineWidth = childWidth; // 記錄行高 height += lineHeight; lineHeight = childHeight; } else { // 不換行情況 // 疊加行寬 lineWidth += childWidth; // 得到最大行高 lineHeight = Math.max(lineHeight, childHeight); } // 處理最后一個(gè)子View的情況 if (i == childCount - 1) { width = Math.max(width, lineWidth); height += lineHeight; } } setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width, modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height); } /** * 與當(dāng)前ViewGroup對應(yīng)的LayoutParams */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } }
希望本文所述對大家學(xué)習(xí)Android實(shí)現(xiàn)熱門標(biāo)簽的流式布局有所幫助。
- Android流式布局FlowLayout詳解
- Android流式布局實(shí)現(xiàn)歷史搜索記錄功能
- Java Swing組件布局管理器之FlowLayout(流式布局)入門教程
- Android簡單實(shí)現(xiàn)自定義流式布局的方法
- Android自定義ViewGroup之實(shí)現(xiàn)FlowLayout流式布局
- Android 簡單實(shí)現(xiàn)一個(gè)流式布局的示例
- Android自定義流式布局/自動(dòng)換行布局實(shí)例
- python GUI框架pyqt5 對圖片進(jìn)行流式布局的方法(瀑布流flowlayout)
- android流式布局onLayout()方法詳解
- Flexbox+ReclyclerView實(shí)現(xiàn)流式布局
相關(guān)文章
Android存儲字符串?dāng)?shù)據(jù)到txt文件
這篇文章主要為大家詳細(xì)介紹了Android存儲字符串?dāng)?shù)據(jù)到txt文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10Android 中FloatingActionButton(懸浮按鈕)實(shí)例詳解
這篇文章主要介紹了Android 中FloatingActionButton(懸浮按鈕)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05Android 關(guān)機(jī)彈出選擇菜單的深入解析
本篇文章是對Android 關(guān)機(jī)彈出選擇菜單進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06Android startActivityForResult的基本用法詳解
這篇文章主要介紹了Android startActivityForResult的基本用法詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08Android ViewPager實(shí)現(xiàn)Banner循環(huán)播放
這篇文章主要為大家詳細(xì)介紹了Android ViewPager實(shí)現(xiàn)Banner循環(huán)播放,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android Activity之間的數(shù)據(jù)傳遞方法總結(jié)
這篇文章主要給大家總結(jié)介紹了關(guān)于Android Activity之間的數(shù)據(jù)傳遞方法,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06