先看下效果图-------------------
首先说一下思路,看到这个设计图的时候首先想到的是流式布局,flowlayou。这个方法可以实现,但是在做删除处理的时候有点难度(也可以实现),后来我就想如果是个recuyclerview就好操作了,于是有了接下来的自定义layoutmanager。
先看一下layoutmanager代码如下:::
// // (powered by Fernflower decompiler) // package com.library.flowlayout; import android.graphics.Rect; import android.support.v7.widget.RecyclerView.LayoutManager; import android.support.v7.widget.RecyclerView.LayoutParams; import android.support.v7.widget.RecyclerView.Recycler; import android.support.v7.widget.RecyclerView.State; import android.util.Log; import android.util.SparseArray; import android.view.View; import java.util.ArrayList; import java.util.List; public class FlowLayoutManager extends LayoutManager { private static final String TAG = FlowLayoutManager.class.getSimpleName(); final FlowLayoutManager self = this; protected int width; protected int height; private int left; private int top; private int right; private int usedMaxWidth; private int verticalScrollOffset = 0; protected int totalHeight = 0; private FlowLayoutManager.Row row = new FlowLayoutManager.Row(); private ListlineRows = new ArrayList(); private SparseArray allItemFrames = new SparseArray(); public int getTotalHeight() { return this.totalHeight; } public FlowLayoutManager() { this.setAutoMeasureEnabled(true); } public LayoutParams generateDefaultLayoutParams() { return new LayoutParams(-2, -2); } public void onLayoutChildren(Recycler recycler, State state) { Log.d(TAG, "onLayoutChildren"); if (this.getItemCount() == 0) { this.detachAndScrapAttachedViews(recycler); this.verticalScrollOffset = 0; } else if (this.getChildCount() != 0 || !state.isPreLayout()) { this.detachAndScrapAttachedViews(recycler); if (this.getChildCount() == 0) { this.width = this.getWidth(); this.height = this.getHeight(); this.left = this.getPaddingLeft(); this.right = this.getPaddingRight(); this.top = this.getPaddingTop(); this.usedMaxWidth = this.width - this.left - this.right; } this.totalHeight = 0; int cuLineTop = this.top; int cuLineWidth = 0; int maxHeightItem = 0; this.row = new FlowLayoutManager.Row(); this.lineRows.clear(); this.allItemFrames.clear(); this.removeAllViews(); for(int i = 0; i < this.getItemCount(); ++i) { Log.d(TAG, "index:" + i); View childAt = recycler.getViewForPosition(i); if (8 != childAt.getVisibility()) { this.measureChildWithMargins(childAt, 0, 0); int childWidth = this.getDecoratedMeasuredWidth(childAt); int childHeight = this.getDecoratedMeasuredHeight(childAt); int itemLeft; Rect frame; if (cuLineWidth + childWidth <= this.usedMaxWidth) { itemLeft = this.left + cuLineWidth; frame = (Rect)this.allItemFrames.get(i); if (frame == null) { frame = new Rect(); } frame.set(itemLeft, cuLineTop, itemLeft + childWidth, cuLineTop + childHeight); this.allItemFrames.put(i, frame); cuLineWidth += childWidth; maxHeightItem = Math.max(maxHeightItem, childHeight); this.row.addViews(new FlowLayoutManager.Item(childHeight, childAt, frame)); this.row.setCuTop((float)cuLineTop); this.row.setMaxHeight((float)maxHeightItem); } else { this.formatAboveRow(); cuLineTop += maxHeightItem; this.totalHeight += maxHeightItem; itemLeft = this.left; frame = (Rect)this.allItemFrames.get(i); if (frame == null) { frame = new Rect(); } frame.set(itemLeft, cuLineTop, itemLeft + childWidth, cuLineTop + childHeight); this.allItemFrames.put(i, frame); cuLineWidth = childWidth; maxHeightItem = childHeight; this.row.addViews(new FlowLayoutManager.Item(childHeight, childAt, frame)); this.row.setCuTop((float)cuLineTop); this.row.setMaxHeight((float)childHeight); } if (i == this.getItemCount() - 1) { this.formatAboveRow(); this.totalHeight += maxHeightItem; } } } this.totalHeight = Math.max(this.totalHeight, this.getVerticalSpace()); this.fillLayout(recycler, state); } } private void fillLayout(Recycler recycler, State state) { if (!state.isPreLayout()) { Rect displayFrame = new Rect(this.getPaddingLeft(), this.getPaddingTop() + this.verticalScrollOffset, this.getWidth() - this.getPaddingRight(), this.verticalScrollOffset + (this.getHeight() - this.getPaddingBottom())); for(int j = 0; j < this.lineRows.size(); ++j) { FlowLayoutManager.Row row = (FlowLayoutManager.Row)this.lineRows.get(j); float lineTop = row.cuTop; float lineBottom = lineTop + row.maxHeight; List views; int i; View scrap; if (lineTop < (float)displayFrame.bottom && (float)displayFrame.top < lineBottom) { views = row.views; for(i = 0; i < views.size(); ++i) { scrap = ((FlowLayoutManager.Item)views.get(i)).view; this.measureChildWithMargins(scrap, 0, 0); this.addView(scrap); Rect frame = ((FlowLayoutManager.Item)views.get(i)).rect; this.layoutDecoratedWithMargins(scrap, frame.left, frame.top - this.verticalScrollOffset, frame.right, frame.bottom - this.verticalScrollOffset); } } else { views = row.views; for(i = 0; i < views.size(); ++i) { scrap = ((FlowLayoutManager.Item)views.get(i)).view; this.removeAndRecycleView(scrap, recycler); } } } } } private void formatAboveRow() { List views = this.row.views; for(int i = 0; i < views.size(); ++i) { FlowLayoutManager.Item item = (FlowLayoutManager.Item)views.get(i); View view = item.view; int position = this.getPosition(view); if ((float)((Rect)this.allItemFrames.get(position)).top < this.row.cuTop + (this.row.maxHeight - (float)((FlowLayoutManager.Item)views.get(i)).useHeight) / 2.0F) { Rect frame = (Rect)this.allItemFrames.get(position); if (frame == null) { frame = new Rect(); } frame.set(((Rect)this.allItemFrames.get(position)).left, (int)(this.row.cuTop + (this.row.maxHeight - (float)((FlowLayoutManager.Item)views.get(i)).useHeight) / 2.0F), ((Rect)this.allItemFrames.get(position)).right, (int)(this.row.cuTop + (this.row.maxHeight - (float)((FlowLayoutManager.Item)views.get(i)).useHeight) / 2.0F + (float)this.getDecoratedMeasuredHeight(view))); this.allItemFrames.put(position, frame); item.setRect(frame); views.set(i, item); } } this.row.views = views; this.lineRows.add(this.row); this.row = new FlowLayoutManager.Row(); } public boolean canScrollVertically() { return true; } public int scrollVerticallyBy(int dy, Recycler recycler, State state) { Log.d("TAG", "totalHeight:" + this.totalHeight); int travel = dy; if (this.verticalScrollOffset + dy < 0) { travel = -this.verticalScrollOffset; } else if (this.verticalScrollOffset + dy > this.totalHeight - this.getVerticalSpace()) { travel = this.totalHeight - this.getVerticalSpace() - this.verticalScrollOffset; } this.verticalScrollOffset += travel; this.offsetChildrenVertical(-travel); this.fillLayout(recycler, state); return travel; } private int getVerticalSpace() { return this.self.getHeight() - this.self.getPaddingBottom() - this.self.getPaddingTop(); } public int getHorizontalSpace() { return this.self.getWidth() - this.self.getPaddingLeft() - this.self.getPaddingRight(); } public class Row { float cuTop; float maxHeight; List views = new ArrayList(); public Row() { } public void setCuTop(float cuTop) { this.cuTop = cuTop; } public void setMaxHeight(float maxHeight) { this.maxHeight = maxHeight; } public void addViews(FlowLayoutManager.Item view) { this.views.add(view); } } public class Item { int useHeight; View view; Rect rect; public void setRect(Rect rect) { this.rect = rect; } public Item(int useHeight, View view, Rect rect) { this.useHeight = useHeight; this.view = view; this.rect = rect; } } } 下面是Mainactivity代码就非常简单了 :::
package com.example.taglayout; import android.content.Intent; import android.graphics.Color; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.library.flowlayout.FlowLayoutManager; import com.library.flowlayout.SpaceItemDecoration; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { public static final String TAG = MainActivity.class.getSimpleName(); private RecyclerView lv; private FlowAdapter mAdapter; private ListmStrings = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { View view = LayoutInflater.from(this).inflate(R.layout.footer, null, false); lv = (RecyclerView) findViewById(R.id.lv); FlowLayoutManager flowLayoutManager = new FlowLayoutManager(); //LinearLayoutManager linearLayoutManager=new LinearLayoutManager()// // GridLayoutManager gridLayoutManager=new GridLayoutManager(this,5); //设置每一个item间距 lv.setLayoutManager(flowLayoutManager); mAdapter = new FlowAdapter(R.layout.tv, getStrings()); // mAdapter.addFooterView(view); // mAdapter.setFooterView(view); // mAdapter.setFooterViewAsFlow(true); lv.setAdapter(mAdapter); mAdapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() { @Override public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) { // mStrings.remove(position); // // mAdapter.replaceData(mStrings); // onClickPopWhite(view); return false; } }); } private List getStrings() { List data = new ArrayList<>(); data.add("Android"); data.add("ios"); data.add("德玛西亚"); data.add("小甜甜"); data.add("西南航空大学"); data.add("成都机场"); data.add("Android"); data.add("ios"); data.add("德玛西亚"); data.add("小甜甜"); data.add("西南航空大学"); data.add("成都机场"); data.add("添加"); mStrings = data; return data; } //带有三角号的下拉菜单框 仿qq public void onClickPopWhite(View view) { DropPopMenu dropPopMenu = new DropPopMenu(this); dropPopMenu.setTriangleIndicatorViewColor(Color.WHITE); dropPopMenu.setBackgroundResource(R.drawable.bg_drop_pop_menu_white_shap); dropPopMenu.setItemTextColor(Color.BLACK); dropPopMenu.setOnItemClickListener(new DropPopMenu.OnItemClickListener() { @Override public void onItemClick(AdapterView> adapterView, View view, int position, long id, MenuItem menuItem) { // Toast.makeText(this, "点击了 " + menuItem.getItemId(), Toast.LENGTH_SHORT).show(); } }); dropPopMenu.setMenuList(getMenuList()); dropPopMenu.show(view); } private List
///最后提示一句如果是可以动态添加item中的布局应该是一个edittext而不是textview并且edittext是可以点击吗的但是不可编辑
只有最后一个item是可以编辑的 adapter代码如下 :::
package com.example.taglayout; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.EditText; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import java.util.ArrayList; import java.util.List; //作者: 冯浩 on 2018/9/12 14:06 // _ooOoo_ // o8888888o // 88" . "88 // (| ^_^ |) // O\ = /O // ____/`---'\____ // .' \\| |// `. // / \\||| : |||// \ // / _||||| -:- |||||- \ // | | \\\ - /// | | // | \_| ''\---/'' | | // \ .-\__ `-` ___/-. / // ___`. .' /--.--\ `. . ___ // ."" '< `.___\_<|>_/___.' >'"". // | | : `- \`.;`\ _ /`;.`/ - ` : | | // \ \ `-. \_ __\ /__ _/ .-` / / // ========`-.____`-.___\_____/___.-`____.-'======== // `=---=' // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // 佛祖保佑 永无BUG 永不修改 // 程序员之歌 作者:冯浩(字:德明) // 十年生死两茫茫 ,写程序,到天亮。 // 千行代码 ,Bug何处藏 ? // 纵使上线又怎样 ,朝令改 ,夕断肠。 // // 领导每天新想法 ,日日改 ,夜夜忙。 // 相顾无言 ,唯有泪千行。 // 每晚灯火阑珊处 ,夜难寐 ,加班狂。 // ************************************************************************************************ class FlowAdapter extends BaseQuickAdapter{ List data = new ArrayList<>(); public FlowAdapter(int layoutResId, @Nullable List data) { super(layoutResId, data); this.data = data; } @Override protected void convert(BaseViewHolder helper, String item) { EditText view =(EditText) helper.getView(R.id.tv); if (data.size() - 1 == helper.getPosition()) { helper.getView(R.id.tv).setEnabled(true); view.setHint("添加"); }else { helper.addOnLongClickListener(R.id.tv); helper.setText(R.id.tv, item); view.setCursorVisible(false); view.setFocusable(false); view.setFocusableInTouchMode(false); } } }