先看下效果图-------------------
首先说一下思路,看到这个设计图的时候首先想到的是流式布局,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 List<FlowLayoutManager.Row> lineRows = new ArrayList();
private SparseArray<Rect> 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<FlowLayoutManager.Item> 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<FlowLayoutManager.Item> 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 List<String> mStrings = 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<String> getStrings() {
List<String> 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<MenuItem> getMenuList() {
List<MenuItem> list = new ArrayList<>();
list.add(new MenuItem(1, "删除"));
return 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<String, BaseViewHolder> {
List<String> data = new ArrayList<>();
public FlowAdapter(int layoutResId, @Nullable List<String> 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);
}
}
}