Android中常見的流式佈局的使用
Android中常見的自定義FlowLayout流式佈局的使用
在日常的app使用中,我們會在Android 的app中看見,比如淘寶購物頁面尺寸的選取,脈脈和慕課技術職位的選取等等熱門標籤自動換行的流式佈局,今天,我們就來看看如何自定義一個類似熱門標籤那樣的流式佈局吧,老規矩,直接上效果圖
概述
1.流式佈局原理:
在佈局內,隨意擺放任意個view,每行所擺放的view個數,根據實施計算出來的寬度,一旦當前要擺放的view寬度和之前擺放的所有view寬度加在一起,超過了佈局的寬度,那麼就把該view換行擺放
2.應用場景:
一般,像這種流式佈局會應用在一些熱門標籤,熱門推薦之類的應用上
3.測量模式:
談到FlowLayout流式佈局,不得不提及他的測量模式:
* MeasureSpec.EXACTLY:精確模式, eg:100dp,match_parent.(明確指出)
* MeasureSpec.AT_MOST: 至多模式, view最多可以獲得的寬高值,它需要計算所有包含的子view的寬高,最後計算出來的寬高總和值,eg:wrap_content.
* UNSPECIFIED:未指定模式,想設定多寬多高,就給你多寬多高,一般的控制元件不會指定這種模式,但也存在,這種模式用的不多。eg:scrollview的寬高測量,就是使用的此種模式
4.在我們的流式佈局內,應該怎麼設定佈局的寬高呢? onMeasure()
1:如果佈局指定的寬是match_parent或者精確的寬度值,那麼直接就可以從父控制元件傳入的測量規格中直接獲取佈局寬度,高度同理.
2:如果佈局指定的寬高不是EXACTLY,而是AT_MOST,那麼這時候,就需要計算每一個子view的寬高,來決定佈局的寬高了。
寬度:擺放的所有子view佔據寬度最多的一行,作為佈局寬度。
高度:擺放的所有子view總共佔據幾行的高度總和。
5.子View的佈局方式: onLayout()
使用onLayout():設定ViewGroup內包含的所有子view的位置;
獲取到每一行的每一個子view,計算出它的left,top,right,bottom,呼叫layout方法設定其在流式佈局當中的位置。
寬度=子view最多的那行的寬度=那一行每一個子view的寬度+leftMargin+rightMargin;
高度=所有行的高度 = 每一行的高度+topMargin+bottomMargin;
LayoutParams引數的設定
ViewGroup LayoutParams :每個 ViewGroup 對應一個 LayoutParams; 即 ViewGroup -> LayoutParams
getLayoutParams 不知道轉為哪個對應的LayoutParams ,其實很簡單,就是如下:
子View.getLayoutParams 得到的LayoutParams對應的就是 子View所在的父控制元件的LayoutParams;
例如,LinearLayout 裡面的子view.getLayoutParams ->LinearLayout.LayoutParams
所以 咱們的FlowLayout 也需要一個LayoutParams,由於上面的效果圖是子View的 margin,
所以應該使用MarginLayoutParams。即FlowLayout->MarginLayoutParams
自定義ViewGroup的實現流式佈局
根據上面的技術分析,自定義類繼承於ViewGroup,並重寫 onMeasure和onLayout等方法。具體實現程式碼如下:
package com.example.administrator.p2pinvest.ui;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
//自定義ViewGroup實現
public class FlowLayout extends ViewGroup {
public FlowLayout(Context context) {
this(context, null);
}
//這個方法必須實現
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//佈局:給每一個子view佈局,childView.layout(l,t,r,b)
private List<Integer> allHeights = new ArrayList<>();//集合中的元素:記錄每一行的高度
private List<List<View>> allViews = new ArrayList<>();//外層集合中的元素:由每行元素構成的集合
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = this.getWidth();//得到父檢視的寬度
int lineWidth = 0;
int lineHeight = 0;
// 一、給集合元素賦值
int childCount = getChildCount();
List<View> lineList = new ArrayList<>();//一行元素構成的集合
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//子檢視的寬高
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
//獲取檢視的邊距
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();
if (lineWidth + childWidth + mp.leftMargin + mp.rightMargin < width) {//不換行
lineList.add(childView);//新增子檢視到集合中
lineWidth += childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + mp.topMargin + mp.bottomMargin);
} else {//換行
allViews.add(lineList);
allHeights.add(lineHeight);
//換行以後需要執行的情況
lineList = new ArrayList<>();
lineList.add(childView);
lineWidth = childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = childHeight + mp.topMargin + mp.bottomMargin;
}
if (i == childCount - 1) {//如果最後一個元素
allViews.add(lineList);
allHeights.add(lineHeight);
}
}
Log.e("TAG", "allViews.size()==" + allViews.size() + "allHeights.size()==" + allHeights.size());
//二、遍歷集合元素,呼叫元素的layout()
int x = 0;
int y = 0;
for (int i = 0; i < allViews.size(); i++) {
List<View> lineViews = allViews.get(i);//獲取每一行的集合
for (int j = 0; j < lineViews.size(); j++) {
View childView = lineViews.get(j);//獲取一行的指定的j位置
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();
//計算的到left,top,right,bottom
int left = x + mp.leftMargin;
int top = y + mp.topMargin;
int right = left + childView.getMeasuredWidth();
int bottom = top + childView.getMeasuredHeight();
childView.layout(left, top, right, bottom);
//重新賦值x,y
x += childView.getMeasuredWidth() + mp.leftMargin + mp.rightMargin;
}
//換行
x = 0;
y += allHeights.get(i);
}
}
//測量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取寬度和高度的佈局的數值,以及各自的設計模式,精確模式,至多模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//聲明當前檢視的寬和高,如果是至多模式,需要計算出此兩個變數的值
int width = 0;
int height = 0;
//宣告每行的寬度和高度
int lineWidth = 0;
int lineHeight = 0;
int childCount = getChildCount();//獲取子檢視的個數
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//為了保證能夠獲取子檢視的測量的寬高,需要調下面的方法
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//獲取子檢視測量的寬高
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
//獲取檢視的邊距
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();
if (lineWidth + childWidth + mp.leftMargin + mp.rightMargin <= widthSize) {//不換行
lineWidth += childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + mp.topMargin + mp.bottomMargin);
} else {//換行
width = Math.max(width, lineWidth);
height += lineHeight;
//重新賦值
lineWidth = childWidth + mp.leftMargin + mp.rightMargin;
lineHeight = childHeight + mp.topMargin + mp.bottomMargin;
}
//單獨的考慮一下最後一個!因為最後一個元素並沒有計算進去
if (i == childCount - 1) {
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
Log.e("TAG", "width ==" + width + ",height==" + height);
Log.e("TAG", "widthSize ==" + widthSize + ",heightSize==" + heightSize);
//呼叫此方法,設定當前佈局的寬高
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : width,
heightMode == MeasureSpec.EXACTLY ? heightSize : height);
}
//FlowLayout中有了如下的方法,在onMeasure()中可通過child就可以getLayoutParams()
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
MarginLayoutParams mp = new MarginLayoutParams(getContext(), attrs);
return mp;
}
}
在佈局檔案中加入自定義的flowLayout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.administrator.p2pinvest.ui.FlowLayout
android:id="@+id/flow_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.administrator.p2pinvest.ui.FlowLayout>
</LinearLayout>
初始化佈局使用的是butterknife
@Bind(R.id.flow_layout)
FlowLayout flowLayout;
提供頁面要顯示的資料,這個資料也可以放在伺服器中進行聯網獲取
private String[] datas = new String[]{"新手計劃", "樂享活系列90天計劃", "錢包", "30天理財計劃(加息2%)",
"林業局投資商業經營與大撈一筆", "中學老師購買車輛", "屌絲下海經商計劃", "新西遊影視拍",
"Java培訓老師自己週轉", "HelloWorld", "C++-C-ObjectC-java", "Android vs ios", "演算法與資料結構", "JNI與NDK", "team working"};
//初始化隨機
private Random random;
在其他類中直接進行呼叫即可
@Override
public void initData(String content) {
random = new Random();
for(int i = 0; i < datas.length; i++) {
final TextView textView = new TextView(getActivity());
textView.setText(datas[i]);
//提供邊距的物件,並設定到textView中
ViewGroup.MarginLayoutParams mp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
mp.leftMargin = UIUtils.dp2px(8);
mp.rightMargin = UIUtils.dp2px(8);
mp.topMargin = UIUtils.dp2px(8);
mp.bottomMargin = UIUtils.dp2px(8);
textView.setLayoutParams(mp);
//設定背景
//設定textView的背景
int red = random.nextInt(211);
int green = random.nextInt(211);
int blue = random.nextInt(211);
//方式一:
// textView.setBackground(DrawUtils.getDrawable(Color.rgb(red, green, blue),UIUtils.dp2px(5)));
//方式二:
//儲存按下能顯示selector的效果,需要設定一個如下的屬性
textView.setBackground(DrawUtils.getSelector(DrawUtils.getDrawable(Color.rgb(red, green, blue),UIUtils.dp2px(5)),DrawUtils.getDrawable(Color.WHITE,UIUtils.dp2px(5))));
//方式一:
// textView.setClickable(true);
//新增點選事件,也是實現顯示selector的效果的一種方式
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ProductHotFragment.this.getActivity(), textView.getText(), Toast.LENGTH_SHORT).show();
}
});
//設定邊距
//設定內邊距
int padding = UIUtils.dp2px(10);
textView.setPadding(padding, padding, padding, padding);
// 2.新增到FlowLayout佈局中
flowLayout.addView(textView);
}
}
用到的工具類
public class DrawUtils {
//提供一個指定顏色和圓角半徑的Drawable物件
public static GradientDrawable getDrawable(int rgb,float radius){
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(rgb);//設定顏色
gradientDrawable.setGradientType(GradientDrawable.RECTANGLE);//設定顯示的樣式
gradientDrawable.setCornerRadius(radius);//設定圓角的半徑
gradientDrawable.setStroke(UIUtils.dp2px(1),rgb);//描邊
return gradientDrawable;
}
public static StateListDrawable getSelector(Drawable normalDrawable,Drawable pressDrawable) {
StateListDrawable stateListDrawable = new StateListDrawable();
//給當前的顏色選擇器新增選中圖片指向狀態,未選中圖片指向狀態
stateListDrawable.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, pressDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_enabled}, normalDrawable);
//設定預設狀態
stateListDrawable.addState(new int[]{}, normalDrawable);
return stateListDrawable;
}
}
相關推薦
Android 自定義流式佈局(快速實現)
首先先寫一個自定義的類繼承viewgroup,程式碼如下 package com.demo.com.jd_zhang.ui.customview; import android.content.Context; import android.util.AttributeS
Android 實現FlowLayout流式佈局(類似熱門標籤)
今天跟大家分享一下FlowLayout,最近專案中有遇到熱門標籤這個樣的佈局(文章末尾可下載原始碼),如下圖: 一,建立FlowLayout並繼承ViewGroup FlowLayout 類主要實現onMeasure,onLayout和generateL
安卓中使用流式佈局實現標籤
我們在開發的時候通常需要加標籤,對於這個標籤怎麼說呢,反正也挺複雜的,最初開發這個標籤的時候還是沒有思路的,後來在github上面查找了一下資料,瞭解了通過流式佈局來實現這個標籤,我記得開始的時候我寫標籤的時候是三個TextView一個一個新增進去的,後來感覺還是不太好,所
android自定義流式佈局解析與原始碼
今天給大家解析一下自定義流式佈局的編寫,以及分析一下寫程式碼過程遇到的難點。該佈局支援水平垂直方向和子view gravity選擇,先看一下執行的效果,左邊是垂直佈局,右邊是水平佈局,套一個scrollview就支援滑動了 說一下遇
解決:Android中常見的熱門標籤的流式佈局flowlayout不能wrap_content
最近在專案中藥使用流式佈局,但是在網上找的都不能滿足要求,這篇部落格內容只支援match_parent,我改後的程式碼可以支援wrap_content,原文也僅僅是少加一行高度而已。。新部落格希望大家多多評論。。原文連結 一:概述: 1.流式佈局的特點以
Android中常見的流式佈局的使用
Android中常見的自定義FlowLayout流式佈局的使用 在日常的app使用中,我們會在Android 的app中看見,比如淘寶購物頁面尺寸的選取,脈脈和慕課技術職位的選取等等熱門標籤自動換行的流式佈局,今天,我們就來看看如何自定義一個類似熱門標籤
Android中的封裝流式佈局FlowLayout
鴻洋的GitHub:https://github.com/hongyangAndroid/FlowLayout 第一步:加依賴 implementation 'com.hyman:flowlayout-lib:1.1.2' 第二步:建立一個Adapter繼承TagAdapter pu
Android 自定義View-----流式佈局(粗糙實現)
//首先檢視一下佈局介面吧 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app
Android : 自定義View之流式佈局
寫了一個很簡單的佈局 這是周圍圓框的drawable <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android">
Android開發流式佈局關聯資料庫
效果如下 自定義View組合控制元件header_View <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res
Android-超簡單的流式佈局
流式佈局,一般在商城類的專案中用到會非常多比如 淘寶中,購物選擇商品列表的時候,這個就是流式佈局 創作起來也很簡單, 只要你計算出寬度,和高度,如果超出螢幕寬度,則換行擺放即可 然後我就嘗試著寫了一下,果然還是可以的 效果圖 核心方法主要是
Java中GUI簡介、AWT概述、以及佈局管理器(流式佈局管理器、邊界佈局管理器、網格佈局管理器、網格包佈局管理器、卡片佈局管理器)
1 GUI簡介 GUI的全稱是Graphical User Interface,即圖形使用者介面。顧名思義,就是應用程式提供給使用者操作的圖形介面,包括視窗、選單、按鈕、工具欄和其他各種使用者介面元素。Java中針對GUI設計提供了豐富的類庫,這些類分別位
Android 實現一個簡易橫向流式佈局
SimpleFlowLayout:一個簡易的橫向流式佈局,只實現核心功能,使用者可自行擴充套件 Demo圖片如下所示: SimpleFlowLayout直接繼承自ViewGroup,主要負責
android自定義View實現流式佈局
//先來一張效果圖 //自定義的控制元件 import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.
[Android]FlowLayout:流式佈局的應用
一、應用 流式佈局即控制元件根據ViewGroup的寬,自動的往右新增,如果當前行剩餘空間不足,則自動新增到下一行。經常應用於搜尋歷史以及熱搜等介面。 二、實現 1.FlowLayout.java 只要是重寫onMeasure和onLayout兩個函式。 onMeasu
Android學習之RecyclerView學習(實現瀑布流式佈局)
RecyclerView,大家可以通過匯入support-v7對其進行使用。 如果使用AndroidStudio開發, 需要在build.gradle中新增: compile 'com.android.support:appcompat-v7:24.2.1' com
android流式佈局、待辦事項應用、貝塞爾曲線、MVP+Rxjava+Retrofit、藝術圖片應用等原始碼
Android精選原始碼 android模仿淘寶首頁效果原始碼 一款藝術圖片應用,採用T-MVVM打造 Android MVP + RxJava + Retrofit專案 android流式佈局實現熱門標籤效果 android仿淘寶客戶端商品詳
Android中常見佈局
android中常見佈局 [1]線性佈局 水平 垂直. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/andro
Android開發之RecyclerView實現流式佈局
RecyclerView是什麼? RecycleView的出現, 替代了ListView, 沒了OnitemClickListener,; LayoutManager負責計算佈局; Adapter 負責適配,還增加了ViewHolder;RecycleView
Android 流式佈局FlowLayout 實現關鍵字標籤
1.介紹 流式佈局的應用還是很廣泛的,比如搜尋熱詞、關鍵詞標籤等,GitHub上已經有很多這樣的佈局了,但是還是想著自己實現一下,最近一直在學自定義控制元件,也鞏固一下所學的知識。 本文實現的效果如下圖所示: 2.思路 繼承自RelativeL