이전에 Master Hongyang의 맞춤형 View 기사를 공부하던 중 플로팅 라벨을 구현하는 맞춤형 ViewGroup을 보았습니다. 그의 아이디어를 미리 살펴보고 내 아이디어를 결합하여 나만의 플로팅 라벨용 맞춤형 ViewGroup을 완성했습니다. 현재 구현에서는 라벨을 동적으로 추가하고 클릭할 수 있습니다. 렌더링은 다음과 같습니다.
1. 아이디어
먼저 onMeasure 메소드에서 ViewGroup의 너비와 높이를 측정합니다. ViewGroup은 Wrap_content로 설정됩니다. 이 경우 문제는 크기를 측정하는 방법입니다. 사용자 정의된 ViewGroup이 Wrap_content로 설정되면 하위 뷰가 먼저 자체적으로 측정되도록 해야 합니다. 하위 뷰의 너비와 높이는 하위 뷰의 getMeasuredWidth 및 getMeasureHeight 메서드를 통해 얻을 수 있습니다. -보다. 하위 뷰를 측정하기 전에 하위 뷰가 추가된 경우 현재 행이 하위 뷰를 수용할 수 있는지 여부를 확인해야 합니다. 그렇지 않은 경우 새 행을 열고 현재 행의 최대 높이를 기록해야 합니다. .
onLayout 메소드에서 핵심 역할은 각 하위 View를 배치하는 것입니다. 즉, ViewGroup의 각 하위 View에 대한 상자 모델의 두 지점(왼쪽 상단과 오른쪽 하단)을 찾는 것입니다. Corner, 즉 점(l,t)과 점(r,b)을 결정하면 두 점이 결정되고 하위 View의 위치도 결정됩니다.
2. 구현
기본 아이디어가 확보되면 코드는 다음과 같습니다.
사용자 정의된 ViewGroup:
/** * 流式标签(动态的,根据传入的数据动态添加标签) */ public class DynamicTagFlowLayout extends ViewGroup { private List<String> mTags = new ArrayList<String>(); public DynamicTagFlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public DynamicTagFlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public DynamicTagFlowLayout(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //当前ViewGroup的总高度 int totalHeight= 0; //所有行中的最大宽度 int maxLineWidth = 0; //当前行的最大高度 int lineMaxHeight = 0; //当前行的总宽度 int currentLineWidth = 0; //每个childView所占用的宽度 int childViewWidthSpace = 0; //每个childView所占用的高度 int childViewHeightSpace = 0; int count = getChildCount(); MarginLayoutParams layoutParams; for(int i = 0; i < count; i++){ View child = getChildAt(i); if(child.getVisibility() != View.GONE){//只有当这个View能够显示的时候才去测量 //测量每个子View,以获取子View的宽和高 measureChild(child, widthMeasureSpec, heightMeasureSpec); layoutParams = (MarginLayoutParams) child.getLayoutParams(); childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; if(currentLineWidth + childViewWidthSpace > widthSize){//表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行 totalHeight += lineMaxHeight; if(maxLineWidth < currentLineWidth){//如果行的最长宽度发生了变化,更新保存的最长宽度 maxLineWidth = currentLineWidth; } currentLineWidth = childViewWidthSpace;//另起一行后,需要重置当前行宽 lineMaxHeight = childViewHeightSpace; }else{//表示当前行可以继续添加子元素 currentLineWidth += childViewWidthSpace; if(lineMaxHeight < childViewHeightSpace){ lineMaxHeight = childViewHeightSpace; } } } } setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxLineWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //当前是第几行 int currentLine = 1; //存放每一行的最大高度 List<Integer> lineMaxHeightList = new ArrayList<Integer>(); //每个childView所占用的宽度 int childViewWidthSpace = 0; //每个childView所占用的高度 int childViewHeightSpace = 0; //当前行的最大高度 int lineMaxHeight = 0; //当前行的总宽度 int currentLineWidth = 0; int count = getChildCount(); MarginLayoutParams layoutParams; for(int i = 0; i < count; i++){ int cl= 0, ct = 0, cr = 0, cb = 0; View child = getChildAt(i); if(child.getVisibility() != View.GONE){//只有当这个View能够显示的时候才去测量 layoutParams = (MarginLayoutParams) child.getLayoutParams(); childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin; childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin; System.out.println("getWidth()---->"+getWidth()); if(currentLineWidth + childViewWidthSpace > getWidth()){//表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行 lineMaxHeightList.add(lineMaxHeight);//此时先将这一行的最大高度加入到集合中 //新的一行,重置一些参数 currentLine++; currentLineWidth = childViewWidthSpace; lineMaxHeight = childViewHeightSpace; cl = layoutParams.leftMargin; if(currentLine > 1){ for(int j = 0; j < currentLine - 1; j++){ ct += lineMaxHeightList.get(j); } ct += layoutParams.topMargin ; }else{ ct = layoutParams.topMargin; } }else{//表示当前行可以继续添加子元素 cl = currentLineWidth + layoutParams.leftMargin; if(currentLine > 1){ for(int j = 0; j < currentLine - 1; j++){ ct += lineMaxHeightList.get(j); } ct += layoutParams.topMargin; }else{ ct = layoutParams.topMargin; } currentLineWidth += childViewWidthSpace; if(lineMaxHeight < childViewHeightSpace){ lineMaxHeight = childViewHeightSpace; } } cr = cl + child.getMeasuredWidth(); cb = ct + child.getMeasuredHeight(); child.layout(cl, ct, cr, cb); } } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } public void setTags(List<String> tags){ if(tags!= null){ mTags.clear(); mTags.addAll(tags); for(int i = 0; i < mTags.size(); i++){ TextView tv = new TextView(getContext()); MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT); lp.setMargins(15, 15, 15, 15); // lp.width = MarginLayoutParams.WRAP_CONTENT; // lp.height = MarginLayoutParams.WRAP_CONTENT; tv.setLayoutParams(lp); tv.setBackgroundResource(R.drawable.tv_bg); /* * setPadding一定要在setBackgroundResource后面使用才有效!!! * http://stackoverflow.com/questions/18327498/setting-padding-for-textview-not-working */ tv.setPadding(15, 15, 15, 15); tv.setTextColor(Color.WHITE); tv.setText(mTags.get(i)); tv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(listener != null){ listener.onClick(v); } } }); addView(tv); } requestLayout(); } } private OnTagItemClickListener listener; public interface OnTagItemClickListener{ public void onClick(View v); } public void setOnTagItemClickListener(OnTagItemClickListener l){ listener = l; } }
MainActivity:
public class MainActivity extends Activity { private DynamicTagFlowLayout dynamicTagFlowLayout; List<String> tags = new ArrayList<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dynamic_tagflowlayout); dynamicTagFlowLayout = (DynamicTagFlowLayout) findViewById(R.id.dynamic_tag); dynamicTagFlowLayout.setOnTagItemClickListener(new OnTagItemClickListener() { @Override public void onClick(View v) { TextView tv = (TextView) v; Toast.makeText(MainActivity.this, tv.getText().toString(), Toast.LENGTH_SHORT).show(); } }); initData(); dynamicTagFlowLayout.setTags(tags); } private void initData() { tags.add("阳哥你好!"); tags.add("Android开发"); tags.add("新闻热点"); tags.add("热水进宿舍啦!"); tags.add("I love you"); tags.add("成都妹子"); tags.add("新余妹子"); tags.add("仙女湖"); tags.add("创新工厂"); tags.add("孵化园"); tags.add("神州100发射"); tags.add("有毒疫苗"); tags.add("顶你阳哥阳哥"); tags.add("Hello World"); tags.add("闲逛的蚂蚁"); tags.add("闲逛的蚂蚁"); tags.add("闲逛的蚂蚁"); tags.add("闲逛的蚂蚁"); tags.add("闲逛的蚂蚁"); tags.add("闲逛的蚂蚁"); } }
이상이 이 글의 전체 내용입니다. 모든 사람의 학습에 도움이 되기를 바랍니다. 또한 모든 사람이 PHP 중국어 웹사이트를 지원하기를 바랍니다.
레이블 플로팅 효과를 구현하는 Android 사용자 정의 ViewGroup에 대한 더 많은 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!