自定义ViewGroup实现随机泡泡效果

来源:互联网 发布:白蜡杆 淘宝 编辑:程序博客网 时间:2024/05/01 20:16

因为项目需要,去学习了自定义ViewGroup,第一次写自定义控件,算是给自己记个笔记,也希望可以帮到有需要的朋友,如果有写的不好的地方,还请大神指导。有问题可以加群:421448830 交流。

1、先看看效果图

这里写图片描述

2、自定义ViewGroup的代码:因为我们项目需求最多是四个,所以我只写了四个的情况,如果有其他的需求,可在此基础上修改,不过这个不建议过多数量的子布局,少量可以使用。且计算时要注意当出现最多子view的数量的时候,父布局的宽高要能放的下所有的子view。

public class MyViewGroup extends ViewGroup {    private static final String TAG = "My_ViewGroup";    public MyViewGroup(Context context) {        super(context);    }    public MyViewGroup(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(), attrs);    }    @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);        /**         * 记录如果是wrap_content是设置的宽和高         */        int width = 0;        int height = 0;        int cCount = getChildCount();        int cWidth = 0;        int cHeight = 0;        MarginLayoutParams cParams = null;        // 用于计算左边两个childView的高度        int lHeight = 0;        // 用于计算右边两个childView的高度,最终高度取二者之间大值        int rHeight = 0;        // 用于计算上边两个childView的宽度        int tWidth = 0;        // 用于计算下面两个childiew的宽度,最终宽度取二者之间大值        int bWidth = 0;        /**         * 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时         */        for (int i = 0; i < cCount; i++) {            View childView = getChildAt(i);            cWidth = childView.getMeasuredWidth();            cHeight = childView.getMeasuredHeight();            cParams = (MarginLayoutParams) childView.getLayoutParams();            // 上面两个childView            if (i == 0 || i == 1) {                tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;            }            if (i == 2 || i == 3) {                bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;            }            if (i == 0 || i == 2) {                lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;            }            if (i == 1 || i == 3) {                rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;            }        }        width = Math.max(tWidth, bWidth);        height = Math.max(lHeight, rHeight);        /**         * 如果是wrap_content设置为我们计算的值         * 否则:直接设置为父容器计算的值         */        setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth                : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight                : height);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int marginTop = 0; //最大的marginTop值        int cCount = getChildCount();        int cWidth = 0;        int cHeight = 0;        MarginLayoutParams cParams = null;        int top_one = 0;        int top_two = 0;        int top_three = 0;        /**         * 遍历所有childView根据其宽和高,以及margin进行布局         */        for (int i = 0; i < cCount; i++) {            View childView = getChildAt(i);            cWidth = childView.getMeasuredWidth();            cHeight = childView.getMeasuredHeight();            cParams = (MarginLayoutParams) childView.getLayoutParams();            int cl = 0, ct = 0, cr = 0, cb = 0;            switch (cCount) {                case 1:                    marginTop = getHeight() - cHeight - cParams.bottomMargin;                    break;                case 2:                    marginTop = (getHeight() / 2) - cHeight - cParams.bottomMargin;                    break;                case 3:                    marginTop = (getHeight() / 3) - cHeight - cParams.bottomMargin;                    break;                case 4:                    marginTop = (getHeight() / 4) - cHeight - cParams.bottomMargin;                    break;            }            switch (i) {                case 0:                    top_one = (int) (Math.random() * marginTop);                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));                    ct = top_one;                    break;                case 1:                    top_two = top_one + cHeight + (int) (Math.random() * marginTop);                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));                    ct = top_two;                    break;                case 2:                    top_three = top_two + cHeight + (int) (Math.random() * marginTop);                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));                    ct = top_three;                    break;                case 3:                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));                    ct = top_three + cHeight + (int) (Math.random() * marginTop);                    break;            }            cr = cl + cWidth;            cb = cHeight + ct;            childView.layout(cl, ct, cr, cb);        }    }}

3、activity_main.xml布局:使用这个自定义控件,其宽高最好给固定值或者match_parent,因为每一次出现的子布局数量不定,需要根据父布局的宽高决定其位置的随机数。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#FFFFFF"    android:orientation="vertical">    <com.xiaoxiao.myviewgroup.MyViewGroup        android:id="@+id/my_viewgroup"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_above="@+id/btn_start"/>    <Button        android:id="@+id/btn_start"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        android:layout_marginRight="10dp"        android:text="开始" /></RelativeLayout>

4、每一个随机冒出的泡泡的布局,item_viewgroup_paopao.xml布局,这个可以根据自己的需要去写。其中CircleImageView是圆形头像的自定义控件。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content">    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerVertical="true"        android:layout_marginLeft="-20dp"        android:layout_toRightOf="@+id/img_head"        android:background="@drawable/shape_paopao_background"        android:orientation="vertical">        <TextView            android:id="@+id/tv_get_task_nick"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginLeft="25dp"            android:text="ID:2***3"            android:textColor="#ef4359"            android:textSize="12sp" />        <TextView            android:id="@+id/tv_get_task_type"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginLeft="25dp"            android:text="冒出一个快乐泡泡"            android:textColor="#4A4A4B"            android:textSize="12sp" />    </LinearLayout>    <com.xiaoxiao.myviewgroup.CircleImageView        android:id="@+id/img_head"        android:layout_width="40dp"        android:layout_height="40dp"        android:layout_centerVertical="true"        android:scaleType="centerCrop"        android:src="@drawable/default_head" /></RelativeLayout>

5、MainActivity的代码,PaopaoBean是一个实体类,用来存放数据,可以根据具体情况去写。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private final static String TAG = "ViewGroupActivity";    List<PaopaoBean> mList = new ArrayList<>();    private RelativeLayout relativeLayout;    private boolean isStart = true;    private AlphaAnimation alphaAnimation;    MyViewGroup my_viewgroup;    Button btn_start;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        my_viewgroup = (MyViewGroup) findViewById(R.id.my_viewgroup);        btn_start = (Button) findViewById(R.id.btn_start);        btn_start.setText("开始");        btn_start.setOnClickListener(this);    }    private void showDate() {        mList.clear();        Random random = new Random();        int count = random.nextInt(4) + 1;        Log.e(TAG, "count==" + count);        int id_front;        int id_after;        int task_type;        String task_name = "";        for (int i = 0; i < count; i++) {            task_type = random.nextInt(3);            id_front = random.nextInt(9) + 1;            id_after = random.nextInt(10);            switch (task_type) {                case 0:                    task_name = "开心";                    break;                case 1:                    task_name = "快乐";                    break;                case 2:                    task_name = "幸福";                    break;            }            mList.add(new PaopaoBean("ID:" + id_front + "***" + id_after, "冒出一个" + task_name + "泡泡"));        }        getPaopao(mList, my_viewgroup);    }    private void getPaopao(List<PaopaoBean> paopao, MyViewGroup viewgroup) {        viewgroup.removeAllViews();        for (int i = 0; i < paopao.size(); i++) {            relativeLayout = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.item_viewgroup_paopao, my_viewgroup, false);            ImageView img_head = (ImageView) relativeLayout.findViewById(R.id.img_head);            TextView tv_nick = (TextView) relativeLayout.findViewById(R.id.tv_get_task_nick);            TextView tv_type = (TextView) relativeLayout.findViewById(R.id.tv_get_task_type);            viewgroup.addView(relativeLayout);            Glide.with(this).load(R.drawable.default_head).into(img_head);            tv_nick.setText(paopao.get(i).getTv_nick());            tv_type.setText(paopao.get(i).getTv_type());        }        generateAlphaAnimation();    }    //渐变动画    private void generateAlphaAnimation() {        Log.e(TAG, "222");        alphaAnimation = new AlphaAnimation(1f, 0);  //渐变动画        alphaAnimation.setDuration(1500);        alphaAnimation.setStartOffset(1500);        alphaAnimation.start();        my_viewgroup.startAnimation(alphaAnimation);        alphaAnimation.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {            }            @Override            public void onAnimationEnd(Animation animation) {                if (!isStart) {                    alphaAnimation.cancel();                    my_viewgroup.clearAnimation();                    mList.clear();                    my_viewgroup.removeAllViews();                    relativeLayout.removeAllViews();                    new Handler().postDelayed(new Runnable() {                        @Override                        public void run() {                            if (!isStart) {                                showDate();                            }                        }                    }, 2000);                }            }            @Override            public void onAnimationRepeat(Animation animation) {            }        });    }    @Override    public void onClick(View v) {        if (isStart) {            btn_start.setText("停止");            isStart = false;            showDate();        } else {            alphaAnimation.cancel();            my_viewgroup.clearAnimation();            mList.clear();            my_viewgroup.removeAllViews();            btn_start.setText("开始");            relativeLayout.removeAllViews();            isStart = true;        }    }}

6、PaopaoBean 类:

public class PaopaoBean {    private String tv_nick;    private String tv_type;    public PaopaoBean( String tv_nick, String tv_type) {        this.tv_nick = tv_nick;        this.tv_type = tv_type;    }    public String getTv_nick() {        return tv_nick;    }    public void setTv_nick(String tv_nick) {        this.tv_nick = tv_nick;    }    public String getTv_type() {        return tv_type;    }    public void setTv_type(String tv_task) {        this.tv_type = tv_task;    }}

源码地址:http://blog.csdn.net/miaoxiaomo/article/details/52936832

6 0
原创粉丝点击