1. 程式人生 > >android 自動換行的線性佈局

android 自動換行的線性佈局


在專案中,有時候會有“橫向排列,排滿後自動換行”的需求(比如下圖),要是子view是定長的就沒什麼好說的了,但如果是變長的話呢?這篇部落格會幫你應對這種需求。

這裡寫圖片描述

基本思路

  1. 最外層一層豎直線性佈局(我們稱為父佈局)
  2. 新建水平線性佈局(我們稱為行佈局)
  3. 計算待放入的view的寬度和行佈局的剩下寬度
  4. 判斷是否可以放入
    (1). 若view的寬度小於等於剩餘寬度,放入,到第三步;
    (2). 若view的寬度大於剩餘寬度,新增行佈局到父佈局,到第二步。

注意

這裡要注意幾點:
1. 子view的寬度要加上間隔;
2. 若是子view的寬度大於行佈局的寬度,不考慮對子view進入換行,直接放入;

接下來看程式碼,註釋已經很詳細,就不累贅了。



1. activity的佈局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#eee"
    >
    <LinearLayout
        android:id="@+id/ll_parent"
android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" />
</FrameLayout>

2. 子view

<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="16sp" android:textColor="@android:color/white" android:gravity="center" android:paddingTop="5dp" android:paddingBottom="5dp" android:paddingLeft="8dp" android:paddingRight="8dp" android:background="@drawable/tv_bg" />

3. 子view的背景

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

4. activity程式碼

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        ll_parent = (LinearLayout) findViewById(R.id.ll_parent);
        initAutoLL();
    }

    //    資料
    ArrayList<String> datas = new ArrayList<>();

    //    初始化資料
    private void initData() {
        datas.add("作 家");
        datas.add("段 子 手");
        datas.add("軟 文 作 者");
        datas.add("攝 影 愛 好 者");
        datas.add("畫 家");
        datas.add("哦 我還很喜歡音樂");
        datas.add("還 有 其 他 七 七 八 八 的 我 就 不 說 了");
        datas.add("老 師");
    }

    //    最外層的豎直線性佈局
    private LinearLayout ll_parent;

    //    繪製自動換行的線性佈局
    private void initAutoLL() {
//        每一行的佈局,初始化第一行佈局
        LinearLayout rowLL = new LinearLayout(this);
        LinearLayout.LayoutParams rowLP = 
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
                        ViewGroup.LayoutParams.WRAP_CONTENT);
        float rowMargin = dipToPx(10);
        rowLP.setMargins(0, (int) rowMargin, 0, 0);
        rowLL.setLayoutParams(rowLP);
        boolean isNewLayout = false;
        float maxWidth = getScreenWidth() - dipToPx(30);
//        剩下的寬度
        float elseWidth = maxWidth;
        LinearLayout.LayoutParams textViewLP = 
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 
                        ViewGroup.LayoutParams.WRAP_CONTENT);
        textViewLP.setMargins((int) dipToPx(8), 0, 0, 0);
        for (int i = 0; i < datas.size(); i++) {
//            若當前為新起的一行,先新增舊的那行
//            然後重新建立佈局物件,設定引數,將isNewLayout判斷重置為false
            if (isNewLayout) {
                ll_parent.addView(rowLL);
                rowLL = new LinearLayout(this);
                rowLL.setLayoutParams(rowLP);
                isNewLayout = false;
            }
//            計算是否需要換行
            TextView textView = (TextView) getLayoutInflater().inflate(R.layout.textview, null);
            textView.setText(datas.get(i));
            textView.measure(0, 0);
//            若是一整行都放不下這個文字框,新增舊的那行,新起一行新增這個文字框
            if (maxWidth < textView.getMeasuredWidth()) {
                ll_parent.addView(rowLL);
                rowLL = new LinearLayout(this);
                rowLL.setLayoutParams(rowLP);
                rowLL.addView(textView);
                isNewLayout = true;
                continue;
            }
//            若是剩下的寬度小於文字框的寬度(放不下了)
//            新增舊的那行,新起一行,但是i要-1,因為當前的文字框還未新增
            if (elseWidth < textView.getMeasuredWidth()) {
                isNewLayout = true;
                i--;
//                重置剩餘寬度
                elseWidth = maxWidth;
                continue;
            } else {
//                剩餘寬度減去文字框的寬度+間隔=新的剩餘寬度
                elseWidth -= textView.getMeasuredWidth() + dipToPx(8);
                if (rowLL.getChildCount() == 0) {
                    rowLL.addView(textView);
                } else {
                    textView.setLayoutParams(textViewLP);
                    rowLL.addView(textView);
                }
            }
        }
//        新增最後一行,但要防止重複新增
        ll_parent.removeView(rowLL);
        ll_parent.addView(rowLL);
    }

    //    dp轉px
    private float dipToPx(int dipValue) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 
                dipValue, 
                this.getResources().getDisplayMetrics());
    }

    //  獲得評論寬度
    private float getScreenWidth() {
        return this.getResources().getDisplayMetrics().widthPixels;
    }

}


其中主要程式碼在initAutoLL函式內部,執行後便可看到文章開頭處的效果圖