注:若不要求放到最后一行,只放在FlowLayout的下面,则可以在xml布局放个文本/按钮实现。
思路:FlowLayout流式布局,把固定文本/按钮的数据添加到要绘制的内容最后面,在绘制时,对最后一个进行特别处理样式,事件等。设置不显示固定文本/按钮时,则不需要添加固定文本/按钮的数据到要绘制的内容最后面。
效果图如下(粉色区域就是动态绘制的):
图1:不显示固定文本/按钮,最后一行放得下
图2:显示固定文本/按钮,最后一行放得下,就放在最后一行右边且跟最后一行垂直居中
图3:不显示固定文本/按钮,最后一行放不下,换行
图4:显示固定文本/按钮,最后一行放不下,新开一行放到新行的右边且垂直居中(新行的行高跟前面的一样)
/**
* xml布局时match_parent或者wrap_content,如果只super()不对宽高测量:控件的大小是由父控件决定的,一般就是会填充父容器。
* 对自定义viewGroup的宽高进行测量
* 行宽:新增view,加了后宽度没有超过GroupView最大宽度,则添加;否则换行,新一行行宽就是该view的测量宽度,有行间距时,需要加上行间距
* 行高:本行子最高view高度,没有换行就一直对比新增的view高度,谁大取谁。换行时,行高=换行后第一个view高度。有行间距时需要加上。
* 期望的总高度:height设置wrap_content 时,ViewGroup的上下内间距 + 行高…+行高
* 注:addView、setVisbility、setTextView等方法会重新调用 requestLayout,会重新测量、重新摆放、重新绘制view,影响性能,因此尽量少用
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//对动态绘制的viewGroup宽高进行测量
…………………………………………………………………………………………………………………………………………
}
/**
* 确定控件现在在哪个位置,view.layout(左,上,右,下)
* 布局,计算view起始位置,顶部偏移量,左侧偏移量(每个child的位置(超出onMeasure宽高部分不显示))
* 行高:当前行最高高度,换行时,顶部偏移量需要加上该行行高+行间距,更新新行高的为新view的高度
* child顶部偏移量:view的top位置,同行的顶部偏移量一样;换行时,新行的顶部偏移 = 当前偏移量+当前行高
* child左侧偏移量:view的left位置,新view的左侧偏移距离 = 当前左侧偏移 + 当前子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) {
………………………………………………………………………………………………………………………………
for (int i = 0; i < getChildCount(); i++) {//遍历所以的view,把它们的位置放好
//计算child的左,上,右,下的值
……………………………………………………………………………………………………………………
child.layout(左,上,右,下); //child的位置,child就会在这个位置显示
}
}
单选、多选、取消选中等,重点是保存好每个child选中的情况,及几个数据的索引一致
private List dataList = new ArrayList<>(); //数据源
//动态绘制的flowlayout布局内容textView,可以设置选中,不选中
private List tvList = new ArrayList<>();
/**
* textView 是否选中boolean,方便后面重置、全选,全不选、指定被选等需求.
* 这里的key跟dataList、tvList的索引是一致的,就可以通过dataMap是否选中得到key进而得到dataList具体的内容
* eg:Boolean为true就是选中的,可以得到key是1,那么dataList.get(1)就可以知道具体被选中的string,也可以将tvList.get(1)设置为不选中setSelected(false)
*/
private Map dataMap = new HashMap<>();
private int maxNum;// 0不限制数量,1单选,大于1的限制选择数量
//是否有固定在最后一行右边的按钮/文字。放不下就换行(true即表示有,且默认最后一个数据就是固定的文本/按钮)
private boolean isRightBt = false;
public class FLWCWindow implements View.OnClickListener {
private FrameLayout frameLayout;
private PopupWindow popupWindow;
private Context context;
private View view;
private FlowLayoutWCView flowLayoutWCView;
private Button btnSubmit, btnCancel, bt_selectAll, bt_cancleAll, bt_selectNum;
private TextView fl_Title;//标题
private OnSubmitListener submitListener;//确定按钮事件回调
public FLWCWindow(Context context) {
this.context = context;
initView();//要处理的数据在此之前就要设置好,比如isRightBt,为了用
}
private void initView() {
view = LayoutInflater.from(context).inflate(R.layout.flwc_window, null);
btnCancel = view.findViewById(R.id.btnCancel);
fl_Title = view.findViewById(R.id.tvTitle);
btnSubmit = view.findViewById(R.id.btnSubmit);
flowLayoutWCView = view.findViewById(R.id.flwc_view);
bt_selectAll = view.findViewById(R.id.bt_selectAll);
bt_cancleAll = view.findViewById(R.id.bt_cancleAll);
bt_selectNum = view.findViewById(R.id.bt_selectNum);
btnCancel.setOnClickListener(this);
btnSubmit.setOnClickListener(this);
bt_selectAll.setOnClickListener(this);
bt_cancleAll.setOnClickListener(this);
//时刻监听flowLayoutWCView通过点击选中的情况
flowLayoutWCView.setOnUpdateListener(new FlowLayoutWCView.OnUpdateListener() {
@Override
public void updateSelect(List list) {
if (list != null && list.size() > 0) {
bt_selectNum.setText("选中了" + list.size() + "个");
} else {
bt_selectNum.setText("");
}
}
});
popupWindow = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
popupWindow.setOutsideTouchable(false);//设置外部区域可以点击取消popupWindow
popupWindow.setTouchable(true);//是否可以触摸,false话,整个窗口点击不了
//设置颜色跟背景色一样,某些情况下,会出现间隙,其实这个也是PopupWindow的一部分
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.parseColor("#d8d8d8")));
//设置触摸监听
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//popupWindow背景色灰色,整个窗口黑色0.5透明(蒙版),点击灰色任意地方调用两次此方法(一次蒙版一次popupWindow),点击非灰色调用一次。
return false;
}
});
//设置取消事件监听
popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
removeFrame();//弹窗消失时,移除蒙版
}
});
}
//设置标题,同样的增加“取消”、“确定”文字,及是否显示等方法
public void setFl_Title(String str) {
fl_Title.setText(str);
}
//是否显示固定文本/按钮,若true则特殊处理最后一个TextView的样式、位置
public void isShowRightBt(boolean isRightBt) {
flowLayoutWCView.isShowRightBt(isRightBt);
}
//设置数据
public void setDataList(List dataList){
flowLayoutWCView.setDataList(dataList);//设置数据源
}
//显示window
public void show() {
addFrame();
popupWindow.showAtLocation(view, Gravity.BOTTOM, 0, 0);//显示在底部BOTTOM,可以改成CENTER、TOP等
}
//添加蒙版
private void addFrame() {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frameLayout = new FrameLayout(context);
frameLayout.setBackgroundColor(context.getResources().getColor(R.color.black));//效果是整个窗口的背景色,配合setAlpha使用
frameLayout.setAlpha(0.5f);//透明度
frameLayout.setLayoutParams(layoutParams);
((Activity) context).getWindow().addContentView(frameLayout, layoutParams);//添加蒙版,必须是activity才有getWindow获取窗口方法
}
//移除蒙版
private void removeFrame() {
((ViewGroup) frameLayout.getParent()).removeView(frameLayout);//添加蒙版时的view父布局ViewGroup移除view
}
/**
* 关闭弹框
*/
public void dismiss() {
if (null != popupWindow && popupWindow.isShowing()) {
popupWindow.dismiss();
}
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.btnCancel) {//取消
dismiss();
} else if (i == R.id.btnSubmit) {//确定
//选中的返回
if (submitListener != null && flowLayoutWCView != null) {
submitListener.onSubmit(flowLayoutWCView.selectList());
}
dismiss();
} else if (i == R.id.bt_selectAll) {//全选
flowLayoutWCView.selectAllTv();
bt_selectNum.setText("选中了" + flowLayoutWCView.selectNum() + "个");
} else if (i == R.id.bt_cancleAll) {//重置
flowLayoutWCView.cancleAllSelect();
}
}
//确定按钮事件回调
public interface OnSubmitListener {
void onSubmit(List str);
}
public void setOnSubmitListener(OnSubmitListener submitListener) {
this.submitListener = submitListener;
}
}
/**
* FlowLayout流式布局+固定文本/按钮(在最右边固定位置,若最后一行放得下,那就放,放不下就重新开一行放在新行的最右边)
* 动态绘制的布局大小是LayoutParams.WRAP_CONTENT
*/
public class FlowLayoutWCView extends ViewGroup {
private String TAG = FlowLayoutWCView.class.getSimpleName();
private Context context;
private List dataList = new ArrayList<>(); //数据源
//动态绘制的flowlayout布局内容textView,可以设置选中,不选中
private List tvList = new ArrayList<>();
/**
* textView 是否选中boolean,方便后面重置、全选,全不选、指定被选等需求.
* 这里的key跟dataList、tvList的索引是一致的,就可以通过dataMap是否选中得到key进而得到dataList具体的内容
* eg:Boolean为true就是选中的,可以得到key是1,那么dataList.get(1)就可以知道具体被选中的string,也可以将tvList.get(1)设置为不选中setSelected(false)
*/
private Map dataMap = new HashMap<>();
//单选时,被选中的位置(跟dataList、tvList、dataMap存的顺序是一样的),用于判断是否需要清除选中的这个,-1当前没有选中
private int nowSelectIndex = -1;
private OnUpdateListener onUpdateListener;//选中情况的更新事件回调,每一次点击的情况,返回当前被选中的list
private List selectList;//选中的list内容
private int maxNum;// 0不限制数量,1单选,大于1的限制选择数量
private int textStyle;//text基本样式,字体大小,颜色
private int text_padding_vertical;//上下内边距
private int text_padding_horizontal;//上下内边距
private int text_margins_vertical;//上下外边距
private int text_margins_horizontal;//左右外边距
private int textBg;//文本的背景,需要圆角,选中未选中状态等用drawable文件里面实现,只是背景颜色颜色可以color
//是否有固定在最后一行右边的按钮/文字。放不下就换行(true即表示有,且默认最后一个数据就是固定的文本/按钮)
private boolean isRightBt = false;
// //实例化时调用
// public FlowLayoutWCView(Context context) {
// super(context);
// this.context=context;
// }
//xml中定义会调用
public FlowLayoutWCView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FlowLayoutWCView);
maxNum = ta.getInteger(R.styleable.FlowLayoutWCView_maxNum_wc, 0);//单选,有限制选中,没有限制选择
textStyle = ta.getResourceId(R.styleable.FlowLayoutWCView_text_wc, R.style.default_text_style_wc);//文本的样式,字体大小、颜色这些
textBg = ta.getInteger(R.styleable.FlowLayoutWCView_textBg_wc, R.drawable.flow_layout_wc_style);//需要圆角,选中未选中状态等用drawable,单颜色可以color
text_padding_vertical = ta.getInteger(R.styleable.FlowLayoutWCView_text_padding_vertical, 10);//上下内边距
text_padding_horizontal = ta.getInteger(R.styleable.FlowLayoutWCView_text_padding_horizontal, 10);//上下内边距
text_margins_vertical = ta.getInteger(R.styleable.FlowLayoutWCView_text_margins_vertical, 20);//左右外边距
text_margins_horizontal = ta.getInteger(R.styleable.FlowLayoutWCView_text_margins_horizontal, 20);//左右外边距
ta.recycle();
}
// //在构造函数中主动调用的 defStyleAttr是默认的Style是指它在当前Application或Activity所用的Theme中的默认Style
// public FlowLayoutWCView(Context context, AttributeSet attrs, int defStyleAttr) {
// super(context, attrs, defStyleAttr);
// this.context = context;
// }
/**
* xml布局时match_parent或者wrap_content,如果只super()不对宽高测量:控件的大小是由父控件决定的,一般就是会填充父容器。
* 对自定义viewGroup的宽高进行测量
* 行宽:新增view,加了后宽度没有超过GroupView最大宽度,则添加;否则换行,新一行行宽就是该view的测量宽度,有行间距时,需要加上行间距
* 行高:本行子最高view高度,没有换行就一直对比新增的view高度,谁大取谁。换行时,行高=换行后第一个view高度。有行间距时需要加上。
* 期望的总高度:height设置wrap_content 时,ViewGroup的上下内间距 + 行高…+行高
* 注:addView、setVisbility、setTextView等方法会重新调用 requestLayout,会重新测量、重新摆放、重新绘制view,影响性能,因此尽量少用
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);//宽度测量大小
int height = 0;//动态布局的总高度
int lineWidth = 0;//行宽
int lineHeight = 0;//行高
int childWidth;//子view的宽度
int childHeight;//子view的高度
//循环遍历子View进行测量
for (int i = 0; i < getChildCount(); i++) {//getChildCount()获取child的数量
View child = getChildAt(i);//获取child view
//测量子view的宽高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到child的lp
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
if (lp == null) {
continue;
}
childWidth = child.getMeasuredWidth();//包含child的padding值
childHeight = child.getMeasuredHeight();
//新加子view后,宽度还没有大于widthSize,就是当前行放得下,不用换行
if (lineWidth + childWidth + lp.leftMargin + lp.rightMargin <= widthSize) {
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;//更新行宽,记得加上child的左右外边距
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin);//行高,一直跟新view+上下外边距对比,哪个大要哪个
} else {//换行
height += lineHeight;//动态布局的总高度=换行前那行的行高,新行的行高还需要跟后面的新child对比大小,所以一直缺少最后一行的行高,需要额外处理
lineWidth = childWidth + lp.leftMargin + lp.rightMargin;//新行宽度=child的测量宽度+左右外边距
lineHeight = childHeight + lp.topMargin + lp.bottomMargin;//新行的行高=child的测量宽度+上下外边距
}
//添加最后一行的高度,因为前面是只有换行时才加行高,最后一行的高度没有加上
if (i == getChildCount() - 1) {
height += lineHeight;//总高度加上最后一行行高,不需要加上下外边距,因为行高赋值时已经加过了
}
}
LogUtil.e(TAG, "onMeasure()方法里面的,onLayout方法布局时的位置超过了此宽高的部分不显示,widthSize=" + widthSize + ",height=" + height);
/**
* 模式
* MeasureSpec.UNSPECIFIED:父不约束子view大小; 如ListView; 注 自定义view一般用不到
* MeasureSpec.EXACTLY:父为子指定确切尺寸,子大小必须在改尺寸内; 如match_parent,具体数字50dp
* MeasureSpec.AT_MOST:父为子指定最大尺寸,所以子必须适应在改尺寸内; 如wrap_content
*
* MeasureSpec.getSize(widthMeasureSpec);//宽度测量大小:占测量规格(MeasureSpec)的低30位
* MeasureSpec.getMode(widthMeasureSpec);//宽度测量模式 :占测量规格(MeasureSpec)的高2位
*/
/**因为在xml布局中宽是match_parent,高是wrap_content,所以直接用宽度测量大小,自己测量出来的高度;
*如果不知道模式是什么,那么需要对比了,宽/高测量模式 == MeasureSpec.EXACTLY ? 宽/高测量大小 : 宽/高自己测量出来大小
*/
setMeasuredDimension(widthSize, height);//存储计算得到的宽高
}
/**
* 确定控件现在在哪个位置,view.layout(左,上,右,下)
* 布局,计算view起始位置,顶部偏移量,左侧偏移量(每个child的位置(超出onMeasure宽高部分不显示))
* 行高:当前行最高高度,换行时,顶部偏移量需要加上该行行高+行间距,更新新行高的为新view的高度
* child顶部偏移量:view的top位置,同行的顶部偏移量一样;换行时,新行的顶部偏移 = 当前偏移量+当前行高
* child左侧偏移量:view的left位置,新view的左侧偏移距离 = 当前左侧偏移 + 当前子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 width = r - l;//可布局的宽 getWidth,值都是一样的
//child 左偏移量
int childLeftOffset = 0;
//child 顶部偏移量
int childTopOffset = 0;
int lineWidth = 0;//行宽
int lineHeight = 0;//行高
int childWidth = 0;//子view 的宽
int childHeight = 0;//子view 的高
//遍历view
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
//过滤gone
if (child.getVisibility() == View.GONE) {
continue;
}
//强制转换,可能导致空指针和类型转换异常
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//可以获取边距
if (lp == null) {
continue;
}
//获取child的宽高
childWidth = child.getMeasuredWidth();//包含child的padding值
childHeight = child.getMeasuredHeight();
//左右margin外边距,不换行
if (lineWidth + childWidth + lp.leftMargin + lp.rightMargin <= width) {
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin + lp.bottomMargin);//行高取本行最高的height
} else {//换行了,此时child就是新行的第一个,那么child的宽高就是新行的数据了
childLeftOffset = 0;//初始化
lineWidth = childWidth + lp.leftMargin + lp.rightMargin;//需要加上左右边距
lineHeight = childHeight;
childTopOffset += lineHeight + lp.topMargin + lp.bottomMargin;//换行了,顶部偏移量是childHeight+上下边距(这是没换行的上下边距)
}
//计算childView的left,top,right,bottom
int lc = childLeftOffset + lp.leftMargin;//自身带的左边距+左偏移量=自身左边实际的位置
int tc = childTopOffset + lp.topMargin;//自身带的上边距+顶部偏移量=自身顶部实际的位置
int rc = lc + childWidth;
int bc = tc + childHeight;
//显示固定文本/按钮,且是最后一个,特殊处理,最右边固定位置,若最后一行放得下,那就放,放不下就重新开一行放在新行的最右边(改固定文本/按钮位置,按需改左上右下四个值即可)
if (isRightBt && i == getChildCount() - 1) {
int left = width - childWidth - lp.leftMargin;
int right = left + childWidth;
child.layout(left, tc, right, bc); //child的位置
Log.e(TAG, "child.layout布局" + " , l = " + left + " , t = " + tc + " , r =" + right + " , b = " + bc);
} else {//否则,按照正常的来放位置
child.layout(lc, tc, rc, bc); //child的位置
Log.e(TAG, "child.layout布局" + " , l = " + lc + " , t = " + tc + " , r =" + rc + " , b = " + bc);
}
childLeftOffset += childWidth + lp.leftMargin + lp.rightMargin;//下一个child 的左侧偏移量=childWidth+自己的左右外边距
}
}
/**
* 设置数据
*
* @param list 需要展示的数据
*/
public void setDataList(List list) {
dataList.clear();
tvList.clear();
dataList.addAll(list);
for (int i = 0; i < dataList.size(); i++) {
//初始化所有的点都为未选中
String data = dataList.get(i);
TextView tv = null;
//显示固定文本/按钮,且是最后一个数据,特殊处理最后一个数据,因为最后一个数据固定在最后一行(若放不下就另起一行)右边(具体位置,可以控制)
if (isRightBt && i == dataList.size() - 1) {
tv = lastText(data);
} else {//否则正常的来
dataMap.put(i, false);//tv的选中与否状态,最后一个tv不需要加进来
tv = newTv(data, i);//代码绘制TextView
tvList.add(tv);//后续选中、不选中,最后一个tv不需要加进来
}
addView(tv, i);
}
invalidate();
}
/**
* TextView初始化方法(非固定文本/按钮)
*
* @param dataStr 数据
* @param position 位置
* @return TextView
*/
private TextView newTv(final String dataStr, final int position) {
final TextView tv = new TextView(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.setMargins(text_margins_horizontal, text_margins_vertical, text_margins_horizontal, text_margins_vertical);//外边距
tv.setPadding(text_padding_horizontal, text_padding_vertical, text_padding_horizontal, text_padding_vertical);//内边距
tv.setLayoutParams(lp);
tv.setText(dataStr);
tv.setGravity(Gravity.CENTER);//center
tv.setTextAppearance(context, textStyle);
tv.setBackgroundResource(textBg);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (maxNum == 0) { //无限选择
if (dataMap.get(position)) {//前面就选中了,那么现在取消选中
tv.setSelected(false);
dataMap.put(position, false);
} else {//没有选中
tv.setSelected(true);
dataMap.put(position, true);
}
} else if (maxNum == 1) {//单选
if (dataMap.get(position)) {//前面就选中了,那么现在取消选中
nowSelectIndex = -1;
tv.setSelected(false);
dataMap.put(position, false);
} else {//没有选中,那么取消已选中的,让这个选中
//有选中的,那么取消已选中的
if (nowSelectIndex != -1) {
cancleSelect(nowSelectIndex);
}
nowSelectIndex = position;//单选时,被选中的是第几个
tv.setSelected(true);
dataMap.put(position, true);
}
} else {//有限选择
if (dataMap.get(position)) {//前面就选中了,那么现在取消选中
tv.setSelected(false);
dataMap.put(position, false);
} else {
if (selectNum() < maxNum) {//还没有达到上限
tv.setSelected(true);
dataMap.put(position, true);
} else {//已达到上限,不能再选了
Toast.makeText(context, "已达上限,不能再选“" + dataStr + "”", Toast.LENGTH_SHORT).show();
}
}
}
onUpdateListener.updateSelect(selectList());//监听选中的情况
}
});
return tv;
}
/**
* 为最后一个数据设置另外的样式(宽高\setMargins\setPadding这些跟tv大小相关的设置和newTv一致时方便计算child.layout()),
* 若不一样则最后一个child.layout()四个参数需要根据实际情况重新算)
*
* @param dataStr
* @return
*/
private TextView lastText(String dataStr) {
TextView tv = new TextView(context);
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);//外边距
lp1.setMargins(text_margins_horizontal, text_margins_vertical, text_margins_horizontal, text_margins_vertical);//外边距
tv.setPadding(text_padding_horizontal, text_padding_vertical, text_padding_horizontal, text_padding_vertical);//内边距
tv.setLayoutParams(lp1);
tv.setText(dataStr);
tv.setGravity(Gravity.CENTER);
tv.setTextAppearance(context, textStyle);
tv.setTextColor(Color.BLUE);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
cancleAllSelect();//固定文本/按钮事件,这里是重置,所以是cancleAllSelect()方法,具体什么方法看要求
}
});
return tv;
}
//是否显示固定文本/按钮,若true则特殊处理最后一个TextView的样式、位置
public void isShowRightBt(boolean isRightBt) {
this.isRightBt = isRightBt;
}
/**
* 取消指定选中
*
* @param i
*/
public void cancleSelect(int i) {
if (tvList != null && tvList.size() > i) {//指定的这个数字,必须在tvList里面才行
dataMap.put(i, false);
tvList.get(i).setSelected(false);
} else {
Toast.makeText(context, "指定取消项不存在,请重新选择!", Toast.LENGTH_SHORT).show();
}
}
/**
* 指定取消选中哪些(List中Integer存的值要跟tvList的顺序一致,eg:指定第2,4个不选中,那么list{2,4},就是要tvList的第2,4个更改为未选中状态)
*
* @param list
*/
public void cancleSelect(List list) {
if (tvList != null && list != null) {
for (int i = 0; i < list.size(); i++) {
if (tvList.size() >= list.get(i)) {//指定的这个,要存在
dataMap.put(list.get(i) - 1, false);// tvList.size(),是从0开始的,第几个第几个是从1开始的,所以减1
tvList.get(list.get(i) - 1).setSelected(false);
} else {
}
}
}
}
/**
* 取消全部选中的(置空)
*/
public void cancleAllSelect() {
if (tvList != null && tvList.size() > 0) {
for (int i = 0; i < tvList.size(); i++) {
dataMap.put(i, false);
tvList.get(i).setSelected(false);
}
}
onUpdateListener.updateSelect(selectList());//监听选中的情况
}
/**
* 指定选中哪些(List中Integer存的值要跟tvList的顺序以至,eg:指定第2,4个为选中,那么list{2,4},就是要tvList的第2,4个更改为选中状态)
*
* @param list
*/
public void selectAppointTv(List list) {
if (tvList != null && list != null) {
for (int i = 0; i < list.size(); i++) {
if (tvList.size() >= list.get(i)) {//指定的这个,要存在
dataMap.put(list.get(i) - 1, true);// tvList.size(),是从0开始的,第几个第几个是从1开始的,所以减1
tvList.get(list.get(i) - 1).setSelected(true);
} else {
}
}
}
}
/**
* 指定选中tvList的前几个
*
* @param num
*/
public void selectAppointTv(int num) {
if (tvList != null && tvList.size() >= num) {
for (int i = 0; i < num; i++) {
dataMap.put(i, true);
tvList.get(i).setSelected(true);
}
}
}
/**
* 指定选中tvList的第几个(从1开始)
*
* @param num
*/
public void selectTv(int num) {
if (tvList != null && tvList.size() >= num) {//指定的这个数字,必须在tvList里面才行
dataMap.put(num - 1, true);
tvList.get(num - 1).setSelected(true);
} else {
Toast.makeText(context, "指定选中项不存在,请重新选择!", Toast.LENGTH_SHORT).show();
}
}
/**
* 全部选中(分单选、限制选择、不限制选择的情况)
*/
public void selectAllTv() {
if (tvList != null && tvList.size() > 0) {
if (maxNum == 0) {//没有限制选择,就全部选中
selectAppointTv(tvList.size());
} else if (maxNum == 1) {//单选,就选中第一个(也可以随机选择一个,从tvList.size()-1中随机选一个数字,)
selectAppointTv(1);
} else {//有限制选择
if (tvList.size() >= maxNum) {//能选的数据数量,大于等于限制选的数量,eg:有7个选项,限制选择5个或7个(也可以随机选择maxNum个)
selectAppointTv(maxNum);
} else {//能选的数据数量,小限制选的数量,eg:有7个选项,限制选择8个,此时就全选了
selectAppointTv(tvList.size());
}
}
}
}
/**
* 选中的数量
*
* @return
*/
public int selectNum() {
int num = 0;
for (Map.Entry entry : dataMap.entrySet()) {//类型 : 循环对象 超级循环
if (entry.getValue()) {//获取的是dataMap的value值boolean,key值是Integer
num++;
}
}
return num;
}
/**
* 选中的list
*
* @return
*/
public List selectList() {
selectList = new ArrayList<>();
for (Map.Entry entry : dataMap.entrySet()) {//类型 : 循环对象 超级循环
if (entry.getValue()) {//获取的是dataMap的value值boolean,key值是Integer
selectList.add(dataList.get(entry.getKey()));//dataList存的顺序跟dataMap的key是一致的
}
}
return selectList;
}
/**
* 需要支持margin,所以使用系统的MarginLayoutParams
*/
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attributeSet) {
return new MarginLayoutParams(getContext(), attributeSet);
}
/**
* 设置默认的MarginLayoutParams
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
public interface OnUpdateListener {
void updateSelect(List list);
}
public void setOnUpdateListener(OnUpdateListener listener) {
this.onUpdateListener = listener;
}
}
tfMonthList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
tfMonthList.add("周" + i);
}
tfMonthList.add("重置");//固定文本/按钮,特殊处理的这个,必须放在数据源的最后
flwcWindow = new FLWCWindow(this);
flwcWindow.setFl_Title("周几");
flwcWindow.isShowRightBt(true);//是否显示固定文本/按钮默认是false,也可以通过attrs实现
flwcWindow.setDataList(tfMonthList);//isShowRightBt必须在前面,不然是否显示无效,因为设置数据时就需要判断是否显示固定文本/按钮了
flwcWindow.setOnSubmitListener(new FLWCWindow.OnSubmitListener() {
@Override
public void onSubmit(List str) {
Toast.makeText(FlowLayoutActivity.this,"您一共选中了"+str.size()+"个分别是:"+str.toString(),Toast.LENGTH_SHORT).show();
}
});
flwcWindow.show();