Android自定义控件---打造不一样的FlowLayout
来源:互联网 发布:php cookies管理系统 编辑:程序博客网 时间:2024/05/21 09:36
网上关于FlowLayout的文章有很多,大部分都是右侧空白不固定:
但是不想我想要的效果,修改了一下,先来看看效果图。
如果你对FlowLayout还不了解,可以看看鸿洋大神的文章:Android 自定义ViewGroup 实战篇 -> 实现FlowLayout。想一想,其实在设置每个子类的宽度的时候,将剩余宽度平均分配给每个子控件便可以实现我要的效果。
嗯,先上FlowLayout文件,其实主要是在layout方法中做了修改。
package com.android.flowlayout;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.ArrayList;import java.util.List;/** * 文字瀑布流,瀑布流中每个子控件是textview,如果不是,请重新写layout方法,将返回的子控件定义为你的控件类型, * Created by wu on 2015/11/12. */public class FlowLayout extends ViewGroup { private List<Line> mLines = new ArrayList<>(); private Line currentLine;//当前行 private int usedWidth = 0;//当前行已经使用的宽度 private int horizontalSpacing;//水平的间隔 private int verticalSpacing;//垂直的间隔 private int width;//控件的宽度 private int height;//控件的高度 private Context mContext; public FlowLayout(Context context) { this(context,null); } public FlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; horizontalSpacing=UiUtils.dp2px(context,13); verticalSpacing=UiUtils.dp2px(context,13); } //测量当前控件 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //获取当前容器的宽高模式和大小 mLines.clear(); currentLine = null; usedWidth = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec)-getPaddingLeft()-getPaddingRight(); height = MeasureSpec.getSize(heightMeasureSpec)-getPaddingTop()-getPaddingBottom(); int childWidthMode; int childHeightMode; //为了测量每个子控件,需要指定每个子控件的测量规则 childWidthMode = widthMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : widthMode; childHeightMode = heightMode == MeasureSpec.EXACTLY ? MeasureSpec.AT_MOST : heightMode; int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, childWidthMode); int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, childHeightMode); currentLine = new Line();//创建了新的一行(第一行) for (int i = 0; i < getChildCount(); i++) { //测量子控件 View child = getChildAt(i); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); int measuredWidth = child.getMeasuredWidth();//获得子控件的宽度 if(usedWidth+measuredWidth+horizontalSpacing<width ||currentLine.getChildCount()==0){ //当前行没有数据或者现在的宽度+下一个宽度<行宽。不需要换行,直接添加到current中。 currentLine.addChild(child); usedWidth+=measuredWidth; usedWidth+=horizontalSpacing; }else{ newLine(); currentLine.addChild(child); usedWidth+=measuredWidth; usedWidth+=horizontalSpacing; } } if (!mLines.contains(currentLine)) {//添加最后一行 mLines.add(currentLine); Log.d("FlowLayout", "currentLine.getChildCount():" + currentLine.getChildCount()); } int totalHeight = 0; for (Line line : mLines) { totalHeight += line.getHeight(); } totalHeight += ((mLines.size() - 1) * verticalSpacing)+getPaddingTop()+getPaddingBottom(); setMeasuredDimension(width+getPaddingLeft()+getPaddingRight(), resolveSize(totalHeight, heightMeasureSpec)); } //分配子控件的位置,如果剩余的距离不够使用,则需要换行 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { l+=getPaddingLeft(); t+=getPaddingTop(); for (int i = 0; i < mLines.size(); i++) { Line line = mLines.get(i); line.layout(l, t); t += line.getHeight() + verticalSpacing;//每一行左上角的t值都会改变 } } /** * 每一个行的类 */ private class Line { int height = 0; List<View> children = new ArrayList<>(); int total = 0; /** * 添加一个子控件 * * @param child */ public void addChild(View child) { children.add(child); if (child.getMeasuredHeight() > height) { height = child.getMeasuredHeight(); } total += child.getMeasuredWidth(); } /** * 获取子控件的数量 * * @return */ public int getChildCount() { return children.size(); } public int getHeight() { return height; } /** * 指定行的左上角位置,其子类的位置由该函数确定 * * @param l 左侧位置 * @param t 顶部位置 */ public void layout(int l, int t) { total += horizontalSpacing * (children.size() - 1);//现有子控件所占有的宽度 int surplusChild = 0; int surplus = width - total;//右侧剩余的宽度 surplusChild = surplus / children.size();//右侧剩余宽度平分给各个控件 for (int i = 0; i < children.size(); i++) { //将每一个子TextView取出来 TextView view = (TextView) children.get(i); //设置每个子TextView的布局,宽度在原有布局的基础上增加了surplusChild view.layout(l, t, l + view.getMeasuredWidth()+surplusChild, t + view.getMeasuredHeight()); //为子View的字体设置居中,此步骤不能在给layout添加view的时候,给view设置gravity属性,只能在这里设置 view.setGravity(Gravity.CENTER); String text=view.getText().toString(); if(text!=null){ //如果此时textview的文字已经绘制完成,因为我们重新layout,会导致文字不居中,重新获取文字,并设置, view.setText(text); } //更新下一个子View的左侧的位置 l += view.getMeasuredWidth()+surplusChild; l += verticalSpacing; } } } /** * 创建新的行 */ public void newLine() { mLines.add(currentLine); currentLine = new Line(); usedWidth = 0; }}
我们的xml主布局文件其实很简单。
activity_main:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent"/></RelativeLayout>
下面来看看我们在activity中是如何使用我们的这个FlowLayout的。
MainActivity.java
package com.android.testflowlayout;import android.graphics.Color;import android.graphics.drawable.Drawable;import android.graphics.drawable.GradientDrawable;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.widget.GridView;import android.widget.LinearLayout;import android.widget.RelativeLayout;import android.widget.ScrollView;import android.widget.TextView;import android.widget.Toast;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.List;import java.util.Random;public class MainActivity extends AppCompatActivity { private android.widget.ScrollView scrollView; private List<String> datas; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initDatas(); FlowLayout flowLayout = new FlowLayout(this); int padding=UiUtils.dp2px(this,13); flowLayout.setPadding(padding,padding,padding,padding); Drawable pressDrawable=DrawableUtils.createShape(this,0xffcecece); for (int i = 0; i < datas.size(); i++) { TextView textView = new TextView(this); //设置textview未点击时的背景,圆角+随机颜色,通过xml设置+代码实现 textView.setBackgroundResource(R.drawable.text_bg); //生成随机颜色,为了防止产生黑色或者白色,设定一定的范围 int color= Color.rgb(new Random().nextInt(200) + 20, new Random().nextInt(200) + 20, new Random().nextInt(200) + 20); GradientDrawable drawable= (GradientDrawable) textView.getBackground(); //将生成的随机色赋值给背景色 drawable.setColor(color); //设置背景为状态选择器 textView.setBackgroundDrawable(new DrawableUtils().creatStateListDrawable(pressDrawable, drawable)); textView.setText(datas.get(i)); final int finalI = i; textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, datas.get(finalI), Toast.LENGTH_SHORT).show(); } }); textView.setGravity(Gravity.CENTER); textView.setTextColor(Color.WHITE); flowLayout.addView(textView); } scrollView.addView(flowLayout); } /** * 生成要显示的数据 */ private void initDatas() { String[] strs=new String[]{"QQ","视频","放开那三国","电子书","酒店","单机","小说","斗地主","优酷", "网游","WIFI万能钥匙","播放器","捕鱼达人2","机票","游戏","熊出没之熊大快跑","美图秀秀","浏览器", "单机游戏","我的世界","电影电视","QQ空间","旅游","免费游戏","2048","刀塔传奇","壁纸","节奏大师", "锁屏","装机必备","天天动听","备份","网盘","海淘网","大众点评","爱奇艺视频","腾讯手机管家", "百度地图","猎豹清理大师","谷歌地图","hao123上网导航","京东","youni有你","万年历-农历黄历","支付宝钱包"}; datas=new ArrayList<>(Arrays.asList(strs)); } private void initViews() { this.scrollView = (ScrollView) findViewById(R.id.scrollView); }}
嗯,个人感觉说明已经很详细了。最后还有一个简单的圆角背景图,和两个辅助类。
text_bg.xml
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="5dp"/> <solid android:color="#000000"/> <padding android:bottom="4dp" android:top="4dp" android:left="7dp" android:right="7dp"/></shape>
UiUtils.java
package com.android.testflowlayout;import android.content.Context;import android.util.TypedValue;/** * UI相关的辅助类 * Created by wu on 2015/11/6. */public class UiUtils { /* * @param context * @param dpVal * @return */ public static int dp2px(Context context,float dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal,context.getResources().getDisplayMetrics()); }}
DrawableUtils.java
package com.android.testflowlayout;import android.content.Context;import android.graphics.drawable.Drawable;import android.graphics.drawable.GradientDrawable;import android.graphics.drawable.StateListDrawable;/** * Created by wu on 2015/11/12. */public class DrawableUtils { /** * 生成圆角图片 * @param context * @param color * @return */ public static Drawable createShape(Context context, int color) { GradientDrawable drawable=new GradientDrawable(); drawable.setCornerRadius(UiUtils.dp2px(context,5)); drawable.setColor(color); return drawable; } /** * 生成selector,动态设置 * @param pressedDrawable 按下时的drawable * @param normalDrawable 正常状态是的drawable * @return */ public static Drawable creatStateListDrawable(Drawable pressedDrawable,Drawable normalDrawable){ StateListDrawable drawable=new StateListDrawable(); drawable.addState(new int[]{android.R.attr.state_pressed},pressedDrawable); drawable.addState(new int[]{},normalDrawable); return drawable; }}
所有的文件基本上都在这儿了。
欢迎大家fork。
https://github.com/kailaisi/FlowLayout
1 0
- Android自定义控件---打造不一样的FlowLayout
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android 高仿微信头像截取 打造不一样的自定义控件
- Android自定义控件--流式布局(FlowLayout)
- Android打造不一样的EmptyView
- Android打造不一样的EmptyView
- Android打造不一样的EmptyView
- Android打造不一样的EmptyView
- 自定义FlowLayout,android flowLayout实现
- 自定义竖直方向的seekbar(一)
- 数据库分表
- 内存优化--图片优化
- 编写程序 分别使用输入 输出 求平均值函数
- android最火的开源项目
- Android自定义控件---打造不一样的FlowLayout
- 排序算法之堆排序
- 自定义组合控件
- 微信中长按弹出菜单(2)实现了功能
- 在Java程序中打log
- ajax
- 学习笔记之重写原型
- 冒泡排序(Bubble_Sort)
- yii2 strace 追踪正在执行的进程