android 仿照蚂蚁森林弹幕功能

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010648159/article/details/81233904

需求

实现一个跟支付宝蚂蚁森林的弹幕一样的效果,每次增加一条弹幕就显示到末尾,每次只能显示一条,一条显示一多半的时候就立马显示下一条。

效果图如下
这里写图片描述

第一步:

将所有的childView摆放位置(摆放到屏幕外)

 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        int childCount = getChildCount();
        if (childCount <= 0) {
            return;
        }
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView == null) {
                return;
            }
            LayoutParams layoutParams = (LayoutParams) childView.getLayoutParams();
            childView.layout(mScreenWidth, layoutParams.topMargin, mScreenWidth + childView.getMeasuredWidth(), layoutParams.topMargin + childView.getMeasuredHeight());
        }
    }

重写了onLayout方法,将所有的childView的位置设置为mScreenWidth+childView.getMeasuredWidth(),这样就保证了childView默认在屏幕外。

第二步:

添加要显示的弹幕view,获取view的宽高,开启平移属性动画,并且在属性动画执行完成的时候移除它,在动画的更新监听中发送消息给下一条要显示的view。

public void createBarrageItemView(final BarrageBean barrageBean) {
        final View itemView = LayoutInflater.from(getContext()).inflate(R.layout.layout_barrage, null);
        TextView tvBarrageContent = itemView.findViewById(R.id.id_tv_barrage_content);
        tvBarrageContent.setText(barrageBean.getContent());

        // 获取itemView的宽高
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        itemView.measure(widthMeasureSpec, heightMeasureSpec);

        int width = itemView.getMeasuredWidth();
        int height = itemView.getMeasuredHeight();

        LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        layoutParams.topMargin = 1 * height;
        addView(itemView, layoutParams);

        final float totalWidth = mScreenWidth + width; // 平移的宽度
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(itemView, "translationX", 0, -(totalWidth));
        objectAnimator.setInterpolator(new LinearInterpolator());
        objectAnimator.setDuration(DURATION);
        objectAnimator.start();
        objectAnimator.addListener(new AnimatorListenerAdapter() {

            @Override public void onAnimationEnd(Animator animation) {
                removeView(itemView);
            }
        });
        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                if (value < -totalWidth / 2 && !barrageBean.isStartTranslationX()) {
                    mBarrageQueue.pop();
                    barrageBean.setStartTranslationX(true);
                    mHandler.sendEmptyMessage(MSG_WAHT_1);
                }
            }
        });
    }

写完这些代码之后,我以为能成,结果点击按钮添加一条弹幕,没有显示,找了一会之后发现得在你的xml文件中加上 android:clipchildren=”false”这个属性,不显示childview的位置和大小。切记!

public class BarrageView extends RelativeLayout {

    private final int               MSG_WAHT_1      = 1;

    private final int               DURATION        = 5000;                                                     // 时长

    private int                     mScreenWidth;                                                               // 屏幕宽度

    // private List<String> mBarrageList = new ArrayList<>(); // 总共有多少条弹幕
    private ArrayDeque<BarrageBean> mBarrageQueue   = new ArrayDeque<>();                                       // 总共有多少条弹幕

    private Handler                 mHandler        = new Handler() {

                                                        @Override public void handleMessage(Message msg) {
                                                            if (mBarrageQueue.isEmpty()) {
                                                                return;
                                                            }
                                                            BarrageBean barrageBean = mBarrageQueue.getFirst();
                                                            createBarrageItemView(barrageBean);
                                                        }
                                                    };

    public BarrageView(@NonNull Context context) {
        this(context, null);
    }

    public BarrageView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BarrageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mScreenWidth = ActivityUtil.getScreenWidth(context);
    }

    @Override public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
    }

    @Override protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        int childCount = getChildCount();
        if (childCount <= 0) {
            return;
        }
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView == null) {
                return;
            }
            LayoutParams layoutParams = (LayoutParams) childView.getLayoutParams();
            childView.layout(mScreenWidth, layoutParams.topMargin, mScreenWidth + childView.getMeasuredWidth(), layoutParams.topMargin + childView.getMeasuredHeight());
        }
    }

    /***
     * 设置数据
     * 
     * @param list
     *            集合数据
     */
    public void setDatas(List<BarrageBean> list) {
        if (list == null || list.isEmpty()) {
            return;
        }
        mBarrageQueue.addAll(list);
        if (mBarrageQueue.size() == list.size()) {
            mHandler.sendEmptyMessageDelayed(MSG_WAHT_1, 0);
        }
    }

    /***
     * 如果当前弹幕集合为空,那么添加进来的弹幕就直接显示, 如果当前弹幕集合不为空,则直接添加到集合中即可
     * 
     * @param barrageBean
     */
    public void addBarrage(BarrageBean barrageBean) {
        mBarrageQueue.add(barrageBean);
        if (mBarrageQueue.size() == 1) {
            mHandler.sendEmptyMessageDelayed(MSG_WAHT_1, 0);
        }
    }

    /***
     * 创建一个弹幕itemView
     *
     * @param barrageBean
     *            内容
     */
    public void createBarrageItemView(final BarrageBean barrageBean) {
        final View itemView = LayoutInflater.from(getContext()).inflate(R.layout.layout_barrage, null);
        TextView tvBarrageContent = itemView.findViewById(R.id.id_tv_barrage_content);
        tvBarrageContent.setText(barrageBean.getContent());

        // 获取itemView的宽高
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        itemView.measure(widthMeasureSpec, heightMeasureSpec);

        int width = itemView.getMeasuredWidth();
        int height = itemView.getMeasuredHeight();

        LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        layoutParams.topMargin = 1 * height;
        addView(itemView, layoutParams);

        final float totalWidth = mScreenWidth + width; // 平移的宽度
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(itemView, "translationX", 0, -(totalWidth));
        objectAnimator.setInterpolator(new LinearInterpolator());
        objectAnimator.setDuration(DURATION);
        objectAnimator.start();
        objectAnimator.addListener(new AnimatorListenerAdapter() {

            @Override public void onAnimationEnd(Animator animation) {
                removeView(itemView);
            }
        });
        objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                if (value < -totalWidth / 2 && !barrageBean.isStartTranslationX()) {
                    mBarrageQueue.pop();
                    barrageBean.setStartTranslationX(true);
                    mHandler.sendEmptyMessage(MSG_WAHT_1);
                }
            }
        });
    }

    @Override protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mHandler != null) {
            mHandler.removeMessages(MSG_WAHT_1);
            mHandler = null;
        }
    }

}
public class BarrageActivity extends Activity implements View.OnClickListener {

    private BarrageView id_barrage;

    private TextView    id_tv_add;

    private TextView    id_tv_start;

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

        id_barrage = findViewById(R.id.id_barrage);
        id_tv_add = findViewById(R.id.id_tv_add);
        id_tv_start = findViewById(R.id.id_tv_start);

        id_tv_add.setOnClickListener(this);
        id_tv_start.setOnClickListener(this);
    }

    @Override public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.id_tv_add:
                BarrageBean barrageBean2 = new BarrageBean();
                barrageBean2.setContent("弹幕"+System.currentTimeMillis());
                id_barrage.addBarrage(barrageBean2);

                break;
            case R.id.id_tv_start:
                List<BarrageBean> list = new ArrayList<>();
                BarrageBean barrageBean = null;
                for (int i = 0; i < 10; i++) {
                    barrageBean = new BarrageBean();
                    barrageBean.setContent("弹幕"+i);
                    list.add(barrageBean);
                }
                id_barrage.setDatas(list);

                break;
        }
    }
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <cms.wxj.community.view.BarrageView
        android:id="@+id/id_barrage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipChildren="false" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/id_tv_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginRight="10dp"
            android:background="@color/colorAccent"
            android:gravity="center"
            android:padding="10dp"
            android:text="添加一个弹幕"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/id_tv_start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            android:background="@color/colorAccent"
            android:gravity="center"
            android:padding="10dp"
            android:text="开始播放弹幕"
            android:textColor="#ffffff" />
    </LinearLayout>
</LinearLayout>

参考博客地址
博客地址

猜你喜欢

转载自blog.csdn.net/u010648159/article/details/81233904
今日推荐