>  기사  >  Java  >  Android 맞춤 ViewGroup은 라벨 부동 효과를 구현합니다.

Android 맞춤 ViewGroup은 라벨 부동 효과를 구현합니다.

高洛峰
高洛峰원래의
2017-01-16 17:04:471661검색

이전에 Master Hongyang의 맞춤형 View 기사를 공부하던 중 플로팅 라벨을 구현하는 맞춤형 ViewGroup을 보았습니다. 그의 아이디어를 미리 살펴보고 내 아이디어를 결합하여 나만의 플로팅 라벨용 맞춤형 ViewGroup을 완성했습니다. 현재 구현에서는 라벨을 동적으로 추가하고 클릭할 수 있습니다. 렌더링은 다음과 같습니다.

Android 맞춤 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 중국어 웹사이트를 주목하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.