[AS3.0.1]自定义选项listview(标签流式布局)

一个自定义选项listview,超过实际宽度会自动换行。
emmmmmm,查了会百度,才知道这个自定义view叫标签流式布局,不过这个是按自己思路写的。
后续实现了一个viewgroup的[AS3.0.1]自定义ViewGroup的学习,写一个FlowLayout布局
可以先看下效果!

效果展示

效果gif

使用如下

xml布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="com.gjn.viewdemo.MainActivity">


    <com.gjn.viewdemo.OptionListView
        android:id="@+id/olv"
        android:layout_margin="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</FrameLayout>

activity

public class MainActivity extends AppCompatActivity {

    private OptionListView olv;
    private List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        olv = findViewById(R.id.olv);

        list = new ArrayList<>();
        list.add("我是btn1111");
        list.add("btn2222");
        list.add("asdasd");
        list.add("我是什么?");
        list.add("测试宽度");
        list.add("??????????");

        olv.setOptionStrList(list);

    }
}

开始设计自定义view

设计思路

其实很简单就是计算每一个加入的view的宽度,然后计算下一个view加入之后是否会超过自定义view的实际宽度,会的话就跳过换行

计算代码如下

    private void calculateRow() {
        List<Integer> viewWidths = new ArrayList<>();
        //超过限制宽度强制设为最大
        for (View view : optionViewList) {
            int width = view.getWidth();
            if (width == 0) {
                view.measure(0, 0);
                width = view.getMeasuredWidth();
            }
            if (width > Width){
                width = Width;
            }
            viewWidths.add(width);
        }
        //计算需要生成的行数 每行需要放置多少个view
        rowList = new ArrayList<>();
        //记录加到第几个view
        int size = 0;
        for (int i = 0; i < viewWidths.size(); i++) {
            rowList.add(new ArrayList<View>());
            int futureWidth = 0;
            for (int j = size; j < optionViewList.size(); j++) {
                //计算加上下次宽度是否超过屏幕宽度
                //超过跳出for
                futureWidth += viewWidths.get(j);
                if (futureWidth <= Width){
                    rowList.get(i).add(optionViewList.get(j));
                    //设置点击事件
                    setOnclick(j, optionViewList.get(j));
                }else {
                    break;
                }
            }
            //记录加到第几个view
            size += rowList.get(i).size();
            //判断是否全部view加载完毕
            if (size >= optionViewList.size()){
                break;
            }
        }
    }

以上代码我做了如下操作

  • 计算是否单一一个view的宽度直接越界

如果单独一个view就超过了自定义view的宽度,就把这个view设置成实际宽度

  • 计算行列数

首先设置一个记录加载到第几个view的参数
再次是新建一行然后计算当前行数的宽度加上下一个view的宽度是否超过了自定义view宽度,如果没有就继续增加view,否则跳过这次循环,新建下一行。
最后当记录的参数等于总共view的数量就跳出全部循环。

  • 其他说明

这边的rowList是一个List<List<View>>对象,直接记录了每行有几个view

具体实现

除了上面的计算稍微提及下,其他都是很简单的设置代码了,这边就不细讲了,直接把全部自定义view的代码贴出来了。具体也有一些注释说明

package com.gjn.viewdemo;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by gjn on 2018/5/11.
 */

public class OptionListView extends LinearLayout {
    private static final String TAG = "OptionListView";

    private Context context;
    private List<String> optionStrList;
    private List<View> optionViewList;
    private List<TextView> textViewList;
    private List<List<View>> rowList;
    private int Width;
    private int select = 0;
    private boolean isSelect = true;
    private viewOnClickListener listener;

    public OptionListView(Context context) {
        this(context, null);
    }

    public OptionListView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public OptionListView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        setOrientation(VERTICAL);
        optionStrList = new ArrayList<>();
        textViewList = new ArrayList<>();
        optionViewList = new ArrayList<>();
    }

    public void setOptionStrList(List<String> optionStrList) {
        this.optionStrList = optionStrList;
        optionViewList.clear();
        creat();
    }

    public void setViewLists(List<View> viewLists, List<TextView> textViewLists) {
        this.optionViewList = viewLists;
        this.textViewList = textViewLists;
        clearViewLists();
        creat();
    }

    public void startSelect() {
        isSelect = true;
        updataView();
    }

    public void stopSelect() {
        isSelect = false;
        updataView();
    }

    public void seletePosition(int position) {
        select = position;
        updataView();
    }

    public int getSelect() {
        return select;
    }

    public String getSelectStr() {
        return optionStrList.get(select);
    }

    public void setListener(viewOnClickListener listener) {
        this.listener = listener;
    }

    private void creat() {
        if (optionStrList == null) {
            Log.e(TAG, "stringList is null.");
            return;
        }
        post(new Runnable() {
            @Override
            public void run() {
                //计算实际可设置布局宽度
                calculateWidth();
                //生成相应的数据
                creatData();
                //计算行数
                calculateRow();
                //生成view
                creatView();
            }
        });
    }

    private void clearViewLists() {
        //复用相关,清除view
        for (View view : optionViewList) {
            ViewGroup group = ((ViewGroup) view.getParent());
            if (group != null) {
                group.removeView(view);
            }
        }
    }

    /**
     * 计算实际宽度扣除左右两边的padding
     */
    private void calculateWidth() {
        Width = getWidth();
        if (Width == 0) {
            measure(0, 0);
            Width = getMeasuredWidth();
        }
        Width -= getPaddingLeft();
        Width -= getPaddingRight();
    }

    private void creatData() {
        if (optionViewList.size() == 0) {
            for (String str : optionStrList) {
                bulidView(str);
            }
        }
    }

    private void calculateRow() {
        List<Integer> viewWidths = new ArrayList<>();
        //超过限制宽度强制设为最大
        for (View view : optionViewList) {
            int width = view.getWidth();
            if (width == 0) {
                view.measure(0, 0);
                width = view.getMeasuredWidth();
            }
            if (width > Width) {
                width = Width;
            }
            viewWidths.add(width);
        }
        //计算需要生成的行数 每行需要放置多少个view
        rowList = new ArrayList<>();
        //记录加到第几个view
        int size = 0;
        for (int i = 0; i < viewWidths.size(); i++) {
            rowList.add(new ArrayList<View>());
            int futureWidth = 0;
            for (int j = size; j < optionViewList.size(); j++) {
                //计算加上下次宽度是否超过屏幕宽度
                //超过跳出for
                futureWidth += viewWidths.get(j);
                if (futureWidth <= Width) {
                    rowList.get(i).add(optionViewList.get(j));
                    //设置点击事件
                    setOnclick(j, optionViewList.get(j));
                } else {
                    break;
                }
            }
            //记录加到第几个view
            size += rowList.get(i).size();
            //判断是否全部view加载完毕
            if (size >= optionViewList.size()) {
                break;
            }
        }
    }

    private void creatView() {
        //遍历生成LinearLayout行数
        removeAllViews();
        for (List<View> list : rowList) {
            LinearLayout linearLayout = new LinearLayout(context);
            for (View view : list) {
                linearLayout.addView(view);
            }
            addView(linearLayout);
        }
        seletePosition(select);
    }

    private void bulidView(String str) {
        View view = LayoutInflater.from(context).inflate(R.layout.tv_str, this, false);
        TextView textView = view.findViewById(R.id.tv);
        textView.setText(str);
        textViewList.add(textView);
        optionViewList.add(view);
    }

    private void updataView() {
        if (isSelect) {
            for (TextView textView : textViewList) {
                textView.setBackgroundResource(R.drawable.str_b);
            }
            textViewList.get(select).setBackgroundResource(R.drawable.str_a);
        }
    }

    private void setOnclick(final int position, final View view) {
        view.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                seletePosition(position);
                if (listener != null) {
                    listener.onClick(position, textViewList.get(position));
                }
            }
        });
    }

    public interface viewOnClickListener {
        void onClick(int position, TextView textView);
    }
}


使用的相关xml

tv_str.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">


    <TextView
        android:id="@+id/tv"
        android:layout_margin="5dp"
        android:padding="5dp"
        android:singleLine="true"
        android:background="@drawable/str_b"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />
</FrameLayout>

str_a.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    <solid android:color="@android:color/holo_blue_bright" />
</shape>

str_b.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    <solid android:color="@android:color/darker_gray" />
</shape>



补充说明
想要修改itme的样式只要在bulidView中修改view就好了。
顺便需要记得设置textViewList这个list,这边是用这个list和viewlist进行绑定的,其实可以使用map进行比较精确的绑定,但是我这边直接使用了list。


总结

一个简单的自定义记录!

猜你喜欢

转载自blog.csdn.net/g777520/article/details/80403905