
Порой необходимо создавать элементы динамически, или же размер элементов может сильно варьироваться. Как же тогда организовать перенос элементов на другую строку? Ни LinearLayout
, ни RelativeLayout
этого не позволяют.
Для начала создадим атрибут для нашего элемента, чтобы можно было в .xml задать отступы между элементами.
1.
<
declare-styleable
name
=
"FlowLayout"
>
2.
<
attr
name
=
"paddingV"
format
=
"string"
/>
3.
<
attr
name
=
"paddingH"
format
=
"string"
/>
4.
</
declare-styleable
>
Сам класс же вот:
01.
public
class
FlowLayout
extends
ViewGroup {
02.
// padding bettwen elements
03.
private
int
PAD_H, PAD_V;
04.
private
int
mHeight;
05.
06.
public
FlowLayout(Context context) {
07.
super
(context);
08.
setPaddings(
0
,
0
);
09.
}
10.
11.
protected
void
setPaddings(
int
V,
int
H){
12.
PAD_H = H;
13.
PAD_V = V;
14.
}
15.
16.
protected
void
setPaddings(Context ctx, AttributeSet attrs){
17.
TypedArray a = ctx
18.
.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
19.
String H = a.getString(R.styleable.FlowLayout_paddingH);
20.
String V = a.getString(R.styleable.FlowLayout_paddingV);
21.
// LOG.d("H = " + H + "V=" + V);
22.
if
(H ==
null
|| V ==
null
)
23.
setPaddings(V ==
null
?
0
: Integer.parseInt(V), H ==
null
?
0
:Integer.parseInt(H));
24.
else
{
25.
setPaddings(Integer.parseInt(V), Integer.parseInt(H));
26.
a.recycle();
27.
}
28.
29.
}
30.
31.
public
FlowLayout(Context context, AttributeSet attrs) {
32.
super
(context, attrs);
33.
setPaddings(context,attrs);
34.
}
35.
36.
public
FlowLayout(Context context, AttributeSet attrs,
int
defStyle) {
37.
super
(context, attrs, defStyle);
38.
setPaddings(context,attrs);
39.
}
40.
@Override
41.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
42.
assert
(MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED);
43.
final
int
width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
44.
int
height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();
45.
final
int
count = getChildCount();
46.
int
xpos = getPaddingLeft();
47.
int
ypos = getPaddingTop();
48.
int
childHeightMeasureSpec;
49.
if
(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST)
50.
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
51.
else
52.
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
0
, MeasureSpec.UNSPECIFIED);
53.
mHeight =
0
;
54.
for
(
int
i =
0
; i < count; i++) {
55.
final
View child = getChildAt(i);
56.
if
(child.getVisibility() != GONE) {
57.
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), childHeightMeasureSpec);
58.
final
int
childw = child.getMeasuredWidth();
59.
mHeight = Math.max(mHeight, child.getMeasuredHeight() + PAD_V);
60.
if
(xpos + childw > width) {
61.
xpos = getPaddingLeft();
62.
ypos += mHeight;
63.
}
64.
xpos += childw + PAD_H;
65.
}
66.
}
67.
if
(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) {
68.
height = ypos + mHeight;
69.
}
else
if
(MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
70.
if
(ypos + mHeight < height) {
71.
height = ypos + mHeight;
72.
}
73.
}
74.
height +=
5
;
// Fudge to avoid clipping bottom of last row.
75.
setMeasuredDimension(width, height);
76.
}
77.
78.
@Override
79.
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
80.
final
int
width = r - l;
81.
int
xpos = getPaddingLeft();
82.
int
ypos = getPaddingTop();
83.
for
(
int
i =
0
; i < getChildCount(); i++) {
84.
final
View child = getChildAt(i);
85.
if
(child.getVisibility() != GONE) {
86.
final
int
childw = child.getMeasuredWidth();
87.
final
int
childh = child.getMeasuredHeight();
88.
if
(xpos + childw > width) {
89.
xpos = getPaddingLeft();
90.
ypos += mHeight;
91.
}
92.
child.layout(xpos, ypos, xpos + childw, ypos + childh);
93.
xpos += childw + PAD_H;
94.
}
95.
}
96.
}
97.
98.
}
Использование класса в .xml будет таким:
01.
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
02.
xmlns:t
=
"http://schemas.android.com/apk/res/ru.suvitruf.flowlayoutexample"
03.
xmlns:tools
=
"http://schemas.android.com/tools"
04.
android:layout_width
=
"match_parent"
05.
android:layout_height
=
"match_parent"
06.
android:paddingBottom
=
"@dimen/activity_vertical_margin"
07.
android:paddingLeft
=
"@dimen/activity_horizontal_margin"
08.
android:paddingRight
=
"@dimen/activity_horizontal_margin"
09.
android:paddingTop
=
"@dimen/activity_vertical_margin"
10.
tools:context
=
".MainActivity"
>
11.
12.
<
ru.suvitruf.flowlayoutexample.view.FlowLayout
13.
android:id
=
"@+id/test_flow_layout"
14.
android:layout_width
=
"wrap_content"
15.
android:layout_height
=
"wrap_content"
16.
t:paddingH
=
"@integer/test_padding_h"
17.
t:paddingV
=
"@integer/test_padding_v"
>
18.
</
ru.suvitruf.flowlayoutexample.view.FlowLayout
>
19.
20.
</
RelativeLayout
>
Можно добавлять элементы прям внутрь нашего элемента. Или же добавлять динамически, скажем так:
01.
FlowLayout l = (FlowLayout) findViewById(R.id.test_flow_layout);
02.
03.
for
(
int
i =
0
; i < TEST_ELEMENTS_COUNT; ++i) {
04.
Button btn =
new
Button(
this
);
05.
btn.setText(getResources().getString(R.string.test_string)
06.
+ Integer.toString(i));
07.
l.addView(btn);
08.
}
09.
Тогда, увидите то же, что и на пикче сверху. Единственная проблема — добавление пустых контейнеров. Скажем, если вы добавите внутрь FlowLayout
, к примеру, LinearLayout
без всяких элементов (даже с явно заданными размерами), то не отобразится этот LinearLayout
. Качнуть можно с гитхаба.
Спасибо, использовал Вашу разработку.
Скопировал весь код,но студия не хочет распознавать эту строку:
xmlns:t=»http://schemas.android.com/apk/res/ru.suvitruf.flowlayoutexample»
После удаления строки ошибка при запуске + не отображается компонент.Подскажите пожалуйста как исправить
А класс у вас в каком пакете находится?