使用Adapter设计模式打造一个流式布局FlowLayout
来源:互联网 发布:软件开发工作计划范文 编辑:程序博客网 时间:2024/06/08 05:42
流式布局可以说是在各种软件中的出场率都很高的一个布局方式,被广泛使用,像一些关键字搜索,标签等等的场景,更是随处可见,今天我们就来手把手打造一个FlowLayout。
FlowLayout由于是以一个容器的身份存在的,所以其需要继承的是ViewGroup而不是View,也就是说,我们今天所要做的,就是去自定义一个ViewGroup。
自定义ViewGroup和自定义View的套路基本都是一致的,但也有部分差异,下面,我们先来说说自定义ViewGroup的套路的几个关键点:
- 自定义属性(这个就不说了,在之前的文章中已经说了很多次了)
- 实现onMeasure()方法,通过测量每一个子View的宽高,来计算自身的宽高,最后达到测量自身宽高的目的。
- 实现onDraw()方法,默然情况下继承ViewGroup是不会调用该方法的,如果需要绘制一些界面,可以实现dispatchDraw()方法
- 实现onLayout()方法,该方法用于按照自己的想法来摆放可见(不为GONE)的子View。
- 注意点:在确定要自定义ViewGroup的时候,可以先考虑下先继承自一些已经封装好的ViewGroup,如LinearLayout等进行开发。
好了,下面,我们开始自定义FlowLayout
首先,自定义属性这一块,我们就直接忽略了,直接从onMeasure方法开始做起(判断view.visibility != GONE,之前忘加了):
/** * 测量该控件的宽高 * 思路:通过测量每一个子view的宽高 来得到该layout的整体宽高 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * 解决onMeasure被调用多次,导致在摆放子View的时候出现多次摆放的情况 */ mChildViews.clear(); //获取子View的数量 int childCount = getChildCount(); //获取该layout的宽度 以方便控制后面TAG的摆放 和计算该Layout的高度 int width = MeasureSpec.getSize(widthMeasureSpec); //初始化layout的高度 int height = getPaddingTop() + getPaddingBottom(); int lineWidth = getPaddingLeft(); //初始化一个每一行的高度 计算layout的总高度时,取每一行的最高 int maxHeight = 0; ArrayList<View> views = new ArrayList<>(); mChildViews.add(views); //for循环测量子View for(int i = 0;i < childCount; i++){ View childView = getChildAt(i); measureChild(childView,widthMeasureSpec,heightMeasureSpec); ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams(); //当叠加的宽度大于该Layout的总宽度时,则换行 if((lineWidth + childView.getMeasuredWidth() + params.leftMargin + params.rightMargin) > width){ maxHeight = Math.max(maxHeight,childView.getMeasuredHeight() + params.topMargin + params.bottomMargin); height += maxHeight; lineWidth = getPaddingLeft() + childView.getMeasuredWidth() + params.leftMargin + params.rightMargin; views = new ArrayList<>(); mChildViews.add(views); views.add(childView); }else{ maxHeight = Math.max(maxHeight,childView.getMeasuredHeight() + params.topMargin + params.bottomMargin); lineWidth += childView.getMeasuredWidth() + params.leftMargin + params.rightMargin; views.add(childView); } } height += maxHeight; setMeasuredDimension(width,height); }
在计算宽度时,我们需要获取到每个子View的margin值,但是由于ViewGroup本身是没有LayoutParams的,所以这里模仿了下LinearLayout的方法,来获取MarginLayoutParams:
/** * 重写ViewGroup的该方法 获取到margin值(可以模仿LinearLayout) * @param attrs * @returnop */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(),attrs); }
在onMeasure方法中,当几个子View的宽度总和大于FlowLayout的宽度时,这个时候就需要换行。其他的,注释应该都有了!
下一步,就是实现onLayout()方法,来摆放所有的子View(判断view.visibility != GONE,之前忘加了)
“`
/**
* 摆放子view
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int maxHeight,left,right,top,bottom;
top = getPaddingTop();
//循环遍历mChildViews for(ArrayList<View> views : mChildViews){ left = getPaddingLeft(); maxHeight = 0; for(int i = 0;i<views.size();i++){ View childView = views.get(i); ViewGroup.MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams(); maxHeight = Math.max(maxHeight,childView.getMeasuredHeight() + marginLayoutParams.topMargin + marginLayoutParams.bottomMargin); left += marginLayoutParams.leftMargin; right = left + childView.getMeasuredWidth(); int childTop = top + marginLayoutParams.topMargin; bottom = childTop + childView.getMeasuredHeight(); childView.layout(left,childTop,right,bottom); left += marginLayoutParams.rightMargin + childView.getMeasuredWidth(); } top += maxHeight; }}
到这里呢,基本是这个流式布局就完成了一大半了,下面要做的,就是往这个布局里面扔数据,测试其是否会按照我们既定的规则摆放。
在设置数据这一步,我们使用了Adapter的设计模式。下面来看具体的步骤:
1:定义一个抽象的BaseAdapter
/** * Created by DELL on 2017/9/9. * Description : */public abstract class BaseAdapter { //获取view的数量 public abstract int getCount(); //获取View public abstract View getView(int position, ViewGroup parent);}
这边暂时只定义了两个方法,一个是获取子View数量的,另外一个是获取到每一个子View的,暂时没有定义notifySetDataChanged等方法,这个可以根据自己的需求,给其设置一个观察者来处理。
下一步,需要在FloawLayout中去设置Adapter达到添加布局的效果
public void setAdapter(BaseAdapter adapter){ //当Adapter为空的时候,抛出异常 if(adapter == null){ throw new NullPointerException("adapter not null"); } this.mAdapter = adapter; int count = mAdapter.getCount(); for(int i = 0;i<count;i++){ View view = mAdapter.getView(i,this); addView(view); } }
每一个子View的布局:
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/bg_textview" android:padding="10dp" android:layout_margin="5dp" ></TextView>
子view背景代码:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <stroke android:color="@color/colorAccent" android:width="1dp"/> <corners android:radius="8dp"/></shape>
下面,在Activity中设置Adapter,来测试下效果:
public class MainActivity extends AppCompatActivity { private FlowLayout mFlowLayout; private ArrayList<String> mStrList; private LayoutInflater mLayoutInflater; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); } private void initData(){ mStrList = new ArrayList<>(); mStrList.add("JUSTH HELLO!"); mStrList.add("JUSTH HEJUSTH HELLO!LLO!"); mStrList.add("JUSTH LLO!"); mStrList.add("JUSTH HEJUSTH HELLO!LLO!"); mStrList.add("JUSTH HELLO!"); mStrList.add("JUSTLO!"); mStrList.add("JUSTH HELLO!"); mStrList.add("JUSTH HEJUSTH HELLO!LLO!"); mStrList.add("JUSTH HELO!"); mStrList.add("JUSTH HELLO!"); } private void initView() { mFlowLayout = (FlowLayout) findViewById(R.id.taglayout); mLayoutInflater = LayoutInflater.from(this); mFlowLayout.setAdapter(new BaseAdapter() { @Override public int getCount() { return mStrList.size(); } @Override public View getView(int position, ViewGroup parent) { View view = mLayoutInflater.inflate(R.layout.layout_textview,parent,false); ((TextView)view.findViewById(R.id.item_textview)).setText(mStrList.get(position)); return view; } }); }}
最后,来一波效果图吧:
- 使用Adapter设计模式打造一个流式布局FlowLayout
- 流式布局FlowLayout使用
- 流式布局之FlowLayout使用
- Flowlayout流式布局的使用步骤
- Flowlayout流式布局使用(轻量级)
- FlowLayout流式布局
- FlowLayout流式布局
- 流式布局FlowLayout
- 流式布局FlowLayout
- 流式布局 FlowLayout
- FlowLayout,流式布局
- (android)自己写一个流式布局吧(FlowLayout)
- UIcollectionView flowlayout 流式布局
- Android 流式布局FlowLayout
- Android流式布局-FlowLayout
- 自定义流式布局FlowLayout
- Android流式布局FlowLayout
- Android:FlowLayout流式布局
- JavaScript 操作 Cookie
- 欢迎使用CSDN-markdown编辑器
- 使用PL/SQL内置的DBMS_SQL包执行动态SQL
- ubuntu 12.04 下DHCP网络设置——链接外网(NAT模式)
- 4
- 使用Adapter设计模式打造一个流式布局FlowLayout
- Android中获得屏幕宽高的实用方法
- 抽象类和接口的区别
- leetcode 306. Additive Number
- 大型网站架构之分布式消息队列
- 包 package
- [Lintcode] 105. Copy List with Random Pointer
- 数据结构与算法常见笔试题
- 百练1321:棋盘问题