安卓流式佈局(可換行的標籤)
最近,公司的專案中需要展示商品的規格和屬性,但是不同的商品屬性個數也是不一樣的,
怎麼能夠讓超過一行的屬性自動換行呢?這就需要用到我們的流式佈局,下面先看看效果圖
這篇文章更改的,流式是怎麼實現的還是請先看完上邊這篇文章.
在將樓主的原始碼下載下來使用的時候遇到以下幾個問題,本文將圍繞這幾個小問題進行講解
首先樓主的這個自定義控制元件始終預設鋪滿螢幕的,但是感覺很奇怪,因為在onMeasure這個方法中
已經根據控制元件設定的模式測量模式進行過計算了,按道理說不應該是鋪滿螢幕的!
而且我設定的高度是wrap_content(自適應) 打了斷點試的時候 發現測量出來的距離並不是鋪滿螢幕,
而是真是高度 又認認真真的看了一下測量發方法 發現樓主將super.onMeasure(widthMeasureSpec, heightMeasureSpec);放在了
方法結束的位置 是不是很扯 這句話放在最後的話,就相當於我測量了半天,測量到最後不使用這個
測量值,而使用其父類(ViewGroup)的測量結果,也就是預設結果
解決方案:把這句話移到方法首句或者直接刪除這句話
接著 確實是自適應了 但是隻是單純的換行不行 我們需要點選之後知道我們選中了那個 並且選中的這個背景顏色需要變
簡單地分析下,我們需要做以下幾件事:
1.兩個自定義屬性 分別是選中和未選中的背景顏色
2.獲取所有控制元件的的位置
3.判斷點選的點是不是包含在某個子控制元件中
4,如果是包含在某個子控制元件中,設定回撥
下面我們具體去完成我們這幾個步驟:
1.兩個自定義屬性
先在values下建立一個attrs的xml檔案 分別代表選中和未選中的兩個狀態
接著 建立兩個shape 分別代表選中和未選中時的背景顏色狀態<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="flowlayout"> <attr name="back_selected" format="reference" /> <attr name="back_un_selected" format="reference" /> </declare-styleable> </resources>
選中狀態
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="1dp" />
<stroke
android:width="2dp"
android:color="#ff6600" />
<solid android:color="#ffffff" />
</shape>
未選中狀態<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="1dp" />
<stroke
android:width="2dp"
android:color="#000000" />
<solid android:color="#ffffff" />
</shape>
接著在自定義控制元件的構造方法中獲取這兩個自定義屬性:
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
childLocationList = new HashMap<Integer, Rect>();
// 獲取自定義屬性的值
TypedArray typedArray = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.flowlayout, defStyle, 0);
int back_selected = typedArray.getResourceId(
R.styleable.flowlayout_back_selected, 0);// 選中時的背景資源id
int back_unselected = typedArray.getResourceId(
R.styleable.flowlayout_back_un_selected, 0);// 未選中時的背景資源id
typedArray.recycle();
}
最後在 佈局檔案中為這兩個屬性賦值 注意名稱空間<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:flayout="http://schemas.android.com/apk/res/com.czm.flowlayout"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.czm.flowlayout.FlowLayout
android:id="@+id/flowlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
flayout:back_selected="@drawable/shape_label_selected"
flayout:back_un_selected="@drawable/shape_label_unselected" >
</com.czm.flowlayout.FlowLayout>
</RelativeLayout>
2.獲得所有控制元件的位置
獲得子控制元件的位置 應該在我們指定了子控制元件的位置之後再去獲取 也就是說在onLayout方法的最後
我們新增一個方法 getAllChildViewLocation
我們先分析一下思路 :樓主當時是這樣儲存所有的子控制元件的
<span style="font-size:14px;"> // 儲存所有子View
private List<List<View>> mAllChildViews = new ArrayList<List<View>>();</span>
先將每一行的控制元件存在一個List集合中 再將所有的行List再存到List集合中
這樣 我們就可以根據行數獲取到指定行中所有的控制元件 再獲取指定行指定第幾個的控制元件
我們能拿到這個具體的控制元件 就能拿到控制元件所在的位置
將拿到的位置存到一個鍵值對型別的集合中 鍵表示在這個流式佈局中第幾個 值是對應控制元件的位置
至於為什麼要存在一個鍵值對型別的集合中,等會再說
接著怎麼去存
看了這個圖,應該知道鍵怎麼存了
值是獲取了子控制元件所在的矩形,因為矩形有個方法contains(int x, int y) 可以判斷一個點是否包含在這個矩形中
建立這個集合最好是在構造方法中建立
private HashMap<Integer, Rect> childLocationList = new HashMap<Integer, Rect>();
獲取並記錄子控制元件的位置記錄 /**
* 獲取所有的子控制元件的位置並記錄
*/
private void getAllChildViewLocation() {
int countBefore = 0;
for (int i = 0; i < mAllChildViews.size(); i++) {
if (i > 0) {
countBefore += mAllChildViews.get(i - 1).size();
}
for (int j = 0; j < mAllChildViews.get(i).size(); j++) {
View view = mAllChildViews.get(i).get(j);
Rect rect = new Rect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom());
childLocationList.put(countBefore + j, rect);
}
}
}
3.判斷點選的是哪個
現在有控制元件的位置了 我們只要能知道點的位置就夠了 下面重寫onTouchEvent方法
/**
* 這裡我們需要判斷子控制元件是否被點選 點選的是哪個子控制元件
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
downY = (int) event.getY();
downTime = SystemClock.currentThreadTimeMillis();
break;
case MotionEvent.ACTION_UP:
long upTime = SystemClock.currentThreadTimeMillis();
if (upTime - downTime <= 50) {// 如果手指按下和手指離開鍵盤的時間小於50毫秒有效
for (int i = 0; i < childLocationList.size(); i++) {
if (childLocationList.get(i).contains(downX, downY)) {// 如果子控制元件所在位置(矩形)包括這個點
if (onLabelSelectedListener != null) {
onLabelSelectedListener.onSelected(i);//這個i是什麼 就是子控制元件是第幾個 直接把這個傳過去 現在知<span style="white-space:pre"> </span>//道這個集合的鍵做什麼用了吧
}
// 將記錄的上一個控制元件顏色改成未選中狀態
if (lastSelectedPosition != -1) {
TextView lastSelectedView = (TextView) getChildAt(lastSelectedPosition);
lastSelectedView
.setBackgroundResource(back_unselected);
}
// 將當前選中的控制元件背景改成選中狀態 並記錄
TextView childAt = (TextView) getChildAt(i);
childAt.setBackgroundResource(back_selected);
lastSelectedPosition = i;
break;
}
}
}
break;
default:
break;
}
return true;
}
4.設定回撥和改變狀態<span style="white-space:pre"> </span>/**
* 子控制元件(標籤)選中監聽
*
* @author HaiPeng
*
*/
public interface OnLabelSelectedListener {
void onSelected(int position);
}
/**
* 設定子控制元件選中時的監聽
*
* @param onLabelSelectedListener
*/
public void setOnLabelSelectedListener(
OnLabelSelectedListener onLabelSelectedListener) {
this.onLabelSelectedListener = onLabelSelectedListener;
}
/**
* 記錄上一次點選的是哪個子控制元件
*/
private int lastSelectedPosition = -1;
private OnLabelSelectedListener onLabelSelectedListener;
對了在測量之前給子控制元件設定上沒有選中的背景 @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 父控制元件傳進來的寬度和高度以及對應的測量模式
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// 如果當前ViewGroup的寬高為wrap_content的情況
int width = 0;// 自己測量的 寬度
int height = 0;// 自己測量的高度
// 記錄每一行的寬度和高度
int lineWidth = 0;
int lineHeight = 0;
// 獲取子view的個數
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
<span style="color:#FF6666;">child.setBackgroundResource(back_unselected);</span>
// 測量子View的寬和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到LayoutParams
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
// 子View佔據的寬度
int childWidth = child.getMeasuredWidth() + lp.leftMargin
+ lp.rightMargin;
// 子View佔據的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin
+ lp.bottomMargin;
// 換行時候
if (lineWidth + childWidth > sizeWidth) {
// 對比得到最大的寬度
width = Math.max(width, lineWidth);
// 重置lineWidth
lineWidth = childWidth;
// 記錄行高
height += lineHeight;
lineHeight = childHeight;
} else {// 不換行情況
// 疊加行寬
lineWidth += childWidth;
// 得到最大行高
lineHeight = Math.max(lineHeight, childHeight);
}
// 處理最後一個子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);
}
最後我們在MainActivity中呼叫一下<span style="white-space:pre"> </span>private void initChildViews() {
// TODO Auto-generated method stub
mFlowLayout = (FlowLayout) 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.BLACK);
view.setPadding(5, 5, 5, 5);
// view.setBackgroundDrawable(getResources().getDrawable(R.drawable.textview_bg));
mFlowLayout.addView(view, lp);
}
mFlowLayout.setOnLabelSelectedListener(new OnLabelSelectedListener() {
@Override
public void onSelected(int position) {
Toast.makeText(MainActivity.this, "第" + position + "個被點選了",
Toast.LENGTH_SHORT).show();
}
});
}
效果圖在最上邊,大家已經看過了
點選這裡下載原始碼
相關推薦
安卓流式佈局(可換行的標籤)
最近,公司的專案中需要展示商品的規格和屬性,但是不同的商品屬性個數也是不一樣的, 怎麼能夠讓超過一行的屬性自動換行呢?這就需要用到我們的流式佈局,下面先看看效果圖 這篇文章更改的,流式是怎麼實現的還是請先看完上邊這篇文章. 在將樓主的原始碼下載下來使用的時候遇到以下幾
Android 實現FlowLayout流式佈局(類似熱門標籤)
今天跟大家分享一下FlowLayout,最近專案中有遇到熱門標籤這個樣的佈局(文章末尾可下載原始碼),如下圖: 一,建立FlowLayout並繼承ViewGroup FlowLayout 類主要實現onMeasure,onLayout和generateL
Android 流式佈局FlowLayout 實現關鍵字標籤
1.介紹 流式佈局的應用還是很廣泛的,比如搜尋熱詞、關鍵詞標籤等,GitHub上已經有很多這樣的佈局了,但是還是想著自己實現一下,最近一直在學自定義控制元件,也鞏固一下所學的知識。 本文實現的效果如下圖所示: 2.思路 繼承自RelativeL
安卓中使用流式佈局實現標籤
我們在開發的時候通常需要加標籤,對於這個標籤怎麼說呢,反正也挺複雜的,最初開發這個標籤的時候還是沒有思路的,後來在github上面查找了一下資料,瞭解了通過流式佈局來實現這個標籤,我記得開始的時候我寫標籤的時候是三個TextView一個一個新增進去的,後來感覺還是不太好,所
安卓沉浸式狀態列開發 輸入法彈出遮擋佈局問題解決
公司新版APP採用沉浸式狀態列開發,開發過程中遇到一些奇葩問題,其中一個問題就是輸入法彈出後,佈局沒有被頂上去,無法看到輸入內容 解決方案,在程式碼中,設定 getWindow().setSoftInputMode( WindowManager.LayoutPara
Android開發之玩轉FlexboxLayout佈局(可用於普通控制元件實現流式佈局,也可結合RecycleView實現流式佈局)
在這之前,我曾認真的研究過鴻洋大神的Android 自定義ViewGroup 實戰篇 -> 實現FlowLayout,按照大神的思路寫出了一個流式佈局,所有的東西都是難者不會會者不難,當自己能自定義流式佈局的時候就會覺得這東西原來很簡單了。如果各位小夥伴也看過那篇
100行Android程式碼自定義一個流式佈局-FlowLayout
首先來看一下 手淘HD - 商品詳情 - 選擇商品屬性 頁面的UI 商品有很多尺碼,而且展現每個尺碼所需要的View的大小也不同(主要是寬度),所以在從伺服器端拉到資料之前,展現所有尺碼所需要的行數和每一行的個數都無法確定,因此不能直接使用GridView
安卓手機上的python運行環境-qpython
clu log bcs var arr lec yun upa ros %E7%AC%AC%E4%BA%8C%E5%B1%8APHP%E5%85%A8%E7%90%83%E5%BC%80%E5%8F%91%E8%80%85%E5%A4%A7%E4%BC%9A%E5%90%A
【安卓9】在windows運行命令中操縱數據庫
order rom sqli select color ros stat pac tool 在windows運行命令中操縱數據庫 Microsoft Windows [版本 10.0.10586] (c) 2015 Microsoft Corporation。保留所
安卓app開發-04- app運行的運行和調試
許可 完成 acc targe settings andro 安卓app開發 href 一個 app 運行的運行和調試 本篇介紹在 Android Studio 開發工具,運行調試設備:真機和虛擬機。 真機調試(USB 連接手機) 盡量使用真機進行調試,無論是調試效果和速度
自定義View,流式佈局,
寫的比較基礎, 備忘使用。 public class FlowLayout extends ViewGroup { public FlowLayout(Context context) { this(context, null); }
流式佈局簡單實現
//主方法 //建立集合 private List<String> mStrings=new ArrayList<>(); final EditText ed_search=(EditText)findViewById(R.id.ed_search
YYLabel 自動佈局 不換行 numberOfLines無效
最近是用Masonry自動佈局YYLabel的時候,發現設定了label.numberOfLines = 0,2,3;這些東西之後,label還是沒有換行。 搞了一下子發現,YYLabel還得設定一個preferredMaxLayoutWidth屬性,這個屬性是設定最大寬度,設定完才能有換行
流式佈局 佈局xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app=
笨鳥兒 靜態佈局、自適應佈局、流式佈局、響應式佈局、彈性佈局等的概念和區別
一、靜態佈局(Static Layout) 即傳統Web設計,網頁上的所有元素的尺寸一律使用px作為單位。 1、佈局特點:不管瀏覽器尺寸具體是多少,網頁佈局始終按照最初寫程式碼時的佈局來顯示。常規的pc的網站都是靜態(定寬度)佈局的,也就是設定了min-width,這樣的話,如果小於這個寬度就會
Flex佈局實戰(二):網格 \ 聖盃 \ 輸入框 \ 懸掛式 \ 固定底欄 \ 流式佈局
參考:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html 下面程式碼可能會比較多,但核心CSS程式碼已經用 /**/ 的標記標出,直接看核心程式碼就好。 一、網格佈局 1、基本網格佈局 最簡單
the Percentage Layout of Android (安卓的百分比佈局)
不用wrap_content.match_parent來指定 控制元件的大小, 1.在app/bulid.gradle檔案的dependencies中新增 compile 'com.android.support:percent:24.2.1(注意在新增檔案之後要同步一下) 2.修改佈局檔案xml檔案中
github中的README md快速佈局和換行
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Android 自定義View-----流式佈局(粗糙實現)
//首先檢視一下佈局介面吧 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app
流式佈局 實現搜尋框
自定義View類 public class FlowLayout extends ViewGroup { public FlowLayout(Context context) { this(context,null); } public Flow