自定义View继承ViewGroup,那么必须重新onLayout方法,确定每一个子元素的位置信息,重写onMeasure方法是测量每个子元素的大小,否则也是无法显示的,
getLayoutParams 是获取布局文件中配置的高度,只有设置具体的高度才能获取到,如果设置成wrap_content 或者是match_parent是获取不到的(这里是在onMeasure方法中进行获取的话是获取不到的)
之前对自定义的measure和layout理解的不是很深刻,最近项目不是很忙,就学习学习这块,写一个Demo,效果图如下:
这个自定义控件思路是:
1、确定哪些元素属于第一行,哪些元素属于第二行。。将所有的行对象存储在lineList里面去
2、根据lineList里面的内容来计算出MyFlowLayout的高度
3、将行对象里面的view对象,进行位置确定,让他们从左到右一字排开
4、细节的修改
onMeasure会被执行多次
别忘了最后一行
每一行的右边需要对齐,需要进行行里面每一个View对象的重新测量
考虑padding的情况
当一行中元素高度不一致的情况之下,这一行的高度应该以最高的元素为主,其他的元素居中显示
自定义控件代码如下:
public class MyFlow extends ViewGroup {
public MyFlow(Context context) {
this(context,null);
}
public MyFlow(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyFlow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private int mUsedWidth = 0;
private Line mCurrentLine;
private ArrayList<Line> lineList = new ArrayList<>();
//水平间距
private int mHorizontalSpacing = UiUtils.dip2px(10);
//垂直间距
private int mVerticalSpacing = UiUtils.dip2px(10);
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
reset();
//获取控件的宽度
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
widthSize = widthSize - getPaddingLeft() - getPaddingRight();
//这种方式有可能是为0
System.out.println("widthSize :" + widthSize);
int count = getChildCount();
mCurrentLine = new Line();
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
//测量给予约束,宽度最多只有屏幕的宽度
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
childView.measure(childWidthMeasureSpec,0);
//获取childView的宽度
int childWidth = childView.getMeasuredWidth();
mUsedWidth = mUsedWidth + childWidth;
if (mUsedWidth < widthSize){
//表示在同一行
mCurrentLine.addLineView(childView);
mUsedWidth = mUsedWidth + mHorizontalSpacing;
}else {
//表示需要换行
newLine();
mCurrentLine.addLineView(childView);
mUsedWidth = mUsedWidth + childWidth + mHorizontalSpacing;
}
}
//添加最后一个元素
if (!lineList.contains(mCurrentLine)){
lineList.add(mCurrentLine);
}
System.out.println("lineList.size() :" + lineList.size());
int totalHeight = 0;
for (int i = 0; i < lineList.size(); i++) {
Line line = lineList.get(i);
totalHeight = totalHeight + line.lineHeight + mVerticalSpacing;
}
//减去最后一行的高度间隔
totalHeight = totalHeight - mVerticalSpacing;
totalHeight = totalHeight + getPaddingTop() + getPaddingBottom();
//确定控件的高度
heightMeasureSpec = MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void reset() {
lineList.clear();
mUsedWidth = 0;
}
private void newLine() {
//把装
lineList.add(mCurrentLine);
//重新创建装载这行view的对象
mCurrentLine = new Line();
//把已经用的mUsedWidth置位0
mUsedWidth = 0;
}
class Line{
public int lineHeight;
private ArrayList<View> lineViews = new ArrayList<>();
public void addLineView(View childView){
if (!lineViews.contains(childView)){
lineViews.add(childView);
if (lineHeight < childView.getMeasuredHeight()){
lineHeight = childView.getMeasuredHeight();
}
}
}
public void layout(int left,int top){
for (int i = 0; i < lineViews.size(); i++) {
View childView = lineViews.get(i);
System.out.println("measuredWidth :" + childView.getMeasuredWidth());
childView.layout(left,top,left + childView.getMeasuredWidth(),top + childView.getMeasuredHeight());
left = left + childView.getMeasuredWidth() + mHorizontalSpacing;
// int topOffset = lineHeight/2 - childView.getMeasuredHeight()/2;
// System.out.println("topOffset :" + topOffset);
// childView.layout(left,top+topOffset,left + childView.getMeasuredWidth(),top+topOffset+childView.getMeasuredHeight());
// left = left + childView.getMeasuredWidth() + mHorizontalSpacing;
}
}
//这个是均等份从新测量
public void reMeasure(){
int surplusWidth = 0;//剩余部分宽度
int totalWidth = 0;//总共所占宽度
int equalWidth = 0;//均等份宽度
for (int i = 0; i < lineViews.size(); i++) {
View childView = lineViews.get(i);
totalWidth = totalWidth + childView.getMeasuredWidth() + mHorizontalSpacing;
}
totalWidth = totalWidth - mHorizontalSpacing;
surplusWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalWidth;
equalWidth = surplusWidth / lineViews.size();
for (int i = 0; i < lineViews.size(); i++) {
View childView = lineViews.get(i);
int measuredWidth = childView.getMeasuredWidth();
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth + equalWidth, MeasureSpec.EXACTLY);
int measuredHeight = childView.getMeasuredHeight();
int measuredHeightSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.AT_MOST);
childView.measure(widthMeasureSpec,measuredHeightSpec);
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int top = getPaddingTop();
int left = getPaddingLeft();
for (int i = 0; i < lineList.size(); i++) {
Line line = lineList.get(i);
line.reMeasure();//在摆放之前从新测量
line.layout(left,top);//确定摆放的位置
top = top + line.lineHeight + mVerticalSpacing;
}
}
}
所用到的工具类代码:
public class UiUtils {
//获取全局Context对象
public static Context getContext() {
return MyApplication.instance.context;
}
public static int getColor(int resId) {
return getContext().getResources().getColor(resId);
}
//产生随机的颜色值 90~230
public static int getRandomColor() {
Random random= new Random();
int red = 90 + random.nextInt(141);;
int green= 90 + random.nextInt(141);;
int blue= 90 + random.nextInt(141);;
int color = Color.rgb(red, green, blue);
return color;
}
//获取颜色的状态选择器
public static ColorStateList getColorStateList(int resId) {
return getContext().getResources().getColorStateList(resId);
}
public static int getDimen(int resId) {
return getContext().getResources().getDimensionPixelSize(resId);
}
//dip2px
public static int dip2px(int dip) {
//屏幕密度
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f);
}
//px2dip
public static int px2dip(int px) {
//屏幕密度
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (px/density + 0.5f);
}
//代码中创建shape标签对应的对象
public static GradientDrawable getShape(int radius,int color) {
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
gradientDrawable.setCornerRadius(radius);
gradientDrawable.setColor(color);
return gradientDrawable;
}
//代码中获取一个状态选择器 对应的类StateListDrawable
public static StateListDrawable getSelector(Drawable pressedDrawable,Drawable normalDrawable) {
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed},pressedDrawable);
stateListDrawable.addState(new int[]{},normalDrawable);
return stateListDrawable;
}
public static int getScreenWidth() {
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
return displayMetrics.widthPixels;
}
}
MainActivity中代码:
public class CustomActivity extends AppCompatActivity {
private ArrayList<String> arrayList = new ArrayList<>();
private ScrollView scrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom);
MyFlow myFlowLayout = new MyFlow(this);
scrollView = findViewById(R.id.scrollView);
myFlowLayout.setPadding(UiUtils.dip2px(5),UiUtils.dip2px(5),UiUtils.dip2px(5),UiUtils.dip2px(5));
arrayList.add("QQ");
arrayList.add("腾讯");
arrayList.add("爱奇艺");
arrayList.add("Boss直聘");
arrayList.add("智联招聘");
arrayList.add("火狐浏览器");
arrayList.add("百度网盘");
arrayList.add("微信");
arrayList.add("支付宝");
arrayList.add("虾米音乐-听见不同");
arrayList.add("蚂蚁财富");
arrayList.add("鲁大师评测");
arrayList.add("盛宏必思恩公司");
arrayList.add("QQ飞车");
arrayList.add("UC浏览器");
arrayList.add("爱奇艺视频");
arrayList.add("火狐");
arrayList.add("Google地图");
arrayList.add("百度地图");
arrayList.add("盛宏电气");
arrayList.add("美图秀秀");
arrayList.add("拉钩");
arrayList.add("TCL");
arrayList.add("诚迈科技");
arrayList.add("钉钉");
arrayList.add("腾讯视频");
MyFlow myFlow = new MyFlow(this);
for (int i = 0; i < arrayList.size(); i++) {
TextView textView = new TextView(this);
;
//press
UiUtils.getShape(UiUtils.dip2px(5),UiUtils.getRandomColor());
//nomal
UiUtils.getShape(UiUtils.dip2px(5),Color.rgb(55,55,55));
StateListDrawable selector = UiUtils.getSelector(UiUtils.getShape(UiUtils.dip2px(5), Color.rgb(55, 55, 55)),UiUtils.getShape(UiUtils.dip2px(5), UiUtils.getRandomColor()));
textView.setTag(arrayList.get(i));
textView.setOnClickListener(listener);
textView.setBackgroundDrawable(selector);
textView.setTextColor(Color.WHITE);
textView.setText(arrayList.get(i));
textView.setTextSize(UiUtils.dip2px(10));
textView.setGravity(Gravity.CENTER);
textView.setPadding(UiUtils.dip2px(5),UiUtils.dip2px(5),UiUtils.dip2px(5),UiUtils.dip2px(5));
myFlowLayout.addView(textView);
}
scrollView.addView(myFlowLayout);
}
private View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
String msg = (String) v.getTag();
UiUtils.toast(msg);
}
};
}