可换行的AutoBreakViewGroup扩展
前段时间一位开发安卓的小伙伴问我一个需求,公司UI妹子给了他一张图,一个加号,一个减号,动态的添加标签,要求流布局的效果实现,考虑到不要重复的造*,就在网上找流布局的自定义view,纵里寻她千百度,那view却在http://blog.csdn.net/syusikoku/article/details/52260811处。
自定义ViewGroup实现换行
于是怀着无比崇拜的心情开始了需求的实现:由于该博客已经写得非常详细,并且知识点也不是很难,直接上代码
布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.bobo.AutoBreakViewGroup android:id="@+id/autoBreakViewGroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp"/> </LinearLayout>考虑使用封装管理的需求,加号和减号封装view中,代码如下:
import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; /** * Created by bobo on 2017/5/8. */ public class OperateView extends FrameLayout { Context mContext; public OperateView(Context context) { super(context); init(context,null); } public OperateView(Context context, AttributeSet attrs) { super(context, attrs); init(context,attrs); } public OperateView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context,attrs); } private void init(Context context, AttributeSet attrs){ mContext = context; View view = LayoutInflater.from(context).inflate(R.layout.view_content_ope, this, true); } }view_content_ope.xml的代码如下开始的时候不用删除,所以减号隐藏掉:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv_add" android:layout_width="45dp" android:layout_height="wrap_content" android:gravity="center" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:background="@drawable/item_bg" android:layout_centerInParent="true" android:text="+"/> <TextView android:id="@+id/tv_delete" android:visibility="gone" android:layout_width="45dp" android:layout_height="wrap_content" android:gravity="center" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:layout_marginBottom="10dp" android:background="@drawable/item_bg" android:layout_centerInParent="true" android:text="-"/> </LinearLayout>好了,准备工作结束开始代码撸起首先把操作view加到viewgroup中关键代码如下:
autoBreakViewGroup= (AutoBreakViewGroup) findViewById(R.id.autoBreakViewGroup); //把加号和减号封装到一个LinearLayout中 OperateView operateView=new OperateView(MainActivity.this); //找到加号和减号的控件 add= (TextView) operateView.findViewById(R.id.tv_add); delete= (TextView) operateView.findViewById(R.id.tv_delete); //刚开始把加号加到viewgroup中去 autoBreakViewGroup.addView(operateView); //添加事件 initEvent();为加号和减号注册点击事件如下:
private void initEvent() { add.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //点击加号后,先生成一个edittext,在里面进行编辑 final EditText editText = new EditText(MainActivity.this); editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); editText.setTextColor(Color.BLACK); editText.setLayoutParams(new ViewGroup.LayoutParams(300, ViewGroup.LayoutParams.WRAP_CONTENT)); editText.setBackgroundResource(R.drawable.item_bg); editText.setPadding(20, 10, 20, 10); //editText.setGravity(Gravity.CENTER); //设置软键盘的换行键不换行 editText.setLines(1); editText.setImeOptions(EditorInfo.IME_ACTION_SEND); editText.setFocusable(true); editText.setFocusableInTouchMode(true); editText.requestFocus(); InputMethodManager inputManager = (InputMethodManager)editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInput(editText, 0); //把edittext添加到加号之前去 autoBreakViewGroup.addView(editText, autoBreakViewGroup.getChildCount() - 1); editText.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER) { // 先隐藏键盘 ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow( ZDYIosActivity.this .getCurrentFocus() .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); //进行标签的添加操作,首先先把先前创建的edittext从viewgroup中去掉 autoBreakViewGroup.removeViewAt(autoBreakViewGroup.getChildCount() - 2); //标签的生成 addView(editText.getText().toString()); } return false; } }); } }); delete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //点击减号后的思路 int count=autoBreakViewGroup.getChildCount()-1; //因为最后一个是操作的view,不是标签,所以循环取长度-1;目的是不算那个操作的view for (int i = 0; i < count; i++) { //删除点击标签删除,也可以把点击事件加在你的那个删除图片上面 ContentView contentView= (ContentView) autoBreakViewGroup.getChildAt(i); //封装的一个方法,刚开始删除图片不显示,点击减号后删除图片显示出来 contentView.showCancel(); contentView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { autoBreakViewGroup.removeView(v); //如果里面都删除完了把减号隐藏掉 if(autoBreakViewGroup.getChildCount()==1){ delete.setVisibility(View.GONE); } } }); } } }); }因为有删除操作,所以考虑标签也封装成view来实现,也就是上文的ContentView,代码如下:
/** * Created by bobo on 2017/5/8. */ public class ContentView extends FrameLayout { Context mContext; TextView textView; ImageView imageView; public ContentView(Context context) { super(context); init(context,null); } public ContentView(Context context, AttributeSet attrs) { super(context, attrs); init(context,attrs); } public ContentView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context,attrs); } private void init(Context context, AttributeSet attrs){ mContext = context; View view = LayoutInflater.from(context).inflate(R.layout.view_content, this, true); textView= (TextView) view.findViewById(R.id.tv_content); imageView= (ImageView) findViewById(R.id.iv_cancel); } public void setContent(String content){ textView.setText(content); } public void showCancel(){ imageView.setVisibility(VISIBLE); } }布局文件很简单,这就不贴代码了,删除有点low
操作中addView方法如下
private void addView(String content) { //因为有那个右上角的删除要出来,所以先封装一下,有点low可以考虑换一下那个删除键 ContentView contentView=new ContentView(MainActivity.this); contentView.setContent(content); autoBreakViewGroup.addView(contentView, autoBreakViewGroup.getChildCount() - 1); //当添加了一个标签以后就可以把减号显示出来了 delete.setVisibility(View.VISIBLE); }好了,大功告成!效果如下:
添加标签后的截图:
是不是有点low,呵呵,其实我也是小菜了,接下来问题来了,UI妹子要把这个功能加在页面的中间,最外层嵌套一个ScrollView,大家可以试一下,刚进去的加号不见了,Where's the plus sign?
网上找相关的代码,没有比较方便的方法可以解决这个问题,只能回到大神写的AutoBreakViewGroup中,打log,打断点的找原因,终于功夫不负我这有心人,还真就找到了,刚进去加载viewgroup的时候
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); }返回的高度为0,所以不会显示,找到原因那就该解决方法了,一种最简单的方法,也是自认为最low的方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); if(height==0){ //当高度为0时也就是刚进去的时候,给个默认高度50 setMeasuredDimension(widthSize, 50); }else { //当有标签后,为了避免刚好一行走完,会把操作view挤出去,往后都比实际的高处50,来装操作view setMeasuredDimension(widthSize, height+50); } setMeasuredDimension(widthSize, heightSize); }呵呵,是不是加号出来了,有木有很开心,还有另一种方案比较委婉,没这么暴力,代码如下:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); int mTotalHeight = 0; int mTotalWidth = 0; int mTempHeight = 0; int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); int measureHeight = childView.getMeasuredHeight(); int measuredWidth = childView.getMeasuredWidth(); if(mHorizontalSpacing==0) { mHorizontalSpacing = mScreenWidth / ((mScreenWidth / measuredWidth) * (mScreenWidth / measuredWidth)); } mTempHeight = (measureHeight > mTempHeight) ? measureHeight : mTempHeight; if ((measuredWidth + mTotalWidth + mHorizontalSpacing) > mScreenWidth) { mTotalWidth = 0; mTotalHeight += (mTempHeight + mVerticalSpacing); mTempHeight = 0; } childView.layout(mTotalWidth + mHorizontalSpacing, mTotalHeight, measuredWidth + mTotalWidth + mHorizontalSpacing, mTotalHeight + measureHeight); mTotalWidth += (measuredWidth + mHorizontalSpacing); height = mTotalHeight + measureHeight; } setMeasuredDimension(widthSize, height); }有木有感觉很委婉了,刚进去就测量一次,然后返回,这样刚进去加号也在。第一次写有点紧张,希望大家多多提意见,一起进步哦!
参考博客:http://blog.csdn.net/syusikoku/article/details/52260811