package com.mozz.htmlnative.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.mozz.htmlnative.css.Background;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author Yang Tao, 17/4/18.
*/
public class HNDiv extends ViewGroup implements IBackgroundView {
private static final String TAG = HNDiv.class.getSimpleName();
private List<List<View>> mAllViews = new ArrayList<>();
private List<View> mFloatLeftViews = new LinkedList<>();
private List<View> mFloatRightViews = new LinkedList<>();
private List<Integer> mLineLength = new ArrayList<>();
private BackgroundManager mBackgroundMgr;
private Map<String, Object> mSavedInheritStyles = new ArrayMap<>();
public HNDiv(Context context) {
super(context);
mBackgroundMgr = new BackgroundManager(this);
}
public HNDiv(Context context, AttributeSet attrs) {
super(context, attrs);
mBackgroundMgr = new BackgroundManager(this);
}
public HNDiv(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBackgroundMgr = new BackgroundManager(this);
}
public HNDiv(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mBackgroundMgr = new BackgroundManager(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mFloatLeftViews.clear();
mFloatRightViews.clear();
final int msWidth = MeasureSpec.getSize(widthMeasureSpec);
final int msHeight = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = 0;
int height = getPaddingLeft();
int length = getChildCount();
int lineWidth = getPaddingLeft();
int lineHeight = 0;
boolean firstLine = true;
for (int i = 0; i < length; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
HNDivLayoutParams lp = (HNDivLayoutParams) child.getLayoutParams();
if (lp.positionMode == HNDivLayoutParams.POSITION_STATIC || lp.positionMode ==
HNDivLayoutParams.POSITION_RELATIVE) {
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
if (childWidth + lineWidth + getPaddingRight() > msWidth) {
width = Math.max(lineWidth + getPaddingRight(), childWidth + getPaddingLeft());
height += lineHeight;
lineWidth = childWidth + getPaddingLeft();
lineHeight = childHeight;
if (firstLine) {
firstLine = false;
}
} else {
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
if (i == length - 1) {
width = Math.max(lineWidth, width);
height += lineHeight;
}
} else if (lp.positionMode == HNDivLayoutParams.POSITION_FLOAT_LEFT) {
mFloatLeftViews.add(child);
} else if (lp.positionMode == HNDivLayoutParams.POSITION_FLOAT_RIGHT) {
mFloatRightViews.add(child);
}
}
height = height + getPaddingBottom();
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? msWidth : width, heightMode ==
MeasureSpec.EXACTLY ? msHeight : height);
}
@Override
public void addView(View child, LayoutParams params) {
if (!(params instanceof HNDivLayoutParams)) {
super.addView(child, new HNDivLayoutParams(params));
} else {
super.addView(child, params);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mAllViews.clear();
mLineLength.clear();
int width = getWidth();
int lineWidth = 0;
int lineHeight = 0;
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
// 存储每一行所有的childView
List<View> lineViews = new ArrayList<>();
int cCount = getChildCount();
// 遍历所有的孩子
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
HNDivLayoutParams lp = (HNDivLayoutParams) child.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
if (lp.positionMode == HNDivLayoutParams.POSITION_STATIC || lp.positionMode ==
HNDivLayoutParams.POSITION_RELATIVE) {
// 如果已经需要换行
if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth + paddingLeft +
paddingRight > width) {
// 记录这一行所有的View以及最大高度
mLineLength.add(lineHeight);
// 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView
mAllViews.add(lineViews);
lineWidth = childWidth;// 重置行宽
lineViews = new ArrayList<>();
lineViews.add(child);
lineHeight = childHeight;
} else {
/**
* 如果不需要换行,则累加
*/
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin);
lineViews.add(child);
}
} else if (lp.positionMode == HNDivLayoutParams.POSITION_ABSOLUTE) {
child.layout(lp.left, lp.top, lp.right, lp.bottom);
}
}
// 记录最后一行
mLineLength.add(lineHeight);
mAllViews.add(lineViews);
int left = paddingLeft;
int top = paddingTop;
// 得到总行数
int lineNums = mAllViews.size();
for (int i = 0; i < lineNums; i++) {
// 每一行的所有的views
lineViews = mAllViews.get(i);
// 当前行的最大高度
lineHeight = mLineLength.get(i);
// 遍历当前行所有的View
for (int j = 0; j < lineViews.size(); j++) {
View child = lineViews.get(j);
if (child.getVisibility() == View.GONE) {
continue;
}
HNDivLayoutParams lp = (HNDivLayoutParams) child.getLayoutParams();
//计算childView的left,top,right,bottom
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
if (lp.positionMode == HNDivLayoutParams.POSITION_STATIC) {
child.layout(lc, tc, rc, bc);
} else {
child.layout(lc + lp.left, tc + lp.top, rc, bc);
}
left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
}
left = paddingLeft;
top += lineHeight;
}
top = paddingTop;
left = paddingLeft;
int lastLineHeight = 0;
for (View v : mFloatLeftViews) {
if (v.getVisibility() == View.GONE) {
continue;
}
HNDivLayoutParams lp = (HNDivLayoutParams) v.getLayoutParams();
if (left + v.getMeasuredWidth() + lp.rightMargin + lp.leftMargin > getMeasuredWidth()) {
left = 0;
top += lastLineHeight;
lastLineHeight = 0;
}
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc = lc + v.getMeasuredWidth();
int bc = tc + v.getMeasuredHeight();
Log.d("HSL", "layout " + v.getClass().getSimpleName() + ", " + String.format("%d, %d," +
"" + " %d, %d", lc, tc, rc, bc));
v.layout(lc, tc, rc, bc);
lastLineHeight = Math.max(lastLineHeight, v.getHeight() + lp.topMargin + lp
.bottomMargin);
left += v.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
top = paddingTop;
int right = paddingRight;
lastLineHeight = 0;
for (View v : mFloatRightViews) {
if (v.getVisibility() == View.GONE) {
continue;
}
HNDivLayoutParams lp = (HNDivLayoutParams) v.getLayoutParams();
if (right + v.getMeasuredWidth() + lp.rightMargin + lp.leftMargin > getMeasuredWidth
()) {
right = 0;
top += lastLineHeight;
lastLineHeight = 0;
}
int lc = right + lp.rightMargin + v.getMeasuredWidth();
int tc = top + lp.topMargin;
int rc = right + lp.rightMargin;
int bc = tc + v.getMeasuredHeight();
v.layout(lc, tc, rc, bc);
lastLineHeight = Math.max(lastLineHeight, v.getHeight() + lp.topMargin + lp
.bottomMargin);
right += v.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
}
public void saveInheritStyles(String styleName, Object style) {
mSavedInheritStyles.put(styleName, style);
}
public Object getInheritStyle(String styleName) {
return mSavedInheritStyles.get(styleName);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new HNDivLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams
.WRAP_CONTENT);
}
public void setHtmlBackground(Bitmap bitmap, Background background) {
mBackgroundMgr.setHtmlBackground(bitmap, background);
}
@Override
public Background getHtmlBackground() {
return mBackgroundMgr.getHtmlBackground();
}
@Override
public void setBackground(Drawable background) {
// don't support the background!! Use setHtmlBackground instead
}
@Override
protected void onDraw(Canvas canvas) {
mBackgroundMgr.onDraw(canvas);
super.onDraw(canvas);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
public static class HNDivLayoutParams extends MarginLayoutParams {
@IntDef({POSITION_ABSOLUTE, POSITION_RELATIVE, POSITION_STATIC, POSITION_FLOAT_LEFT,
POSITION_FLOAT_RIGHT})
@Retention(RetentionPolicy.SOURCE)
public @interface HNDivPosition {
}
public static final int POSITION_STATIC = 0x01;
public static final int POSITION_RELATIVE = 0x02;
public static final int POSITION_ABSOLUTE = 0x03;
public static final int POSITION_FLOAT_LEFT = 0x04;
public static final int POSITION_FLOAT_RIGHT = 0x05;
public int left, top, right, bottom;
@HNDivPosition
public int positionMode;
public HNDivLayoutParams(int width, int height) {
super(width, height);
this.positionMode = POSITION_STATIC;
}
public HNDivLayoutParams(MarginLayoutParams source) {
super(source);
this.positionMode = POSITION_STATIC;
}
public HNDivLayoutParams(LayoutParams source) {
super(source);
this.positionMode = POSITION_STATIC;
}
public HNDivLayoutParams(HNDivLayoutParams source) {
super(source);
this.left = source.left;
this.top = source.top;
this.right = source.right;
this.bottom = source.bottom;
this.positionMode = source.positionMode;
}
}
}