仿照墨迹天气的天气时景做了一个类似的图片流水式布局的控件。
首先需要自定义一个高度随图片宽度自适应的控件。
public class RatioImageView extends ImageView {
private float mDrawableSizeRatio; //图片的高/宽比例
public RatioImageView(Context context) {
this(context, null);
}
public RatioImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取图片的高/宽比例
if (getDrawable() != null) {
mDrawableSizeRatio = 1f * getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth();
}
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
//获取图片的高/宽比例
if (getDrawable() != null) {
mDrawableSizeRatio = 1f * getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth();
}
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
//获取图片的高/宽比例
if (getDrawable() != null) {
mDrawableSizeRatio = 1f * getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth();
}
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
//获取图片的高/宽比例
if (getDrawable() != null) {
mDrawableSizeRatio = 1f * getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
//根据测量的宽度值求出该控件的高度值,并设置该控件最终的宽度和高度
if (getDrawable() != null) {
if (mode == MeasureSpec.EXACTLY) {
int imageHeight = (int) ((width - getPaddingLeft() - getPaddingRight()) * mDrawableSizeRatio);
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(imageHeight + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY));
} else if (mode == MeasureSpec.AT_MOST) {
int imageWidth = getDrawable().getIntrinsicWidth();
int imageHeight = (int) (imageWidth * mDrawableSizeRatio);
super.onMeasure(MeasureSpec.makeMeasureSpec(imageWidth + getPaddingLeft() + getPaddingRight(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(imageHeight + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY));
}
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
然后自定义用来容纳RatioImageView的ViewGroup。
public class FlowLayout extends ViewGroup {
private int lineCount;//每行容纳的子View的个数
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);
parseAttrs(attrs);
}
private void parseAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout);
lineCount = typedArray.getInteger(R.styleable.FlowLayout_lineCount, 2);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int childWidth = (sizeWidth - getPaddingLeft() - getPaddingRight())/lineCount; //分配给每个子View的宽度
int[] columnHeight = new int[lineCount];//记录每列的高度
int childCount = getChildCount();
int minIndex;
int minHeight;
if (childCount == 0) {
setMeasuredDimension(0, 0);
} else {
//测量每个子view
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
childView.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}
}
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
minHeight = getMinHeight(columnHeight);
minIndex = getMinIndex(columnHeight);
columnHeight[minIndex] += childView.getMeasuredHeight();
}
//设置父容器最终的宽度和高度
if (modeHeight == MeasureSpec.AT_MOST || modeHeight == MeasureSpec.UNSPECIFIED) {
setMeasuredDimension(sizeWidth, getMaxHeight(columnHeight) + getPaddingTop() + getPaddingBottom());
} else {
setMeasuredDimension(sizeWidth, sizeHeight);
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int perWidth = (getMeasuredWidth() - getPaddingLeft() - getPaddingRight())/lineCount;
final int[] columnHeight = new int[lineCount];
int minHeight = 0;
int minIndex = 0;
final int childCount = getChildCount();
//设置每个子View在父容器中的坐标
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() != View.GONE) {
minHeight = getMinHeight(columnHeight);
minIndex = getMinIndex(columnHeight);
childView.layout(minIndex * perWidth + getPaddingLeft(), minHeight + getPaddingTop(),
minIndex * perWidth + childView.getMeasuredWidth() + getPaddingLeft(),
minHeight + childView.getMeasuredHeight() + getPaddingTop());
columnHeight[minIndex] += childView.getMeasuredHeight();
}
}
}
private int getMinHeight(int[] columnHeight) {
int maxHeight = columnHeight[0];
for (int i = 1; i < columnHeight.length; i++) {
if (columnHeight[i] < maxHeight) {
maxHeight = columnHeight[i];
}
}
return maxHeight;
}
private int getMinIndex(int[] columnHeight) {
int maxIndex = 0;
for (int i = 1; i < columnHeight.length; i ++){
if (columnHeight[i] < columnHeight[maxIndex]) {
maxIndex = i;
}
}
return maxIndex;
}
private int getMaxHeight(int[] columnHeight) {
int maxHeight = columnHeight[0];
for (int i = 1; i < columnHeight.length; i++) {
if (columnHeight[i] > maxHeight) {
maxHeight = columnHeight[i];
}
}
return maxHeight;
}
}
最终的效果图:
image.png
网友评论