自定义FlowLayout,android flowLayout实现

简介: 自定义FlowLayout,android flowLayout实现

image.png

我想大家在开发过程中都碰到过这样的需求,类似标签展示,要展示如上图效果,这里面的数据不确定每项字数,有的非常长,有的很短,数据动态填充。


这种情况用listView和gridView展示效果都没有上图的效果。


这时我们其实是要自己写一个控件来填充上图的数据,也就是我们今天要说的自定义view,流式布局。


方法还是重写onMeasure和onLayout


话不多说  ,代码贴上


一.自定义view

package com.jky.mobilebzt.view;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class XCFlowLayout extends ViewGroup {
  // 存储所有子View
  private List<List<View>> mAllChildViews = new ArrayList<List<View>>();
  // 每一行的高度
  private List<Integer> mLineHeight = new ArrayList<Integer>();
  public XCFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
  public XCFlowLayout(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public XCFlowLayout(Context context) {
    this(context, null);
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // TODO Auto-generated method stub
    // 父控件传进来的宽度和高度以及对应的测量模式
    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
    int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
    // 如果当前ViewGroup的宽高为wrap_content的情况
    int width = 0; // 自己测量的宽度
    int height = 0; // 自己测量的高度
    int lineWidth = 0;// 每一行的宽度
    int lineHeight = 0; // 每一行的高度
    int childCount = getChildCount();// 获取子view的个数
    for (int i = 0; i < childCount; i++) {
      View child = getChildAt(i);
      // 测量子View的宽和高
      measureChild(child, widthMeasureSpec, heightMeasureSpec);
      // 得到LayoutParams
      MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
      // 得到子View占据的宽度
      int childWidth = child.getMeasuredWidth() + lp.leftMargin
          + lp.rightMargin;
      // 得到子View占据的高度
      int childHeight = child.getMeasuredHeight() + lp.topMargin
          + lp.bottomMargin;
      if (lineWidth + childWidth > sizeWidth) {// 需要进行换行
        width = Math.max(width, lineWidth); // 得到最大宽度
        lineWidth = childWidth; // 重置lineWidth
        height += lineHeight; // 得到高度
        lineHeight = childHeight;// 重置LineHeight
      } else {// 不需要进行换行
        lineWidth += childWidth;// 叠加行宽
        lineHeight = Math.max(lineHeight, childHeight);
      }
      if (i == childCount - 1) {// 处理最后一个子View的情况
        width = Math.max(width, lineWidth);
        height += lineHeight;
      }
    }
    // wrapcontent
    setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth
        : width, modeHeight == MeasureSpec.EXACTLY ? sizeHeight
        : height);
//    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // TODO Auto-generated method stub
    mAllChildViews.clear();
    mLineHeight.clear();
    int width = getWidth();// 获取当前ViewGroup宽度
    int lineWidth = 0;
    int lineHeight = 0;
    List<View> lineViews = new ArrayList<View>();// 记录当前行的View
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View child = getChildAt(i);
      MarginLayoutParams lp = (MarginLayoutParams) child
          .getLayoutParams();
      int childWidth = child.getMeasuredWidth();
      int childHeight = child.getMeasuredHeight();
      // 需要换行
      if (lineWidth + childWidth + lp.leftMargin + lp.rightMargin > width) {
        mLineHeight.add(lineHeight); // 记录lineHeight
        mAllChildViews.add(lineViews); // 记录当前行的Views
        // 重置 行的宽高
        lineWidth = 0;
        lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
        // 重置当前行的View集合;
        lineViews = new ArrayList<View>();
      }
      lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
      lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
          + lp.bottomMargin);
      lineViews.add(child);
    }
    // 处理最后一行
    mLineHeight.add(lineHeight);
    mAllChildViews.add(lineViews);
    // 设置子View的位置
    int left = 0;
    int top = 0;
    // 获取行数
    int lineCount = mAllChildViews.size();
    for (int i = 0; i < lineCount; i++) {
      // 当前行的views和高度
      lineViews = mAllChildViews.get(i);
      lineHeight = mLineHeight.get(i);
      for (int j = 0; j < lineViews.size(); j++) {
        View child = lineViews.get(j);
        // 判断是否显示
        if (child.getVisibility() == View.GONE) {
          continue;
        }
        MarginLayoutParams lp = (MarginLayoutParams) child
            .getLayoutParams();
        int cLeft = left + lp.leftMargin;
        int cTop = top + lp.topMargin;
        int cRight = cLeft + child.getMeasuredWidth();
        int cBottom = cTop + child.getMeasuredHeight();
        // 进行子View进行布局
        child.layout(cLeft, cTop, cRight, cBottom);
        left += child.getMeasuredWidth() + lp.leftMargin
            + lp.rightMargin;
      }
      left = 0;
      top += lineHeight;
    }
  }
  /**
   * 与当前ViewGroup对应的LayoutParams
   */
  @Override
  public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new MarginLayoutParams(getContext(), attrs);
  }
}

二.xml部分

xml布局中加上这个

  <com.jky.mobilebzt.view.XCFlowLayout
                android:id="@+id/xcf_hot_words"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/margin_lsmall"
                android:layout_marginBottom="@dimen/margin_normal"
                android:layout_marginRight="@dimen/margin_normal" />

三.初始化数据部分

  @SuppressLint("NewApi")
  private void initHotWordViews() {
    MarginLayoutParams lp = new MarginLayoutParams(
        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    lp.leftMargin = 20;
    lp.rightMargin = 20;
    lp.topMargin = 8;
    lp.bottomMargin = 8;
    for (int i = 0; i < hotWords.size(); i++) {
      final String hotWord = hotWords.get(i);
      TextView view = new TextView(this);
      view.setGravity(Gravity.CENTER);
      view.setText(hotWords.get(i));
      view.setTextColor(Color.BLACK);
      view.setBackground(getResources().getDrawable(R.drawable.hot_word_selector));
      mFlowLayout.addView(view, lp);
      view.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
        }
      });
    }
  }


相关文章
|
27天前
|
缓存 前端开发 Android开发
安卓开发中的自定义视图:从零到英雄
【10月更文挑战第42天】 在安卓的世界里,自定义视图是一块画布,让开发者能够绘制出独一无二的界面体验。本文将带你走进自定义视图的大门,通过深入浅出的方式,让你从零基础到能够独立设计并实现复杂的自定义组件。我们将探索自定义视图的核心概念、实现步骤,以及如何优化你的视图以提高性能和兼容性。准备好了吗?让我们开始这段创造性的旅程吧!
23 1
|
2月前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
43 3
|
2月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
94 0
|
1月前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
1月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
37 5
|
2月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
3月前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
67 10
|
3月前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
55 3
|
3月前
|
前端开发 Android开发 开发者
安卓应用开发中的自定义视图基础
【9月更文挑战第13天】在安卓开发的广阔天地中,自定义视图是一块神奇的画布,它允许开发者将想象力转化为用户界面的创新元素。本文将带你一探究竟,了解如何从零开始构建自定义视图,包括绘图基础、触摸事件处理,以及性能优化的实用技巧。无论你是想提升应用的视觉吸引力,还是追求更流畅的交互体验,这里都有你需要的金钥匙。
|
3月前
|
缓存 搜索推荐 Android开发
安卓应用开发中的自定义View组件实践
【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。