[AS3.0.1]自定義選項listview(標籤流式佈局)
阿新 • • 發佈:2019-02-04
一個自定義選項listview,超過實際寬度會自動換行。
emmmmmm,查了會百度,才知道這個自定義view叫標籤流式佈局,不過這個是按自己思路寫的。
後續實現了一個viewgroup的[AS3.0.1]自定義ViewGroup的學習,寫一個FlowLayout佈局
可以先看下效果!
效果展示
使用如下
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。
總結
一個簡單的自定義記錄!