前言
網上關於流式佈局的代碼已經很多了,但是我爲什麼又要去寫呢,主要是在實際使用中發現定製化困難的緣故了.另一方面,湊文章數 😃
目前支持新增,長按刪除,點擊item切換背景和字體顏色,以及item的單擊和長按回調.
思路
一個文字列表,逐行排列,在每次開始的時候計算一下同一行剩餘的width是否足夠文字顯示,如果不夠,就增加高度,即移至下一行.
先看一下效果:
不過目前有一處並未做處理,就是view整體的高度自動適配,這個等我先把DailyLife做完之後去優化吧.
代碼參考如下:
public class FlowLayout extends View {
private static final String TAG = "FlowLayout";
private Paint mPaint;
private List<String> textList = new ArrayList<>();
private int minPadding = 80;
private int textPadding = 20;
private float textSize = 30;
private int textPace = 35; //兩個item的間隔
private int textHeight = 40;
private int distanceW, distanceH;
private List<int[]> pointList = new ArrayList<>();
private int clickIndex = -1;
private long touchDownTime;
private boolean isLongClick;
private int textNormalColor;
private int textClickColor;
private boolean isShowEndAdd;
private FlowClickListener listener;
private float downY;
private boolean paddingHasChanged;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
textNormalColor = a.getColor(R.styleable.FlowLayout_flow_text_normal_color, Color.BLUE);
textClickColor = a.getColor(R.styleable.FlowLayout_flow_text_click_color, Color.WHITE);
textSize = a.getDimension(R.styleable.FlowLayout_flow_text_size, context.getResources().getDimension(R.dimen.dimen_size_15));
initPaint();
}
public boolean isLongClick() {
return isLongClick;
}
public void setData(List<String> textList) {
setData(textList, false, -1);
}
public void setData(List<String> textList, boolean isShowEndAdd, int clickIndex) {
this.isShowEndAdd = isShowEndAdd;
this.clickIndex = clickIndex;
this.textList.clear();
this.textList.addAll(textList);
postInvalidate();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setTextSize(textSize);
Rect rect = new Rect();
mPaint.getTextBounds("1", 0, "1".length(), rect);
textHeight = rect.height();
textPadding = textHeight;
textPace = textHeight * 3 / 2;
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
if (textList.size() <= 0)
return;
int startTextTop = minPadding;
int startTextLeft;
int totalTextWidth = 0;
pointList.clear();
mPaint.setTextSize(textSize);
for (int i = 0; i < textList.size(); i++) {
mPaint.setTextAlign(Paint.Align.LEFT);
float width = mPaint.measureText(textList.get(i));
int lastWidth = totalTextWidth;
totalTextWidth += (width + textPadding * 2 + textPace);
if (totalTextWidth + textHeight * 2 > distanceW) { //判斷width 是否夠
totalTextWidth = (int) (width + textPadding * 2 + textPace);
startTextLeft = textHeight * 2;
startTextTop += textHeight + textPadding * 2 + textPace;
} else {
startTextLeft = lastWidth + textHeight * 2;
}
mPaint.setColor(textNormalColor);
boolean isShowClickType = i == clickIndex && null != listener && (!isShowEndAdd || i != textList.size() - 1) && !paddingHasChanged;
mPaint.setStyle(isShowClickType ? Paint.Style.FILL : Paint.Style.STROKE);
int[] array = {startTextLeft - textPadding, startTextTop - textPadding - textHeight,
(int) (startTextLeft + width + textPadding), startTextTop + textPadding};
canvas.drawRect(array[0], array[1], array[2], array[3], mPaint);
mPaint.setColor(isShowClickType ? textClickColor : textNormalColor);
canvas.drawText(textList.get(i), startTextLeft, startTextTop, mPaint);
if (isLongClick && null != listener && (!isShowEndAdd || i != textList.size() - 1) && !paddingHasChanged) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(textClickColor);
canvas.drawCircle(startTextLeft + width + textPadding, startTextTop - textPadding * 2, textPadding, mPaint);
mPaint.setColor(textNormalColor);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(startTextLeft + width + textPadding, startTextTop - textPadding * 2, textPadding, mPaint);
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("x", startTextLeft + width + textPadding, startTextTop - textPadding * 2 + textHeight / 2, mPaint);
int part = textPadding + 5;
array[1] -= part;
array[2] += part;
array[0] = array[2] - part * 2;
array[3] = array[1] + part * 2;
}
pointList.add(array);
}
}
public void clearDeleteType() {
clickIndex = -1;
isLongClick = false;
postInvalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int resultWidth = sizeWidth;
int resultHeight = sizeHeight;
// 考慮內邊距對尺寸的影響
resultWidth += getPaddingLeft() + getPaddingRight();
resultHeight += getPaddingTop() + getPaddingBottom();
// 考慮父容器對尺寸的影響
distanceW = resultWidth = resolveMeasure(sizeWidth, resultWidth);
distanceH = resultHeight = resolveMeasure(sizeHeight, resultHeight);
Log.d(TAG, "onMeasure: sizeHeight: " + sizeHeight + ",resultHeight: " + resultHeight);
setMeasuredDimension(resultWidth, resultHeight);
}
/**
* 根據傳入的值進行測量
*/
public int resolveMeasure(int measureSpec, int defaultSize) {
int result = 0;
int specSize = MeasureSpec.getSize(measureSpec);
switch (MeasureSpec.getMode(measureSpec)) {
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
//設置warp_content時設置默認值
result = Math.min(specSize, defaultSize);
break;
default:
result = defaultSize;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
if (paddingHasChanged) {
postInvalidate();
paddingHasChanged = false;
}
if (System.currentTimeMillis() - touchDownTime > 500) {
if (null != listener) {
clickIndex = -1;
isLongClick = !isLongClick;
postInvalidate();
}
} else {
int i = 0;
float x = event.getX();
float y = event.getY();
for (int[] arr : pointList) {
if (x >= arr[0] && x <= arr[2] && y >= arr[1] && y <= arr[3]) {
clickIndex = i;
break;
}
i++;
}
if (null != listener && clickIndex >= 0) {
if (isLongClick)
listener.setOnClickLongItemListener(clickIndex);
else
listener.setOnClickItemListener(clickIndex);
postInvalidate();
}
}
break;
case MotionEvent.ACTION_DOWN:
downY = event.getY();
paddingHasChanged = false;
touchDownTime = System.currentTimeMillis();
break;
}
return true;
}
public void removeItem(int index) {
textList.remove(index);
clickIndex = -1;
postInvalidate();
}
public void setFlowClickListener(FlowClickListener clickListener) {
this.listener = clickListener;
}
public interface FlowClickListener {
void setOnClickItemListener(int index);
void setOnClickLongItemListener(int index);
}
}
attrs.xml文件內容
<declare-styleable name="FlowLayout">
<attr name="flow_text_normal_color" format="color"/>
<attr name="flow_text_click_color" format="color"/>
<attr name="flow_text_size" format="dimension"/>
</declare-styleable>
最後
地址等我明天上傳吧,順便把寫過的自定義view統一做一下處理.
誒,小熊錄屏的gif爲什麼不能在csdn播放啊啊啊啊啊啊,明天找原因.