Android仿淘宝搜索历史界面功能

龙旋

共 7131字,需浏览 15分钟

 ·

2021-08-03 19:12

一 关于自定义viewGroup

自定义viewGroup需要继承viewGroup,并重写抽象方法onLayout,
负责子view位置的摆放。onMeasure()方法则完成对子view的测量过程,
其中,对子view的测量涉及到MeasureSpec,包括父布局的MeasureSpec
和子view的MeasureSpec。对子view的宽高测量完成后,相加即为
viewGroup的宽高,再通过setMeasuredDimension()
这个方法来保存测量好的宽高。

二 关于MeasureSpec

MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代码SpecSize,SpecMode是指测量模式,而SpecSize是指在某种测量模式下地规格地大小。

SpecMode有三类,每一类都表示特殊的含义,如下所示。

UNSPECIFIED
父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量状态。

EXACTLY
父容器以及检测出View所需要的大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体数值这两种模式。

AT_MOST
父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么要看不同View的具体实现。它对应于LayoutParams中的wrap_content。


三 关于流式布局Measure流程


1.遍历所有的子view,根据父布局的MeasureSpec还有子view的
LayoutParams 算出子view的MesureSpec

LayoutParams childLP = childView.getLayoutParams();
int childWidthMesureSpec =getChildMeasureSpec(widthMeasureSpec,paddingLeft+paddingRight,childLP.width);
int childHeightMesureSpec =getChildMeasureSpec(heightMeasureSpec,paddingBottom+paddingTop,childLP.height);
childView.measure(childWidthMesureSpec,childHeightMesureSpec);

2.通过宽度来判断是否需要换行,通过换行后的每行的行高来获取整个viewGroup的行高
如果宽度不够,则需换行

if (childMesuredWidth + lineWidthUsed +mHorizontalSpacing > selfWidth){
allLines.add(lineViews);
lineHeights.add(lineHeight);
//一旦换行,可以判断当前所需的宽高了
parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed+mHorizontalSpacing);
parentNeededHeight = parentNeededHeight + lineHeight +mVerticalSpacing;
lineViews =new ArrayList<>();
lineWidthUsed =0;
lineHeight =0;
}

3.根据子View的度量结果,来重新度量自己ViewGroup
作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = (widthMode == MeasureSpec.EXACTLY)? selfWidth:parentNeededWidth;
int realHeight = (heightMode == MeasureSpec.EXACTLY)?selfHeight:parentNeededHeight;
setMeasuredDimension(realWidth,realHeight);

四 关于layout流程

measure完成后,每个子view的宽高通过view.getMeasuredHeight/Width 
来获得,只需算出距离viewGroup左边和顶部的距离传入即可。

int lineCount =allLines.size();
int curL =0;
int curT =0;
for (int i =0;i
List lineViews =allLines.get(i);
int lineHeight =lineHeights.get(i);
for (int j =0;j
View view = lineViews.get(j);
int left = curL;
int top = curT;
int bottom = top + view.getMeasuredHeight();
int right = left + view.getMeasuredWidth();
view.layout(left,top,right,bottom);
curL = right +mHorizontalSpacing;
}
curL =0;
curT = curT + lineHeight +mVerticalSpacing;

五 关于源代码FlowLayout源代码
public class FlowLayoutextends ViewGroup {
private int mHorizontalSpacing =dp2px(16); //每个item横向间距
private int mVerticalSpacing =dp2px(8); //每个item横向间距
private List>allLines ; // 记录所有的行,一行一行的存储
private ListlineHeights =new ArrayList<>(); // 记录每一行的行高
boolean isMeasuredOver =false;
public FlowLayout(Context context) {
super(context);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
initMeasureParams();
int childCount = getChildCount();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int selfWidth = MeasureSpec.getSize(widthMeasureSpec); //ViewGroup解析的宽度
int selfHeight = MeasureSpec.getSize(heightMeasureSpec); // ViewGroup解析的高度
List lineViews =new ArrayList<>(); //保存一行中的所有的view
int lineWidthUsed =0; //记录这行已经使用了多宽的size
int lineHeight =0; // 一行的行高
int parentNeededWidth =0; // measure过程中,子View要求的父ViewGroup的宽
int parentNeededHeight =0; // measure过程中,子View要求的父ViewGroup的高
for (int i =0;i
View childView = getChildAt(i);
LayoutParams childLP = childView.getLayoutParams();
int childWidthMesureSpec =getChildMeasureSpec(widthMeasureSpec,paddingLeft+paddingRight,childLP.width);
int childHeightMesureSpec =getChildMeasureSpec(heightMeasureSpec,paddingBottom+paddingTop,childLP.height);
childView.measure(childWidthMesureSpec,childHeightMesureSpec);
//获取子view的宽高
int childMesuredWidth = childView.getMeasuredWidth();
int childMesuredHeight = childView.getMeasuredHeight();
//通过宽度来判断是否需要换行,通过换行后的每行的行高来获取整个viewGroup的行高
//如果宽度不够,则需换行
if (childMesuredWidth + lineWidthUsed +mHorizontalSpacing > selfWidth){
allLines.add(lineViews);
lineHeights.add(lineHeight);
//一旦换行,可以判断当前所需的宽高了
parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed+mHorizontalSpacing);
parentNeededHeight = parentNeededHeight + lineHeight +mVerticalSpacing;
lineViews =new ArrayList<>();
lineWidthUsed =0;
lineHeight =0;
}
// view 是分行layout的,所以要记录每一行有哪些view,这样可以方便layout布局
lineViews.add(childView);
lineWidthUsed = lineWidthUsed + childMesuredWidth +mHorizontalSpacing;
lineHeight = Math.max(lineHeight,childMesuredHeight);
//childview 最后一行
if (i == childCount -1){
lineHeights.add(lineHeight);
allLines.add(lineViews);
parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed );
parentNeededHeight += lineHeight;
}
//根据子View的度量结果,来重新度量自己ViewGroup
// 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = (widthMode == MeasureSpec.EXACTLY)? selfWidth:parentNeededWidth;
int realHeight = (heightMode == MeasureSpec.EXACTLY)?selfHeight:parentNeededHeight;
setMeasuredDimension(realWidth,realHeight);
isMeasuredOver =true;
}
}
private void initMeasureParams() {
allLines =new ArrayList<>();
lineHeights =new ArrayList<>();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int lineCount =allLines.size();
int curL =0;
int curT =0;
for (int i =0;i
List lineViews =allLines.get(i);
int lineHeight =lineHeights.get(i);
for (int j =0;j
View view = lineViews.get(j);
int left = curL;
int top = curT;
int bottom = top + view.getMeasuredHeight();
int right = left + view.getMeasuredWidth();
view.layout(left,top,right,bottom);
curL = right +mHorizontalSpacing;
}
curL =0;
curT = curT + lineHeight +mVerticalSpacing;
}
}
public static int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
    }}

六 项目运行截图


源码地址:
(https://github.com/doubizhu/flowlayout)

到这里就结束啦。
浏览 117
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报