android根据子view里面的数量自动排版的一个ViewGroup

很多时候 android系统给我们的控件不能满足我们的需求,要使界面做的好,所以大部分都是自己写的控件 。

在我做一个 短信群发界面时,需要 这个 自适应子view数量 改变自身高度的 view 所以就自写了一个 这样的控件

现 分享出来 和大家一起共享。  当然  如果朋友 下载了  添加了一些新功能  也请 帮忙 分享 出来 谢谢。

大家一起成长。

废话少说 上代码:

MainActivity

public class MainActivity extends ActionBarActivity implements OnClickListener{

	private Button button;
	private EditText edit;
	private ScrollView scrollview;
	private MyViewGroup myviewgroup;
	private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initControl();

		/**
		 * 设置  回调接口
		 */
		myviewgroup.setMyViewGroupInterface(new MyViewGroupInterface() {
			@Override
			public void setScrollViewHeight(int scrollViewHeight,final boolean isAdd) {
				//设置 scrollview的高度  注意 scrollview 的父容器是哪个布局  下面的LayoutParams就应该选择对应的
				//如 scrollview的父容器是 LinearLayout 则下面应该用
//				final LinearLayout.LayoutParams rl = (android.widget.LinearLayout.LayoutParams) scrollview.getLayoutParams();
				final RelativeLayout.LayoutParams rl = (LayoutParams) scrollview.getLayoutParams();
				rl.height = scrollViewHeight;
				handler.post(new Runnable() {
					@Override
					public void run() {
						scrollview.setLayoutParams(rl);
						if(isAdd) //如果是添加 就要将 位置定位到最新添加的子view上
							scrollview.scrollBy(0, myviewgroup.getGroupViewheight());
					}
				});
			}
		});

	}

	/**
	 * 初始化控件
	 */
	private void initControl(){
		handler = new Handler();
		button = (Button) this.findViewById(R.id.button);
		edit = (EditText) this.findViewById(R.id.edit);
		scrollview = (ScrollView) this.findViewById(R.id.scrollview);
		myviewgroup = (MyViewGroup) this.findViewById(R.id.myviewgroup);
		button.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch(v.getId()){
		case R.id.button:
			String text = edit.getText().toString().trim();
			if(TextUtils.isEmpty(text)){
				Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show();
				break;
			}
			addChildView(text);
			break;
		}
	}

	@SuppressLint("NewApi")
	private void addChildView(String text)
	{
		final TextView tv = new TextView(this);
		tv.setText(text);
		tv.setSingleLine(true);
		tv.setTextSize(TypedValue.COMPLEX_UNIT_SP,12);
		tv.setMaxEms(6);
		tv.setTextColor(Color.BLACK);
		tv.setAlpha(0.5f);
		int dp = DpToPx.dip2px(this, 8);
		tv.setPadding(dp,dp,dp,dp);
		tv.setBackgroundResource(R.drawable.text_style);
		tv.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				myviewgroup.removeView(tv);		//点击 删除
			}
		});
		myviewgroup.addView(tv, myviewgroup.getChildCount());//添加
	}

}

MyViewGroup
public class MyViewGroup extends ViewGroup {

	public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public MyViewGroup(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MyViewGroup(Context context) {
		super(context);
	}

	public void setMyViewGroupInterface(MyViewGroupInterface face)
	{
		this.face = face;
	}

	private int count;		//改变前的 子View数量
	private boolean isAdd; //判断 是添加还是删除
	private MyViewGroupInterface face;

	/**
	 * 计算 各个子View的位置
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
//		 if (!changed)
//			 return;
		int i = getChildCount();
		if(count == i)
			return;
		isAdd = true;
		if(count > i)		//改变前的子view数量 大于 当前的数量 表示 删除
			isAdd = false;
		count = i;
		int width = getMeasuredWidth();

		int childWidth = 0;
		int childHeight = 0;

		int totalWidth = leftSpacing + rightSpacing;
		int currentTopPosition = topSpacing;
		int currentLeftPosition = leftSpacing;

		int maxHeight = 0;
		for (int j = 0; j < i; j++) {
			View v = getChildAt(j);
			childWidth = v.getMeasuredWidth();
			childHeight = v.getMeasuredHeight();
			if (j == 0) {
				totalWidth += childWidth;
				maxHeight = childHeight;
			} else {
				totalWidth += (childWidth + left_Right_Spacing);
				if (totalWidth > width) {
					currentTopPosition += (maxHeight + top_Bottom_Spacing);
					currentLeftPosition = leftSpacing;
					maxHeight = childHeight;
					totalWidth = leftSpacing + rightSpacing + childWidth;
				} else {
					if (childHeight > maxHeight) {
						maxHeight = childHeight;
					}
					View upChild = getChildAt(j-1);
					int upChildWidth = upChild.getMeasuredWidth();
					currentLeftPosition += (upChildWidth + left_Right_Spacing);
				}
			}
			l = currentLeftPosition;
			t = currentTopPosition;
			r = childWidth + currentLeftPosition;
			b = currentTopPosition + childHeight;
			v.layout(l, t, r, b);
		}
		if(i == 0){
			if(face != null)
				face.setScrollViewHeight(0,isAdd);
		}else{
			if(face != null)
				face.setScrollViewHeight(scrollViewHeight,isAdd);
		}

	}

	private final int leftSpacing = 10; // 子View离父容器左边的距离
	private final int rightSpacing = 10;// 子View离父容器右边的距离
	private final int topSpacing = 8;// 子View离父容器上边的距离
	private final int bottomSpacing = 8;// 子View离父容器底边的距离
	private final int left_Right_Spacing = 10;// 两个子View左右之间的间隔
	private final int top_Bottom_Spacing = 10;// 两个子View上下之间的间隔
	private final int umberRows = 3;//父容器中能够窜下子View的最大行数
	private int groupHeight;
	private int scrollViewHeight;
	private int height,width;

	/**
	 * 计算 父容器的宽高
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		// 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
		int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

		// 计算出所有的childView的宽和高
		measureChildren(widthMeasureSpec, heightMeasureSpec);
		width = sizeWidth;// ViewGroup 的宽
		height = topSpacing + bottomSpacing;// ViewGroup 的高

		int childCount = this.getChildCount();// 子View的总个数

		int childWidth = 0;// 子View的宽
		int childHeight = 0;// 子View的高

		int currentWidth = leftSpacing + rightSpacing;// 子View相加的 宽
		int currentHeight = 0;
		int row = 0;//子View的行数
		for (int i = 0; i < childCount; i++) {
			View childView = getChildAt(i);
			childWidth = childView.getMeasuredWidth();
			childHeight = childView.getMeasuredHeight();
			if (i == 0) {
				row++;
				currentWidth += childWidth;
				currentHeight = childHeight;
			} else {
				currentWidth += (childWidth + left_Right_Spacing);
			}
			if (currentWidth > width) {//当子View相加的宽大于父容器的宽时 自动换行 并重置currentWidth 和 currentHeight
				row++;
				currentWidth = leftSpacing + rightSpacing + childWidth;
				height += (currentHeight + top_Bottom_Spacing);
				currentHeight = childHeight;
			} else {
				if (childHeight > currentHeight) {
					currentHeight = childHeight;
				}
			}
			if(row <= umberRows)
			{
				scrollViewHeight = height+ currentHeight;
			}
		}
		height += currentHeight;
		groupHeight = height;
		Log.e("", "scrollViewHeight == "+scrollViewHeight);
		setMeasuredDimension(width, height);
	}

	public int getGroupViewheight()
	{
		return groupHeight;
	}

	@Override
	public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
		new MarginLayoutParams(getContext(), attrs);
		return super.generateLayoutParams(attrs);
	}

	/**
	 * 改变 scrollview高度的 回调接口
	 * @author linxi
	 *
	 */
	public interface MyViewGroupInterface
	{
		public void setScrollViewHeight(int scrollViewHeight,boolean isAdd);
	}

}

DpToPx
public class DpToPx {

	/**
	 * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
	 */
	public static int dip2px(Context context, float dpValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dpValue * scale + 0.5f);
	}

	/**
	 * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
	 */
	public static int px2dip(Context context,float pxValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (pxValue / scale + 0.5f);
	}

     /**
      * 将px值转换为sp值,保证文字大小不变
      *
      * @param pxValue
      * @param fontScale
      *            (DisplayMetrics类中属性scaledDensity)
      * @return
      */
     public static int px2sp(Context context, float pxValue) {
         final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
         return (int) (pxValue / fontScale + 0.5f);
     } 

     /**
      * 将sp值转换为px值,保证文字大小不变
      *
      * @param spValue
      * @param fontScale
      *            (DisplayMetrics类中属性scaledDensity)
      * @return
      */
     public static int sp2px(Context context, float spValue) {
         final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
         return (int) (spValue * fontScale + 0.5f);
     }

}
activity_main.xml
<RelativeLayout 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" >

    <View
        android:id="@+id/v"
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:layout_marginTop="10dp"
        android:background="#FF2525" />

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_below="@+id/v"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp" >

        <com.example.adaptivewidthheightviewgroup.MyViewGroup
            android:id="@+id/myviewgroup"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </com.example.adaptivewidthheightviewgroup.MyViewGroup>
    </ScrollView>

    <View
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:layout_below="@+id/scrollview"
        android:background="#195794" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >

        <EditText
            android:id="@+id/edit"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:hint="@string/input_content"
            android:inputType="text"
            android:paddingBottom="5dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:paddingTop="5dp"
            android:textColor="#000000"
            android:textSize="18sp" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="10dp"
            android:text="@string/add"
            android:textColor="#000000"
            android:textSize="18sp" />
    </LinearLayout>

</RelativeLayout>

  

有需要 源码的 朋友 可以 在评论里面写邮箱 ,看到我会发给你。


  


  

时间: 2024-10-28 14:41:19

android根据子view里面的数量自动排版的一个ViewGroup的相关文章

Android FrameLayout子view居中(左居中,右居中)等

Android的布局FrameLayout默认是把布局内的子view堆砌在左上角,但是,可以通过设置子view的: android:layout_gravity 此参数控制子view的布局位置,实现FrameLayout的各种子view布局位置,如左居中,右居中,居中等等,要点在android:layout_gravity的参数设置中组合使用"center"."left"."right"等,现在给出XML代码实例布局: <FrameLayo

UIViewController中的view自动排版(ios6和ios7)

问题提出,ios6和ios7中视图控制器的创建中的view的是会自动排版的,就是你自定义其中的view,也改变不了其尺寸,ios6自定义的尺寸是{{0, 20}, {320, 460}},ios7自定义的尺寸是{{0, 0}, {320, 480}}这就为兼容性造成很大的困扰? //解决方案,苹果这么设计貌似是为了自动匹配设备方向的改变,那么我们就可以在设备支持方向里面进行尺寸的修改, eg:- (BOOL)shouldAutorotateToInterfaceOrientation:(UIIn

android:clipChildren属性的分析——是否剪裁子View

MainActivity如下: package cc.testclipchildren; import android.os.Bundle; import android.app.Activity; /** * android:clipChildren属性的分析 * 该属性默认值为android:clipChildren="true" * 单从字面意思理解clipChildren的意思是:裁剪(缩短)孩子 * 我们将其值设置为false后那么当子控件的高度高于父控件时 * 也会完全显示

Android中如何将子View的坐标转换为父View的坐标

最近打算照着Android的Launcher2源码写一个精简的带有拖动功能的Launcher.在分析DragLayer类的时候发现了一个有趣方法——getDescendantCoordRelativeToSelf.通过一下两篇文章的介绍和自己的实验,总算是弄清楚了该方法的原理. http://blog.csdn.net/hahajluzxb/article/details/8165258 http://www.cnblogs.com/platte/p/3534279.html 下面主要分析一下代

Android中父View和子view的点击事件的执行过程

Android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解. 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP 当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewG

android事件如何分发给子view

哈哈,第一次使用markdown,看着挺高大上的啊.如果顺手了,会直接切换默认为markdown. 话说关于android事件分发的博客真的不在少数,基本都是基于源码分析+实例代码的形式讲解.今天的这篇博客呢,主要的侧重点并不是在事件分发上,而是在事件的转换上. 为什么需要事件转换? 打个比方吧: 我们点击一个TextView的左上角,加入这个TextView在它老子的中间位置,那我们点击的x/y应该是多少呢? 在它老子那这两个值可能是100/100,而在TextView上打印就会是1/1了,也

android XML布局和子View按比例布局

首先按照程序的目录结构大致分析: res/layout/ 这个目录存放的就是布局用的xml文件,一般默认为main.xml res/values/ 这个目录存放的是一堆常量的xml文件 res/drawable/ 存放的是一些图片什么的,当然图标也在这里 下面主要对layout下的xml文件做个介绍,顺便也把布局的方法总结一下: ·文件的开头 <?xml version="1.0" encoding="utf-8"?> 这是在说明xml版本及字符编码 ·

Android 深入了解View(二)

相信每个Android程序员都知道,我们每天的开发工作当中都在不停地跟View打交道,Android中的任何一个布局.任何一个控件其实都是直接或间接继承自View的,如TextView.Button.ImageView.ListView等.这些控件虽然是Android系统本身就提供好的,我们只需要拿过来使用就可以了,但你知道它们是怎样被绘制到屏幕上的吗?多知道一些总是没有坏处的,那么我们赶快进入到本篇文章的正题内容吧. 要知道,任何一个视图都不可能凭空突然出现在屏幕上,它们都是要经过非常科学的绘

Android开发:View的几种布局及实践

引言 View的布局显示方式有下面几种:线性布局(Linear Layout).相对布局(Relative Layout).表格布局(Table Layout).网格视图(Grid View).标签布局(Tab Layout).列表视图(List View).绝对布局(AbsoluteLayout).本文虽然是介绍View的布局方式,但不仅仅是这样,其中涉及了很多小的知识点,绝对能给你带来Android大餐! 本文的主要内容就是分别介绍以上视图的七种布局显示方式效果及实现,大纲如下: 1.Vie