1. 程式人生 > >[AS3.0.1]自定義選項listview(標籤流式佈局)

[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。

總結

一個簡單的自定義記錄!