当前位置:   article > 正文

Android自定义控件---打造不一样的FlowLayout_android gridview改成flowlayout

android gridview改成flowlayout

网上关于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;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177

我们的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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

下面来看看我们在activity中是如何使用我们的这个FlowLayout的。
MainActivity.java

 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);
    }
}
" data-snippet-id="ext.bd8139366275f1dcc899ee363c33ef9b" data-snippet-saved="false" data-codota-status="done">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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

嗯,个人感觉说明已经很详细了。最后还有一个简单的圆角背景图,和两个辅助类。
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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

所有的文件基本上都在这儿了。
欢迎大家fork。
https://github.com/kailaisi/FlowLayout

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/293739
推荐阅读
相关标签
  

闽ICP备14008679号