在最新的安卓控件中可以使用约束布局来实现相同的效果,使用链的形式来实现
如下实例:
app:layout_constraintHorizontal_chainStyle="packed" ....
等实现多个view排列间距的形式 但是不能满足我不同的需求来排列;
下面我自己手撸了一个:可以实现多个view 排列自由组合
package com.kyli.base.view;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
public class SpaceLinearLayout extends LinearLayout {
private static final String TAG="SpaceLinearLayout";
private SpaceSetting spaceSetting;
private SparseIntArray spaceSize = new SparseIntArray();
public void setSpaceSetting(SpaceSetting spaceSetting) {
this.spaceSetting = spaceSetting;
}
public interface SpaceSetting {
int setSpace(int position, int totalSpace, int onlySpace);
}
public SpaceLinearLayout(Context context) {
super(context);
}
public SpaceLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SpaceLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (spaceSetting == null)
return;
if (getOrientation() == HORIZONTAL)
onLayoutWidth(l, t, r, b);
else
onLayoutHeight(l, t, r, b);
}
private void onLayoutWidth(int l, int t, int r, int b) {
int left = l + getPaddingStart();
for (int i = 0; i < getChildCount(); i++) {
left += spaceSize.get(i);
View v = getChildAt(i);
int measuredWidth = v.getMeasuredWidth();
int measuredHeight = v.getMeasuredHeight();
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) v.getLayoutParams();
if (v.getVisibility() != GONE) {
left += layoutParams.getMarginStart();
Log.e(TAG,"[left:]"+left);
v.layout(left, v.getTop(), left + measuredWidth, v.getBottom());
left += layoutParams.getMarginEnd();
left+=measuredWidth;
}
}
}
private void onLayoutHeight(int l, int t, int r, int b) {
int top = l + getPaddingTop();
for (int i = 0; i < getChildCount(); i++) {
top += spaceSize.get(i);
View v = getChildAt(i);
int measuredWidth = v.getMeasuredWidth();
int measuredHeight = v.getMeasuredHeight();
ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) v.getLayoutParams();
if (v.getVisibility() != GONE) {
top += layoutParams.topMargin;
v.layout(v.getLeft(), top, v.getRight(), top + measuredHeight);
top += layoutParams.bottomMargin;
top+=measuredHeight;
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (spaceSetting == null)
return;
if (getOrientation() == HORIZONTAL) {
computerForWidth(widthMeasureSpec, heightMeasureSpec);
} else {
computerForHeight(widthMeasureSpec, heightMeasureSpec);
}
}
private void computerForWidth(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
Log.e(TAG,"[Parent:Width]="+width);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int totalSpace = width - getPaddingStart() - getPaddingEnd();//
int childHieght = 0;
int childWidth = 0;//所有空间的宽度和
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int measuredWidth = child.getMeasuredWidth();
int measuredHeight = child.getMeasuredHeight();
MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
int totalMarginW = layoutParams.getMarginStart() + layoutParams.getMarginEnd();
int totalMarginH = layoutParams.topMargin + layoutParams.bottomMargin;
childHieght = Math.max(childHieght, measuredHeight + totalMarginH);
if (child.getVisibility() != GONE) {
childWidth += totalMarginW;
childWidth += measuredWidth;
totalSpace -= measuredWidth;
totalSpace -= totalMarginW;
}
if (totalSpace <= 0) {
totalSpace = 0;
break;
}
}
if (totalSpace == 0)
return;
int consumeSpace = 0;//消费空间
for (int i = 0; i < getChildCount(); i++) {
int iNeedSpace = spaceSetting.setSpace(i, totalSpace, totalSpace - consumeSpace);
if (iNeedSpace <= totalSpace - consumeSpace) {
consumeSpace += iNeedSpace;
spaceSize.put(i, iNeedSpace);
}else{
spaceSize.put(i, totalSpace - consumeSpace);
consumeSpace =totalSpace;
}
}
int lastSpace = spaceSetting.setSpace(getChildCount(), totalSpace, totalSpace - consumeSpace);
if (lastSpace <= totalSpace - consumeSpace) {
consumeSpace += lastSpace;
spaceSize.put(getChildCount(), lastSpace);
}else{
spaceSize.put(getChildCount(),totalSpace - consumeSpace);
consumeSpace =totalSpace;
}
if (totalSpace - consumeSpace <= 0) {
Log.e("SpaceLinearLayout", "space <=0");
}
setMeasuredDimension(
widthMode != MeasureSpec.EXACTLY ?
consumeSpace + childWidth + getPaddingStart() + getPaddingEnd() :
width,
heightMode != MeasureSpec.EXACTLY ?
childHieght + getPaddingTop() + getPaddingBottom() :
height);
}
private void computerForHeight(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int totalSpace = height - getPaddingTop() - getPaddingBottom();//
int childHieght = 0;
int childWidth = 0;//找出最大宽度
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int measuredWidth = child.getMeasuredWidth();
int measuredHeight = child.getMeasuredHeight();
MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
int totalMarginW = layoutParams.getMarginStart() + layoutParams.getMarginEnd();
int totalMarginH = layoutParams.topMargin + layoutParams.bottomMargin;
childWidth = Math.max(childWidth, measuredWidth + totalMarginW);
if (child.getVisibility() != GONE) {
childHieght += totalMarginH;
childHieght += measuredHeight;
totalSpace -= measuredHeight;
totalSpace -= totalMarginH;
}
if (totalSpace <= 0) {
totalSpace = 0;
break;
}
}
if (totalSpace == 0)
return;
int consumeSpace = 0;
for (int i = 0; i < getChildCount(); i++) {
int iNeedSpace = spaceSetting.setSpace(i, totalSpace, totalSpace - consumeSpace);
if (iNeedSpace <= totalSpace - consumeSpace) {
consumeSpace += iNeedSpace;
spaceSize.put(i, iNeedSpace);
}else{
spaceSize.put(i, totalSpace - consumeSpace);
consumeSpace = totalSpace;
}
}
int lastSpace = spaceSetting.setSpace(getChildCount(), totalSpace, totalSpace - consumeSpace);
if (lastSpace <= totalSpace - consumeSpace) {
consumeSpace += lastSpace;
spaceSize.put(getChildCount(), lastSpace);
}else{
spaceSize.put(getChildCount(), totalSpace - consumeSpace);
consumeSpace=totalSpace;
}
if (totalSpace - consumeSpace <= 0) {
Log.e("SpaceLinearLayout", "space <=0");
}
setMeasuredDimension(
widthMode != MeasureSpec.EXACTLY ?
childWidth + getPaddingStart() + getPaddingEnd() :
width,
heightMode != MeasureSpec.EXACTLY ?
consumeSpace + childHieght + getPaddingTop() + getPaddingBottom() :
height);
}
}
使用时xml中和linearlayout 线性布局完全一样 没有任何不同;
只是需要在java中去自由组合我们的间距:
viewBind.spaceLinear.setSpaceSetting(new SpaceLinearLayout.SpaceSetting() {
@Override
public int setSpace(int position, int totalSpace, int onlySpace) {
int childCount = viewBind.spaceLinear.getChildCount();
return totalSpace / (childCount+1);
}
});
需要说明的是:
该接口中三个参数:
position 每个view的下标 和最后一个view后面的下标
(设计是按照每个view之前会出现一个间距 并且最后一个view的后面也会出现一个)
totalSpace 表示总的间距
onlyspace 表示剩余可用的(position时)空间
手撸完成没多久 ,没有经过大量测试…



