天天看点

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

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

1、先看看效果图

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

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 = ;
        int height = ;

        int cCount = getChildCount();

        int cWidth = ;
        int cHeight = ;
        MarginLayoutParams cParams = null;

        // 用于计算左边两个childView的高度
        int lHeight = ;
        // 用于计算右边两个childView的高度,最终高度取二者之间大值
        int rHeight = ;

        // 用于计算上边两个childView的宽度
        int tWidth = ;
        // 用于计算下面两个childiew的宽度,最终宽度取二者之间大值
        int bWidth = ;

        /**
         * 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时
         */
        for (int i = ; i < cCount; i++) {
            View childView = getChildAt(i);
            cWidth = childView.getMeasuredWidth();
            cHeight = childView.getMeasuredHeight();
            cParams = (MarginLayoutParams) childView.getLayoutParams();

            // 上面两个childView
            if (i ==  || i == ) {
                tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
            }

            if (i ==  || i == ) {
                bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
            }

            if (i ==  || i == ) {
                lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
            }

            if (i ==  || i == ) {
                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 = ; //最大的marginTop值
        int cCount = getChildCount();
        int cWidth = ;
        int cHeight = ;
        MarginLayoutParams cParams = null;

        int top_one = ;
        int top_two = ;
        int top_three = ;

        /**
         * 遍历所有childView根据其宽和高,以及margin进行布局
         */
        for (int i = ; i < cCount; i++) {
            View childView = getChildAt(i);
            cWidth = childView.getMeasuredWidth();
            cHeight = childView.getMeasuredHeight();
            cParams = (MarginLayoutParams) childView.getLayoutParams();

            int cl = , ct = , cr = , cb = ;

            switch (cCount) {
                case :
                    marginTop = getHeight() - cHeight - cParams.bottomMargin;
                    break;
                case :
                    marginTop = (getHeight() / ) - cHeight - cParams.bottomMargin;
                    break;
                case :
                    marginTop = (getHeight() / ) - cHeight - cParams.bottomMargin;
                    break;
                case :
                    marginTop = (getHeight() / ) - cHeight - cParams.bottomMargin;
                    break;
            }

            switch (i) {
                case :
                    top_one = (int) (Math.random() * marginTop);
                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));
                    ct = top_one;
                    break;
                case :
                    top_two = top_one + cHeight + (int) (Math.random() * marginTop);
                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));
                    ct = top_two;
                    break;
                case :
                    top_three = top_two + cHeight + (int) (Math.random() * marginTop);
                    cl = (int) (Math.random() * (getWidth() - cWidth - cParams.rightMargin));
                    ct = top_three;
                    break;
                case :
                    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() + ;
        Log.e(TAG, "count==" + count);

        int id_front;
        int id_after;
        int task_type;
        String task_name = "";

        for (int i = ; i < count; i++) {
            task_type = random.nextInt();
            id_front = random.nextInt() + ;
            id_after = random.nextInt();
            switch (task_type) {
                case :
                    task_name = "开心";
                    break;
                case :
                    task_name = "快乐";
                    break;
                case :
                    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 = ; 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(f, );  //渐变动画
        alphaAnimation.setDuration();
        alphaAnimation.setStartOffset();
        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();
                            }
                        }
                    }, );
                }
            }

            @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