流式布局,相信很多项目中都有这样的需求,比如各大软件的标签呀,小说,热搜等,网上现成的有很多,不过普通的流布局并不难,今天闲来无事,我们就来实现一下。
下面的这种效果就是典型的流式布局。
代码如下
public class TagLayout extends ViewGroup{
private TagAdapter mAdapter;
public TagLayout(Context context) {
this(context,null);
}
public TagLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int width=0;
int height=0;
//一行的宽度
int lineWidth=0;
//一行的高度
int lineHeight=0;
//子控件的数目
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
//获取子控件
View childAt = getChildAt(i);
//测量
measureChild(childAt,widthMeasureSpec,heightMeasureSpec);
//得到margin
MarginLayoutParams lp= (MarginLayoutParams) childAt.getLayoutParams();
//子控件的宽度
int childWidth = childAt.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
//子控件的高度
int childHeight = childAt.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
//大于宽度换行
if(lineWidth+childWidth>widthSize){
//宽度
width=Math.max(lineWidth,childWidth);
lineWidth=childWidth;
//高度
height+=lineHeight;
lineHeight=childHeight;
}else{
lineWidth+=childWidth;
lineHeight=Math.max(lineHeight,childHeight);
}
//如果是最后一个
if(i==childCount-1){
width=Math.max(width,lineWidth);
height+=lineHeight;
}
}
//设置进去
setMeasuredDimension((modeWidth==MeasureSpec.EXACTLY)?widthSize:width
,(modeHeight==MeasureSpec.EXACTLY)?heightSize:height
);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int lineWidth=0;
int lineHeight=0;
int left=0,top=0;
int measuredWidth = getMeasuredWidth();
for(int i=0;i<childCount;i++){
//获取子控件
View childView = getChildAt(i);
//如果子控件不可见跳出循环
if(childView.getVisibility()==GONE){
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) childView
.getLayoutParams();
int childWidth = childView.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
int childHeight = childView.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
if(lineWidth+childWidth> measuredWidth){
//如果换行,高度叠加,left从左开始
top += lineHeight;
left = 0;
//初始化lineHeight和lineWidth
lineHeight = childHeight;
lineWidth = childWidth;
}else {
lineHeight = Math.max(lineHeight,childHeight);
lineWidth += childWidth;
}
//算出child的四个点
int cl=left+lp.leftMargin;
int ct=top+lp.topMargin;
int cr=cl+childView.getMeasuredWidth();
int cb=ct+childView.getMeasuredHeight();
//摆放child的位置
childView.layout(cl,ct,cr,cb);
//更新left的位置,把下一个子控件的位置作为起点
left+=childWidth;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
/**
* 设置设配器
* @param adapter
*/
public void setAdapter(TagAdapter adapter){
if(adapter==null){
throw new NullPointerException("TagAdapter不能为空");
}
removeAllViews();
mAdapter = adapter;
int itemCount = mAdapter.getItemCount();
for(int i=0;i<itemCount;i++){
View childView = mAdapter.getView(i, this);
addView(childView);
}
}
}
//创建适配器
public abstract class TagAdapter {
public abstract int getItemCount();
public abstract View getView(int position, ViewGroup parent);
}
//MainActivity
public class MainActivity extends AppCompatActivity {
private TagLayout tagLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tagLayout = (TagLayout) findViewById(R.id.tagview);
tagLayout.setAdapter(new MyAdapter(getDatas()));
}
/**
* adapter
*/
class MyAdapter extends TagAdapter{
private List<TagBean> datas;
public MyAdapter(List<TagBean> datas){
this.datas = datas;
}
@Override
public int getItemCount() {
return datas.size();
}
@Override
public View getView(int position, ViewGroup parent) {
TextView itmeView = (TextView) LayoutInflater.from(MainActivity.this).inflate(R.layout.tag_item, parent, false);
itmeView.setText(datas.get(position).getSrc());
GradientDrawable drawable=new GradientDrawable();
drawable.setStroke(1,Color.parseColor("#99541D"));
drawable.setCornerRadius(5);
drawable.setColor(datas.get(position).getBgColor());
itmeView.setBackgroundDrawable(drawable);
//设置点击事件
itmeView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,datas.get(position).getSrc(),Toast.LENGTH_SHORT).show();
}
});
return itmeView;
}
}
public List<TagBean> getDatas() {
List<TagBean> datas=new ArrayList<>();
datas.add(new TagBean(Color.parseColor("#F8F2EC"),"微信"));
datas.add(new TagBean(Color.parseColor("#F8EAEB"),"百度糯米"));
datas.add(new TagBean(Color.parseColor("#F2F2F2"),"qq"));
datas.add(new TagBean(Color.parseColor("#F2F5E9"),"大众点评"));
datas.add(new TagBean(Color.parseColor("#F2F5E9"),"同程旅游"));
datas.add(new TagBean(Color.parseColor("#F2F5E9"),"爱奇艺"));
datas.add(new TagBean(Color.parseColor("#F8F2EC"),"贝贝"));
datas.add(new TagBean(Color.parseColor("#F8EAEB"),"万能钥匙"));
datas.add(new TagBean(Color.parseColor("#E8F6F6"),"驾校一点通"));
datas.add(new TagBean(Color.parseColor("#F8F2EC"),"天天快报"));
datas.add(new TagBean(Color.parseColor("#E8F6F6"),"蘑菇街"));
return datas;
}
}
效果图
总结
流式布局的实现并不复杂,在这里使用对象Adapter设计模式,可以很好的拥抱变化,我们不用在关心Ui层是什么样子,只需知道是view即可,从而达到无限适配,拥抱变化的目的。