您好,登錄后才能下訂單哦!
在我們開發過程中會經常遇見一些客戶要求但是Android系統又不提供的效果,這時我們只能自己動手去實現它,或者從網絡上借鑒他人的資源,本著用別人不如自己會做的心態,在此我總結了一下Android中如何實現自動換行的LinearLayout。
在本文中,說是LinearLayout其實是繼承自GroupView,在這里主要重寫了兩個方法,onMeasure、onLayout方法,下面我對此加以介紹。(代碼中使用了AttributeSet,由于時間問題不再予以介紹)。
1. onMeasure是干什么的?
在ViewGroup的創建過程中,onMeasure是在onLayout之前的,所以在此先對onMeasure進行介紹,onMeasure方法是計算子控件與父控件在屏幕中所占長寬大小的,onMeasure傳入兩個參數——widthMeasureSpec和heightMeasureSpec. 這兩個參數指明控件可獲得的空間以及關于這個空間描述的元數據.
int withMode = MeasureSpec.getMode(widthMeasureSpec); int withSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Mode有3種模式分別是UNSPECIFIED, EXACTLY和AT_MOST,如果是AT_MOST,Size代表的是最大可獲得的空間;如果是EXACTLY,Size代表的是精確的尺寸;如果是UNSPECIFIED,就是你想要多少就有多少。經過代碼測試就知道,當我們設置width或height為fill_parent時,容器在布局時調用子 view的measure方法傳入的模式是EXACTLY,因為子view會占據剩余容器的空間,所以它大小是確定的。而當設置為 wrap_content時,容器傳進去的是AT_MOST, 表示子view的大小最多是多少,這樣子view會根據這個上限來設置自己的尺寸。當子view的大小設置為精確值時,容器傳入的是EXACTLY。
2. onLayout是干什么的?
與onMesaure相比,onLayout更加容易理解,它的作用就是調座位,就是把所有的子View根據不同的需要,通過View. layout(int l, int t, int r, int b)方法指定它所在的位置。
3. 解決問題
只要對onMeasure和onLayout加以理解,對于該篇所要實現的功能就不再難以實現,下面貼上代碼,并在代碼中講解。
WaroLinearLayout.java
public class WarpLinearLayout extends ViewGroup { private Type mType; private List<WarpLine> mWarpLineGroup; public WarpLinearLayout(Context context) { this(context, null); } public WarpLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, R.style.WarpLinearLayoutDefault); } public WarpLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mType = new Type(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int withMode = MeasureSpec.getMode(widthMeasureSpec); int withSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int with = 0; int height = 0; int childCount = getChildCount(); /** * 在調用childView。getMeasre之前必須先調用該行代碼,用于對子View大小的測量 */ measureChildren(widthMeasureSpec, heightMeasureSpec); /** * 計算寬度 */ switch (withMode) { case MeasureSpec.EXACTLY: with = withSize; break; case MeasureSpec.AT_MOST: for (int i = 0; i < childCount; i++) { if (i != 0) { with += mType.horizontal_Space; } with += getChildAt(i).getMeasuredWidth(); } with += getPaddingLeft() + getPaddingRight(); with = with > withSize ? withSize : with; break; case MeasureSpec.UNSPECIFIED: for (int i = 0; i < childCount; i++) { if (i != 0) { with += mType.horizontal_Space; } with += getChildAt(i).getMeasuredWidth(); } with += getPaddingLeft() + getPaddingRight(); break; default: with = withSize; break; } /** * 根據計算出的寬度,計算出所需要的行數 */ WarpLine warpLine = new WarpLine(); /** * 不能夠在定義屬性時初始化,因為onMeasure方法會多次調用 */ mWarpLineGroup = new ArrayList<WarpLine>(); for (int i = 0; i < childCount; i++) { if (warpLine.lineWidth + getChildAt(i).getMeasuredWidth() + mType.horizontal_Space > with) { if (warpLine.lineView.size() == 0) { warpLine.addView(getChildAt(i)); mWarpLineGroup.add(warpLine); warpLine = new WarpLine(); } else { mWarpLineGroup.add(warpLine); warpLine = new WarpLine(); warpLine.addView(getChildAt(i)); } } else { warpLine.addView(getChildAt(i)); } } /** * 添加最后一行 */ if (warpLine.lineView.size() > 0 && !mWarpLineGroup.contains(warpLine)) { mWarpLineGroup.add(warpLine); } /** * 計算寬度 */ height = getPaddingTop() + getPaddingBottom(); for (int i = 0; i < mWarpLineGroup.size(); i++) { if (i != 0) { height += mType.vertical_Space; } height += mWarpLineGroup.get(i).height; } switch (heightMode) { case MeasureSpec.EXACTLY: height = heightSize; break; case MeasureSpec.AT_MOST: height = height > heightSize ? heightSize : height; break; case MeasureSpec.UNSPECIFIED: break; default: break; } setMeasuredDimension(with, height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { t = getPaddingTop(); for (int i = 0; i < mWarpLineGroup.size(); i++) { int left = getPaddingLeft(); WarpLine warpLine = mWarpLineGroup.get(i); int lastWidth = getMeasuredWidth() - warpLine.lineWidth; for (int j = 0; j < warpLine.lineView.size(); j++) { View view = warpLine.lineView.get(j); if (isFull()) {//需要充滿當前行時 view.layout(left, t, left + view.getMeasuredWidth() + lastWidth / warpLine.lineView.size(), t + view.getMeasuredHeight()); left += view.getMeasuredWidth() + mType.horizontal_Space + lastWidth / warpLine.lineView.size(); } else { switch (getGrivate()) { case 0://右對齊 view.layout(left + lastWidth, t, left + lastWidth + view.getMeasuredWidth(), t + view.getMeasuredHeight()); break; case 2://居中對齊 view.layout(left + lastWidth / 2, t, left + lastWidth / 2 + view.getMeasuredWidth(), t + view.getMeasuredHeight()); break; default://左對齊 view.layout(left, t, left + view.getMeasuredWidth(), t + view.getMeasuredHeight()); break; } left += view.getMeasuredWidth() + mType.horizontal_Space; } } t += warpLine.height + mType.vertical_Space; } } /** * 用于存放一行子View */ private final class WarpLine { private List<View> lineView = new ArrayList<View>(); /** * 當前行中所需要占用的寬度 */ private int lineWidth = getPaddingLeft() + getPaddingRight(); /** * 該行View中所需要占用的最大高度 */ private int height = 0; private void addView(View view) { if (lineView.size() != 0) { lineWidth += mType.horizontal_Space; } height = height > view.getMeasuredHeight() ? height : view.getMeasuredHeight(); lineWidth += view.getMeasuredWidth(); lineView.add(view); } } /** * 對樣式的初始化 */ private final static class Type { /* *對齊方式 right 0,left 1,center 2 */ private int grivate; /** * 水平間距,單位px */ private float horizontal_Space; /** * 垂直間距,單位px */ private float vertical_Space; /** * 是否自動填滿 */ private boolean isFull; Type(Context context, AttributeSet attrs) { if (attrs == null) { return; } TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WarpLinearLayout); grivate = typedArray.getInt(R.styleable.WarpLinearLayout_grivate, grivate); horizontal_Space = typedArray.getDimension(R.styleable.WarpLinearLayout_horizontal_Space, horizontal_Space); vertical_Space = typedArray.getDimension(R.styleable.WarpLinearLayout_vertical_Space, vertical_Space); isFull = typedArray.getBoolean(R.styleable.WarpLinearLayout_isFull, isFull); } } public int getGrivate() { return mType.grivate; } public float getHorizontal_Space() { return mType.horizontal_Space; } public float getVertical_Space() { return mType.vertical_Space; } public boolean isFull() { return mType.isFull; } public void setGrivate(int grivate) { mType.grivate = grivate; } public void setHorizontal_Space(float horizontal_Space) { mType.horizontal_Space = horizontal_Space; } public void setVertical_Space(float vertical_Space) { mType.vertical_Space = vertical_Space; } public void setIsFull(boolean isFull) { mType.isFull = isFull; } /** * 每行子View的對齊方式 */ public final static class Gravite { public final static int RIGHT = 0; public final static int LEFT = 1; public final static int CENTER = 2; } }
attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="WarpLinearLayout"> <attr name="grivate" format="enum"><!--對齊方式 !--> <enum name="right" value="0"></enum> <enum name="left" value="1"></enum> <enum name="center" value="2"></enum> </attr> <attr name="horizontal_Space" format="dimension"></attr> <attr name="vertical_Space" format="dimension"></attr> <attr name="isFull" format="boolean"></attr> </declare-styleable> </resources>
WarpLinearLayoutDefault
<style name="WarpLinearLayoutDefault"> <item name="grivate">left</item> <item name="horizontal_Space">20dp</item> <item name="vertical_Space">20dp</item> <item name="isFull">false</item> </style>
MainActivity.java
public class MainActivity extends Activity { private Button btn; private WarpLinearLayout warpLinearLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); warpLinearLayout = (WarpLinearLayout) findViewById(R.id.warpLinearLayout); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int n = new Random().nextInt(10) + 5; StringBuffer stringBuffer = new StringBuffer(); Random random = new Random(); Log.i("WarpLinearLayout","n="+n); for (int i = 0; i < n; i++) { stringBuffer.append((char)(65+random.nextInt(26))); Log.i("WarpLinearLayout", "StringBuffer=" + stringBuffer.toString()); } TextView tv = new TextView(MainActivity.this); tv.setText(stringBuffer.toString()+"000"); tv.setBackgroundResource(R.drawable.radius_backgroup_yellow); tv.setPadding(10,10,10,10); warpLinearLayout.addView(tv); } }); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="add" android:textSize="20dp" /> <com.example.customview.viewgroup.WarpLinearLayout android:id="@+id/warpLinearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/btn" android:background="#FF00FF00" android:padding="10dp" app:grivate="right" app:horizontal_Space="10dp" app:isFull="false" app:vertical_Space="10dp"></com.example.customview.viewgroup.WarpLinearLayout> </RelativeLayout>
運行效果圖如下:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。