/
FlowLayout.java
702 lines (636 loc) · 27.2 KB
/
FlowLayout.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
package com.renj.flowlayout;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import java.util.ArrayList;
import java.util.List;
/**
* ======================================================================
* <p>
* 作者:Renj
* <p>
* 创建时间:2020-10-29 09:51
* <p>
* 描述:流式布局控件
* <p>
* 修订历史:
* <p>
* ======================================================================
*/
public class FlowLayout extends ViewGroup {
/**
* 居左对齐,默认
*/
public static final int HORIZONTAL_GRAVITY_LEFT = 0;
/**
* 居右对齐
*/
public static final int HORIZONTAL_GRAVITY_RIGHT = 1;
/**
* 左右对齐
*/
public static final int HORIZONTAL_GRAVITY_LEFT_RIGHT = 2;
/**
* 居中对齐
*/
public static final int HORIZONTAL_GRAVITY_CENTER = 3;
private int mViewContentWidth; // 内容显示宽度
private int mViewContentHeight; // 内容显示高度
private int mViewReallyHeight; // 控件实际高度(所有子控件的高度和+paddingTop+paddingBottom)
private int mHorizontalSpacing; // 水平方向间距
private int mVerticalSpacing; // 竖直方向间距
private int mShowChildViewCount; // 显示的子控件数
private boolean mChildViewAllShow = true; // 子控件是否已经全部显示了
private int mTotalShowRowCount; // 总显示行数
private int mMaxRowCount = Integer.MAX_VALUE; // 最大显示行数
private List<RowChildViewInfo> mRowChildViewList = new ArrayList<>(); // 所有子控件行信息集合
private int mMaxScrollY; // 滑动时,最大滑动偏移量
private Scroller mScroller; // 支持滑动
private VelocityTracker mVelocityTracker; // ACTION_UP 时测速
// 每一行的水平方向对齐方式
private int mHorizontalGravity = HORIZONTAL_GRAVITY_LEFT;
// 适配器对象
private FlowLayoutAdapter mFlowLayoutAdapter;
// 子控件点击监听
private OnItemClickListener mOnItemClickListener;
// 子控件布局完成监听
private OnChildLayoutFinishListener mOnChildLayoutFinishListener;
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);
init(context, attrs);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mScroller = new Scroller(context);
mVelocityTracker = VelocityTracker.obtain();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
mMaxRowCount = typedArray.getInteger(R.styleable.FlowLayout_flow_max_row_count, Integer.MAX_VALUE);
mHorizontalGravity = typedArray.getInteger(R.styleable.FlowLayout_flow_horizontal_gravity, HORIZONTAL_GRAVITY_LEFT);
mHorizontalSpacing = typedArray.getDimensionPixelSize(R.styleable.FlowLayout_flow_horizontal_spacing, 0);
mVerticalSpacing = typedArray.getDimensionPixelSize(R.styleable.FlowLayout_flow_vertical_spacing, 0);
typedArray.recycle();
}
/**
* 设置适配器
*
* @param flowLayoutAdapter {@link FlowLayoutAdapter} 子类对象
*/
public void setAdapter(FlowLayoutAdapter flowLayoutAdapter) {
if (flowLayoutAdapter != null) {
this.mFlowLayoutAdapter = flowLayoutAdapter;
flowLayoutAdapter.setFlowLayout(this);
requestLayout();
}
}
/**
* 获取适配器,由方法 {@link #setAdapter(FlowLayoutAdapter)} 设置的
*
* @return 返回设置的适配器
*/
public FlowLayoutAdapter getFlowLayoutAdapter() {
return mFlowLayoutAdapter;
}
/**
* 设置子控件布局完成监听
*
* @param onChildLayoutFinishListener {@link OnChildLayoutFinishListener}
*/
public void setOnChildLayoutFinishListener(OnChildLayoutFinishListener onChildLayoutFinishListener) {
this.mOnChildLayoutFinishListener = onChildLayoutFinishListener;
}
/**
* 移除子控件布局完成监听
*/
public void removeOnLayoutFinishListener() {
this.mOnChildLayoutFinishListener = null;
}
/**
* 设置子控件点击监听
*
* @param onItemClickListener {@link OnItemClickListener}
*/
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
/**
* 设置最大显示行数
*
* @param maxRowCount 最大显示行数 小于0表示全部显示
*/
public void setMaxRowCount(int maxRowCount) {
if ((maxRowCount == mMaxRowCount)
|| (maxRowCount < 0 && mMaxRowCount < 0)
|| (mChildViewAllShow && maxRowCount > mTotalShowRowCount)
|| (mChildViewAllShow && maxRowCount < 0)) {
return;
}
if (maxRowCount < 0) {
maxRowCount = Integer.MAX_VALUE;
}
this.mMaxRowCount = maxRowCount;
// 先滑动到顶部
if (getScrollY() > 0) {
scrollTo(0, 0);
}
requestLayout();
}
/**
* 设置水平方向控件对齐方式,默认居左对齐({@link #HORIZONTAL_GRAVITY_LEFT})
*
* @param horizontalGravity {@link #HORIZONTAL_GRAVITY_LEFT}、
* {@link #HORIZONTAL_GRAVITY_RIGHT}、
* {@link #HORIZONTAL_GRAVITY_LEFT_RIGHT}、
* {@link #HORIZONTAL_GRAVITY_CENTER}
*/
public void setHorizontalGravity(int horizontalGravity) {
if (this.mHorizontalGravity != horizontalGravity) {
this.mHorizontalGravity = horizontalGravity;
requestLayout();
}
}
/**
* 设置子控件之间的间距
*
* @param horizontalSpacing 水平方向间距 dp
* @param verticalSpacing 竖直方向间距 dp
*/
public void setSpacing(int horizontalSpacing, int verticalSpacing) {
if (horizontalSpacing < 0 || verticalSpacing < 0) return;
horizontalSpacing = dip2px(getContext(), horizontalSpacing);
verticalSpacing = dip2px(getContext(), verticalSpacing);
if (this.mHorizontalSpacing != horizontalSpacing || this.mVerticalSpacing != verticalSpacing) {
this.mHorizontalSpacing = horizontalSpacing;
this.mVerticalSpacing = verticalSpacing;
requestLayout();
}
}
/**
* 滚动到顶部
*
* @param animation true:使用动画滚动 false:不使用动画
*/
public void scrollToTop(boolean animation) {
int scrollY = getScrollY();
if (scrollY > 0) {
if (animation) {
smallScrollToPosition(scrollY, -scrollY);
} else {
scrollTo(0, 0);
}
}
}
/**
* 滚动到底部
*
* @param animation true:使用动画滚动 false:不使用动画
*/
public void scrollToBottom(boolean animation) {
int scrollY = getScrollY();
if (mMaxScrollY > scrollY) {
if (animation) {
smallScrollToPosition(scrollY, mMaxScrollY - scrollY);
} else {
scrollTo(0, mMaxScrollY);
}
}
}
/**
* 滚动到指定位置
*
* @param animation position:需要滚动到的位置
* @param animation true:使用动画滚动 false:不使用动画
*/
public void scrollToPosition(int position, boolean animation) {
if (position <= 0) {
scrollToTop(animation);
} else if (position >= mMaxScrollY) {
scrollToBottom(animation);
} else {
if (animation) {
int scrollY = getScrollY();
smallScrollToPosition(scrollY, position - scrollY);
} else {
scrollTo(0, position);
}
}
}
/**
* 滚动到指定行数
*
* @param animation rowNumber:需要滚动到的行数值
* @param animation true:使用动画滚动 false:不使用动画
*/
public void scrollToRowNumber(int rowNumber, boolean animation) {
if (rowNumber <= 0) {
scrollToTop(animation);
} else if (rowNumber >= mTotalShowRowCount) {
scrollToBottom(animation);
} else {
int position = 0;
// 根据行数计算 position
for (RowChildViewInfo rowChildViewInfo : mRowChildViewList) {
position += rowChildViewInfo.rowHeight;
if (rowChildViewInfo.rowNumber == rowNumber) {
break;
}
}
scrollToPosition(position, animation);
}
}
/**
* 竖直方向平滑滚动方法
*
* @param startY 开始位置
* @param dy 移动距离
*/
private void smallScrollToPosition(int startY, int dy) {
mScroller.startScroll(0, startY, 0, dy, Math.min(600, Math.max(300, Math.abs(dy))));
postInvalidate();
}
/**
* 获取显示的行数。<br/>
* <b>重点注意:不要直接调用,而要在 {@link #setOnChildLayoutFinishListener(OnChildLayoutFinishListener)}
* 回调中调用才能保证结果的正确性。</b><br/><br/>
* 注意:当调用 {@link #setMaxRowCount(int)} 方法设置了最大行数时,<br/>
*
* 可能小于最大行数:当设置的最大行数 > 全部显示完成所需的行数时;<br/>
*
* 或者等于最大行数:当设置的最大行数 <= 全部显示完成所需的行数时。
*
* @return 显示的行数
*/
public int getShowRowCount() {
return mTotalShowRowCount;
}
/**
* 是否所有的子控件都显示了。<br/>
* <b>重点注意:不要直接调用,而要在 {@link #setOnChildLayoutFinishListener(OnChildLayoutFinishListener)}
* 回调中调用才能保证结果的正确性。</b><br/><br/>
* 注意:当调用 {@link #setMaxRowCount(int)} 方法设置了最大行数时,可能并非所有子控件都显示完全了。
*
* @return true:所有子控件都显示出来了 false:还有子控件没有显示出来
*/
public boolean isChildViewAllShow() {
return mChildViewAllShow;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
removeAllViews();
mRowChildViewList.clear();
mShowChildViewCount = 0;
mTotalShowRowCount = 0;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (mFlowLayoutAdapter == null || mMaxRowCount == 0) {
// 确定高度
if (heightMode == MeasureSpec.EXACTLY) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
} else {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
// 内容显示宽度和高度
mViewContentWidth = widthSize - getPaddingLeft() - getPaddingRight();
mViewContentHeight = heightSize - getPaddingTop() - getPaddingBottom();
// 所有孩子控件都完全显示需要的高度,默认加上顶部的 padding 值
int flowLayoutReallyHeight = getPaddingTop();
int childCount = mFlowLayoutAdapter.getItemCount();
if (childCount > 0) {
// 当前行已使用的宽度
int currentRowWidth = 0;
// 当前子控件所需要的水平方向间距,因为间距数 = 水平方向一行子控件个数 - 1(最后一个没有间距)
// 本来是每行除最后一个之外,每个后面有间距;这里转换一下,变成除了第一个之外,后面每个在前面加一个间距
int currentHorizontalSpacing = 0;
// 当前行的高度,以一行中最大高度的子控件高度为行高
int currentRowMaxHeight = 0;
// 总行数
mTotalShowRowCount = 1;
// 显示的子控件数
mShowChildViewCount = 0;
List<ChildViewInfo> mChildViewList = new ArrayList<>();
for (int i = 0; i < childCount; i++) {
View childView = mFlowLayoutAdapter.createView(getContext(), this, i);
if (childView.getVisibility() == View.GONE) {
continue;
}
addView(childView);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int childViewLeftMargin = 0;
int childViewTopMargin = 0;
int childViewRightMargin = 0;
int childViewBottomMargin = 0;
if (marginLayoutParams != null) {
childViewLeftMargin = marginLayoutParams.leftMargin;
childViewTopMargin = marginLayoutParams.topMargin;
childViewRightMargin = marginLayoutParams.rightMargin;
childViewBottomMargin = marginLayoutParams.bottomMargin;
}
int childViewWidth = childView.getMeasuredWidth();
int childViewHeight = childView.getMeasuredHeight();
// 计算当前行已使用的宽度
currentRowWidth += childViewWidth + childViewLeftMargin + childViewRightMargin + currentHorizontalSpacing;
// 取一行最大高度为行高
currentRowMaxHeight = Math.max(childViewHeight + childViewTopMargin + childViewBottomMargin, currentRowMaxHeight);
// 换行
if (currentRowWidth > mViewContentWidth) {
// 增加上一行高度
flowLayoutReallyHeight += currentRowMaxHeight;
// 组合成新的行对象信息
RowChildViewInfo rowChildViewInfo = new RowChildViewInfo();
rowChildViewInfo.rowChildViews = mChildViewList;
rowChildViewInfo.rowNumber = mTotalShowRowCount;
rowChildViewInfo.rowHeight = currentRowMaxHeight;
rowChildViewInfo.currentRowUsedWidth = currentRowWidth - (childViewWidth + childViewLeftMargin + childViewRightMargin + currentHorizontalSpacing);
mRowChildViewList.add(rowChildViewInfo);
// 换行时设置为0,因为间距数 = 水平方向一行子控件个数 - 1 (最后一个没有间距)
// 本来是每行除最后一个之外,每个后面有间距;这里转换一下,变成除了第一个之外,后面每个在前面加一个间距
currentHorizontalSpacing = 0;
// 计算新行已使用宽度
currentRowWidth = childViewWidth + childViewLeftMargin + childViewRightMargin + currentHorizontalSpacing;
// 新行高度
currentRowMaxHeight = childViewHeight + childViewTopMargin + childViewBottomMargin;
// 新行子控件集合
mChildViewList = new ArrayList<>();
// 总行数加1
mTotalShowRowCount += 1;
// 显示最大行数控制
if (mTotalShowRowCount > mMaxRowCount) {
// 超过最大行数的部分,减掉
mTotalShowRowCount -= 1;
currentRowMaxHeight = 0;
break;
}
// 增加竖直方向上的间距
flowLayoutReallyHeight += mVerticalSpacing;
}
// 确定当前子控件所在的位置
ChildViewInfo childViewInfo = new ChildViewInfo(childView, mTotalShowRowCount, i);
childViewInfo.right = currentRowWidth - childViewRightMargin + getPaddingLeft();
childViewInfo.left = childViewInfo.right - childViewWidth;
childViewInfo.top = flowLayoutReallyHeight + childViewTopMargin;
childViewInfo.bottom = childViewInfo.top + childViewHeight;
mChildViewList.add(childViewInfo);
mShowChildViewCount += 1;
// 除了每行的第一个,后面的子控件都在前边加上一个水平间距
currentHorizontalSpacing = mHorizontalSpacing;
}
// 加上最后一行高度
flowLayoutReallyHeight += currentRowMaxHeight;
// 加上最后一行的行对象信息
RowChildViewInfo rowChildViewInfo = new RowChildViewInfo();
rowChildViewInfo.rowChildViews = mChildViewList;
rowChildViewInfo.rowNumber = mTotalShowRowCount;
rowChildViewInfo.rowHeight = currentRowMaxHeight;
rowChildViewInfo.currentRowUsedWidth = currentRowWidth;
mRowChildViewList.add(rowChildViewInfo);
}
// 加上底部 padding 值
flowLayoutReallyHeight += getPaddingBottom();
mViewReallyHeight = flowLayoutReallyHeight;
// 确定高度
if (heightMode == MeasureSpec.EXACTLY) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
} else {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(flowLayoutReallyHeight, MeasureSpec.EXACTLY);
}
// 滑动时,最大滑动偏移量
mMaxScrollY = mViewReallyHeight - mViewContentHeight - getPaddingBottom() - getPaddingTop();
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 确定子控件是否已经全部显示了
mChildViewAllShow = mFlowLayoutAdapter == null ? true : mFlowLayoutAdapter.getItemCount() == mShowChildViewCount;
if (mRowChildViewList.isEmpty()) {
if (mOnChildLayoutFinishListener != null)
mOnChildLayoutFinishListener.onLayoutFinish(this, mShowChildViewCount);
return;
}
// 水平方向不同对齐方式偏移量,默认居左对齐,不偏移
int offsetX = 0;
for (final RowChildViewInfo rowChildViewInfo : mRowChildViewList) {
List<ChildViewInfo> rowChildViews = rowChildViewInfo.rowChildViews;
if (mHorizontalGravity == HORIZONTAL_GRAVITY_RIGHT) {
// 居右对齐
offsetX = mViewContentWidth - rowChildViewInfo.currentRowUsedWidth;
} else if (mHorizontalGravity == HORIZONTAL_GRAVITY_LEFT_RIGHT) {
// 左右两端对齐
if (rowChildViews.size() > 1) {
offsetX = (mViewContentWidth - rowChildViewInfo.currentRowUsedWidth) / (rowChildViews.size() - 1);
} else {
offsetX = 0;
}
} else if (mHorizontalGravity == HORIZONTAL_GRAVITY_CENTER) {
// 居中对齐
offsetX = (mViewContentWidth - rowChildViewInfo.currentRowUsedWidth) / 2;
}
for (int i = 0; i < rowChildViews.size(); i++) {
final ChildViewInfo childViewInfo = rowChildViews.get(i);
if (mHorizontalGravity == HORIZONTAL_GRAVITY_LEFT_RIGHT) {
// 左右两端对齐,需要特殊处理
childViewInfo.onLayout(offsetX * i);
} else {
childViewInfo.onLayout(offsetX);
}
childViewInfo.addClickListener(mOnItemClickListener, this, mFlowLayoutAdapter);
}
}
if (mOnChildLayoutFinishListener != null)
mOnChildLayoutFinishListener.onLayoutFinish(this, mShowChildViewCount);
}
private float mInterceptDownX;
// 获取TouchSlop值
float mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInterceptDownX = ev.getRawX();
break;
case MotionEvent.ACTION_MOVE:
float mXMove = ev.getRawX();
float diff = Math.abs(mXMove - mInterceptDownX);
// 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
if (diff > mTouchSlop) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
private int mTouchEventLastY;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mViewReallyHeight > mViewContentHeight + getPaddingTop() + getPaddingBottom()) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchEventLastY = y;
break;
case MotionEvent.ACTION_MOVE:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
int dy = mTouchEventLastY - y;
// 向上滑动
if (dy < 0) {
if (getScrollY() == 0) {
return super.onTouchEvent(event);
}
if (getScrollY() + dy < 0) {
scrollTo(0, 0);
return true;
}
} else {
// 向下滑动
if (getScrollY() == mMaxScrollY) {
return super.onTouchEvent(event);
}
if (getScrollY() + dy > mMaxScrollY) {
scrollTo(0, mMaxScrollY);
return true;
}
}
scrollBy(0, dy);
mTouchEventLastY = y;
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
int initialVelocity = (int) mVelocityTracker.getYVelocity();
if (Math.abs(initialVelocity) > 200) {
// 由于坐标轴正方向问题,要加负号。
mScroller.fling(0, getScrollY(), 0, -initialVelocity, 0, 0, 0, 10000);
}
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
postInvalidate();
return true;
} else {
return super.onTouchEvent(event);
}
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
int currY = mScroller.getCurrY();
// 快速滑动边界判断
if (currY > mMaxScrollY) {
currY = mMaxScrollY;
scrollTo(0, currY);
mScroller.abortAnimation();
}
scrollTo(0, currY);
postInvalidate();
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 每一行信息
*/
private static class RowChildViewInfo {
private int currentRowUsedWidth; // 行使用宽度
private int rowNumber; // 行号
private int rowHeight; // 行高
private List<ChildViewInfo> rowChildViews; // 行内子控件列表
}
/**
* 子控件信息
*/
private static class ChildViewInfo {
private View childView;
private int left;
private int top;
private int right;
private int bottom;
private int rowNumber; // 所在行位置
private int position; // 在父控件中的位置
private ChildViewInfo(View childView, int rowNumber, int position) {
this.childView = childView;
this.rowNumber = rowNumber;
this.position = position;
}
private void onLayout(int offsetX) {
childView.layout(left + offsetX, top, right + offsetX, bottom);
}
private void addClickListener(final OnItemClickListener onItemClickListener,
final FlowLayout flowLayout,
final FlowLayoutAdapter flowLayoutAdapter) {
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(flowLayout, flowLayoutAdapter,
rowNumber, position);
}
}
});
}
}
/**
* 子控件点击监听
*/
public interface OnItemClickListener {
/**
* 点击子控件回调方法
*
* @param flowLayout {@link FlowLayout} 控件对象
* @param adapter {@link FlowLayoutAdapter} 对象
* @param rowNumber 所在行,行号从 1 开始
* @param position 在父控件的位置,位置从 0 开始
*/
void onItemClick(FlowLayout flowLayout, FlowLayoutAdapter adapter, int rowNumber, int position);
}
/**
* 孩子控件布局完成监听
*/
public interface OnChildLayoutFinishListener {
/**
* @param flowLayout {@link FlowLayout} 控件对象
* @param showChildCount 当前完成布局的孩子数(显示的孩子数)
*/
void onLayoutFinish(FlowLayout flowLayout, int showChildCount);
}
}