viewgroup简单说就是可以装view的view.今天遇到一个问题,就是需要一个可以自动根据一行中view的宽度自动换行的布局,网上找了下,没有相关的例子,但是找到了思路:自定义一个viewgroup,然后在onlayout文件里面自动检测view的右边缘的横坐标值,和你的view的parent view的况度判断是否换行显示view就可以了。因为代码比较简单,就不多说了:
public class MyViewGroup extends ViewGroup {
private final static String TAG = "MyViewGroup";
private final static int VIEW_MARGIN=2;
public MyViewGroup(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "widthMeasureSpec = "+widthMeasureSpec+" heightMeasureSpec"+heightMeasureSpec);
for (int index = 0; index < getChildCount(); index++) {
final View child = getChildAt(index);
// measure
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
Log.d(TAG, "changed = "+arg0+" left = "+arg1+" top = "+arg2+" right = "+arg3+" botom = "+arg4);
final int count = getChildCount();
int row=0;// which row lay you view relative to parent
int lengthX=arg1; // right position of child relative to parent
int lengthY=arg2; // bottom position of child relative to parent
for(int i=0;i<count;i++){
final View child = this.getChildAt(i);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
lengthX+=width+VIEW_MARGIN;
lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2;
//if it can't drawing on a same line , skip to next line
if(lengthX>arg3){
lengthX=width+VIEW_MARGIN+arg1;
row++;
lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2;
}
child.layout(lengthX-width, lengthY-height, lengthX, lengthY);
}
}
}
这里有个地方要注意,那就要明白ViewGroup的绘图流程:ViewGroup绘制包括两个步骤:1.measure 2.layout
在两个步骤中分别调用回调函数:1.onMeasure() 2.onLayout()
1.onMeasure() 在这个函数中,ViewGroup会接受childView的请求的大小,然后通过childView的 measure(newWidthMeasureSpec, heightMeasureSpec)函数存储到childView中,以便childView的getMeasuredWidth() andgetMeasuredHeight() 的值可以被后续工作得到。
2.onLayout() 在这个函数中,ViewGroup会拿到childView的getMeasuredWidth() andgetMeasuredHeight(),用来布局所有的childView。
3.View.MeasureSpec 与 LayoutParams 这两个类,是ViewGroup与childView协商大小用的。其中,View.MeasureSpec是ViewGroup用来部署 childView用的, LayoutParams是childView告诉ViewGroup 我需要多大的地方。
4.在View 的onMeasure的最后要调用setMeasuredDimension()这个方法存储View的大小,这个方法决定了当前View的大小。
public class RecipientShowHelper {
int mRow;
int mChildHeight;
LinearLayout.LayoutParams mLayoutParams;
public RecipientShowHelper() {
}
public void mesure(ViewGroup vg, int margin, boolean isLimit2Row, int viewWidth) {
final int count = vg.getChildCount();
mRow = 0;// which row lay you view relative to parent
int lengthX = 0; // right position of child relative to parent
mChildHeight = 0; // the height of child
for (int i = 0; i < count; i++) {
final View child = vg.getChildAt(i);
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width = child.getMeasuredWidth();
mChildHeight = child.getMeasuredHeight();
lengthX += width + margin;
// if it can't drawing on a same line , skip to next line
if (lengthX > viewWidth) {
lengthX = width + margin + 0;
mRow++;
}
}
if (isLimit2Row) {
if (mRow >= 1) {
mLayoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, (2 * mChildHeight + margin * 4));
} else {
mLayoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, (mChildHeight + margin * 2));
}
}
}
public static void layout(int left, int top, int right, int bottom,
ViewGroup vg, int margin) {
final int count = vg.getChildCount();
int row = 0;// which row lay you view relative to parent
int lengthX = left; // right position of child relative to parent
int lengthY = top; // bottom position of child relative to parent
for (int i = 0; i < count; i++) {
final View child = vg.getChildAt(i);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
lengthX += width + margin;
lengthY = row * (height + margin) + margin + height + top;
// if it can't drawing on a same line , skip to next line
if (lengthX > right) {
lengthX = width + margin + left;
row++;
lengthY = row * (height + margin) + margin + height + top;
}
child.layout(lengthX - width, lengthY - height, lengthX, lengthY);
}
}
public int getRow() {
return mRow;
}
public int getChildHeight() {
return mChildHeight;
}
public LinearLayout.LayoutParams getLayoutParams() {
return mLayoutParams;
}
}
//RecipientShower.java
public class RecipientShower extends ViewGroup {
private final static String TAG = "RecipientShower";
private static final String TEL = "tel";
private int mViewWidth = 200;
private float mDensity = 1.0f;
private ScrollView mScrollView;
private Contact mContact;
private ContactList mContactListRecipient;
private ContactList mContactListRecipientCC;
protected SetTheOtherRecioientEditorListener mSetTextListener;
public static final int RECEPIENT_TYPE_NORMAL = 0;
public static final int RECEPIENT_TYPE_RECENT = 1;
public static final int RECEPIENT_TYPE_CC = 2;
public final static int VIEW_MARGIN = 4;
private int mTextView_id = -1;
private boolean isNewCompose = true;
private int mRow = -1;
private int mChildHeight = -1;
private RecipientShowHelper mShowHelper = new RecipientShowHelper();
public RecipientShower(Context context) {
super(context);
}
public RecipientShower(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public RecipientShower(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void initViewSize(Activity activity) {
DisplayMetrics metric = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metric);
mViewWidth = metric.widthPixels - 10;
mDensity = metric.density;
}
public void setViewWidth(int width) {
this.mViewWidth = width;
}
public void setmScrollView(ScrollView mScrollView) {
this.mScrollView = mScrollView;
}
public void setmDensity(float mDensity) {
this.mDensity = mDensity;
}
public void setmContext(Context mContext) {
this.mContext = mContext;
}
public void setmSetTextListener(SetTheOtherRecioientEditorListener mSetTextListener) {
this.mSetTextListener = mSetTextListener;
}
public boolean isNewCompose() {
return isNewCompose;
}
public void setNewCompose(boolean isNewCompose) {
this.isNewCompose = isNewCompose;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
}
public void removeAllViews() {
super.removeAllViews();
mContactListRecipient.clear();
mContactListRecipientCC.clear();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!isNewCompose) {
getScrollLayoutParams();
}
setMeasuredDimension(mViewWidth, ((mRow + 1) * mChildHeight + VIEW_MARGIN * (mRow + 1) * 2));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
RecipientShowHelper.layout(left, top, right, bottom, this, VIEW_MARGIN);
if (mScrollView != null) {
mScrollView.scrollTo(0, getHeight());
}
}
public void addTextViewChild(int recipientType, Contact contact) {
Paint paint = new Paint();
String text = contact.getName();
int background;
int textview_id;
if (recipientType == RECEPIENT_TYPE_NORMAL) {
background = R.drawable.pressed_span_bg;
mContactListRecipient.add(contact);
textview_id = mContactListRecipient.size();
} else {
background = R.drawable.pressed_span_cc_bg;
text = "CC:" + text;
mContactListRecipientCC.add(contact);
textview_id = mContactListRecipientCC.size() * -1;
}
paint.setTextSize(16.0f);
int width = (int) (paint.measureText(text) * mDensity);
TextView tv = new TextView(mContext);
tv.setId(textview_id);
tv.setTextSize(16.0f);
tv.setText(text);
tv.setBackgroundResource(background);
// tv.setWidth(width);
tv.setHeight(mContext.getResources().getDimensionPixelSize(R.dimen.span_button_height));
tv.setSingleLine(true);
/*
* if (isLandscape()) { tv.setMaxWidth((int)
* getResources().getDimension(
* R.dimen.recipient_editor_span_width_land)); } else {
* tv.setMaxWidth((int)
* getResources().getDimension(R.dimen.recipient_editor_span_width_port
* )); }
*/
tv.setMaxWidth(mViewWidth - VIEW_MARGIN);
tv.setClickable(true);
tv.setOnClickListener(onClickListener);
addView(tv);
}
public void reAddTextViewChild() {
final int count = getChildCount();
for (int index = 0; index < count; index++) {
final View child = this.getChildAt(index);
String text;
int background;
Contact contact;
int textview_id = child.getId();
if (textview_id > 0) {
background = R.drawable.pressed_span_bg;
contact = mContactListRecipient.get(textview_id - 1);
text = contact.getName();
} else {
background = R.drawable.pressed_span_cc_bg;
contact = mContactListRecipientCC.get((textview_id * -1) - 1);
text = "CC:" + contact.getName();
}
TextView tv = new TextView(mContext);
tv.setId(child.getId());
tv.setTextSize(16.0f);
tv.setText(text);
tv.setBackgroundResource(background);
tv.setHeight(mContext.getResources().getDimensionPixelSize(R.dimen.span_button_height));
tv.setSingleLine(true);
tv.setMaxWidth(mViewWidth - VIEW_MARGIN);
tv.setClickable(true);
tv.setOnClickListener(onClickListener);
removeViewAt(index);
addView(tv, index);
}
}
public LinearLayout.LayoutParams getScrollLayoutParams() {
mShowHelper.mesure(this, VIEW_MARGIN, isNewCompose, mViewWidth);
mRow = mShowHelper.getRow();
mChildHeight = mShowHelper.getChildHeight();
return mShowHelper.getLayoutParams();
}
public interface SetTheOtherRecioientEditorListener {
/**
* before edit the number,move the old number to
* mRecipientsEditor_Orginal.
*
* @param number
*/
void setTheOtherRecipientEditorText(String number);
/**
* If the contact is exist in the mNotInDbRecipientCCMap or
* mInDbRecipientCCMap on new compose message return true,else return
* false.
*
* @param contact
* @return
*/
boolean isExistInRecipientCCMap_New_Message(Contact contact);
/**
* synchronized the Contactlist in RecipientsEditor.
*/
void syncContactList(ContactList contactListRecipient, ContactList contactListRecipientCC);
}
}
<!--布局文件-->
<ScrollView
android:id="@+id/my_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:fadingEdge="none">
<com.android.mms.thundersoft.ui.RecipientShower
android:id="@+id/recipient_shower"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</ScrollView>