Most of the code has been deleted from the source code and optimized into the new version. The new profile to: http://www.jianshu.com/p/6649f5239aef


Attention! Due to a version update, most of the code below no longer exists. The address of the project: https://github.com/xujiaji/DMView

# Outline, renderings

Introduction to the

Recently, the company’s project requirements require the implementation of bullet screen, which is the reason why I write this bullet screen demo. At present, the addition of bullets has been realized, and the simple encapsulation of the realization part. You can add a barrage by calling addBarrage(String Name, String MSG, String PIC) to pass the name, message and avatar address. The bullet screen is added from bottom to top again (default is 10 lines). After the total number of lines is filled, the lines that have been slid below will be filled first.

# thought

  1. Because RecyclerView can add item animation
  2. Each barrage is an object when initializedisLive = trueIndicates active status
  3. When the barrage endsisLive = falseRepresents inactive state (its value is assigned by end of animation listener)
  4. When initializing ten shells behind the scenes (default ten lines), the loop checksisLiveWhether it isfalseIf so, reset the content and then update the corresponding row.

implementation

1. First, how to animate

  • Animation realize copy some classes of the project: https://github.com/wasabeef/recyclerview-animators
  • The main animation parent is:BaseItemAnimator.
  • Then you create oneBaseItemAnimatorA subclass ofOverTotalLengthAnimator, to achieve the animation effect from right to left, add the end of the animation listening, detailed code as follows:
package com.jiaji.dmview.recyclerview_item_anim;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

public class OverTotalLengthAnimator extends BaseItemAnimator {
    @Override
    protected void animateRemoveImpl(RecyclerView.ViewHolder holder) {
        Log.e("TAG"."animateRemoveImpl...........................");
    }

    @Override
    protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {
        Log.e("TAG"."preAnimateRemoveImpl...........................");
    }
// After data is added, calling notifyItemInserted performs this method first, moving the Item header to the right edge
    @Override
    protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
        Log.e("TAG"."preAnimateAddImpl...........................");
        ViewCompat.setTranslationX(holder.itemView, holder.itemView.getRootView().getWidth());
    }
// After preAnimateAddImpl is executed, this method calls startAnimation to animate from right to left
    @Override
    protected void animateAddImpl(RecyclerView.ViewHolder holder) {
        Log.e("TAG"."animateAddImpl...........................");
        startAnimation(holder);
    }
// This method is called when an item is filled in, instead of being added, the previous barrage object is reused, and 'notifyItemChanged' is updated.
    @Override
    public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
        Log.e("TAG"."animateChange...........................");
// Since the item is hidden after the animation, the initialization should be displayed
        newHolder.itemView.setVisibility(View.VISIBLE);
        ViewCompat.setTranslationX(newHolder.itemView, newHolder.itemView.getRootView().getWidth());
        startAnimation(newHolder);
        return true;
    }

// Start the animation, the entire process defaults to 8 seconds, when the animation ends call over to end the listening
    private void startAnimation(final RecyclerView.ViewHolder holder) {
        ViewCompat.animate(holder.itemView)
                .translationX(-holder.itemView.getRootView().getWidth())
                .setDuration(8000)
                .setListener(new ViewPropertyAnimatorListener() {
                    @Override
                    public void onAnimationStart(View view) {
                        Log.e("TAG"."onAnimationStart");
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        holder.itemView.setVisibility(View.GONE);
                        if(onAnimListener ! =null) {
                            onAnimListener.over();
                        }
                        Log.e("TAG"."onAnimationEnd");
                    }

                    @Override
                    public void onAnimationCancel(View view) {
                        Log.e("TAG"."onAnimationCancel");
                    }
                })
                .setStartDelay(getAddDelay(holder))
                .start();
    }
    private OnAnimListener onAnimListener;
    public void setOnAnimListener(OnAnimListener l) {
        this.onAnimListener = l;
    }
    public interface OnAnimListener {
        void over(a); }}Copy the code

2. Fill in data

  • Initialize RecyclerView, vertical layout, add from bottom to top.
  • Check whether it can continue to add (whether more than 10 lines), if not, the loop checks whether there is an item at the end of the animation, if there is, the item will be updated. If not, it will be added to the cache list. After each animation, it will continue to add the bullet-screen objects in the cache list.
  • The previous implementation part was implemented in MainActivity, but later in order to facilitate the use of this later (I said in case one day), so it is written inBarrageUtilThe inside. Take a look atBarrageUtil:
package com.jiaji.dmview.barrage;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;

import com.jiaji.dmview.recyclerview_item_anim.OverTotalLengthAnimator;

import java.util.ArrayList;
import java.util.List;

/** * Created by Administrator on 2016/6/7. */
public class BarrageUtil {
    private Context context;/ / context
    private RecyclerView rvBarrage;// Display the RecyclerView of bullets
    private List<BarrageEntity> barrageList;// Fill the list of RecyclerView
    private BarrageAdapter mBarrageAdapter;// Barrage adapter
    private OverTotalLengthAnimator anim;// Live screen animation
    private List<Integer> indexList;// Save the current barrage subscript
    private List<BarrageEntity> barrageCache;// Cache a barrage object that cannot be added when the current screen is full
    private LinearLayoutManager layoutManager;

    public BarrageUtil(Context context, RecyclerView rvBarrage) {
        this.context = context;
        this.rvBarrage = rvBarrage;
        init();
    }

    private void init(a) {
        barrageList = new ArrayList<>();
        barrageCache = new ArrayList<>();
        indexList = new ArrayList<>();
        layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true);
        rvBarrage.setLayoutManager(layoutManager);
        anim = new OverTotalLengthAnimator();
        rvBarrage.setItemAnimator(anim);
        mBarrageAdapter = new BarrageAdapter(barrageList);
        rvBarrage.setAdapter(mBarrageAdapter);
// this is called when the animation ends
        anim.setOnAnimListener(new OverTotalLengthAnimator.OnAnimListener() {
            @Override
            public void over(a) {
                int index = indexList.get(0);// Get the index that ends this item
                barrageList.get(index).over();// Get the object with the corresponding subscript, call the over() method on that object, and set isLive to false
                indexList.remove(0);// Remove the subscript of the currently ending item from the movement subscript collection
                if(! barrageCache.isEmpty()) {// Check whether the cached barrage list has a barrage
                    BarrageEntity b = barrageCache.get(0);
                    addBarrage(b.getPname(), b.getChatStr(), b.getPic());
                    barrageCache.remove(0); }}}); }public void addBarrage(String name, String msg, String pic) {
    // Log.e("TAG", "visible_item_position = " + layoutManager.findFirstCompletelyVisibleItemPosition());
        boolean isAdd = false;
        if (barrageList.size() >= 10) {// If it is greater than 10, it will not be added
            for (int i = 0, len = barrageList.size(); i < len; i++) {
                BarrageEntity barrageEntity = barrageList.get(i);
                if (barrageEntity.isLive()) {
                    continue;
                }

                if (rvBarrage.isComputingLayout()) {// notifyItemChanged cannot be changed when RecyclerView is computing, there is a certain probability of backout, so if the layout is computing, then jump out of the loop directly
                    isAdd = false;
                    break;
                }
                barrageEntity.change(name, msg, pic);
                mBarrageAdapter.notifyItemChanged(i);
                indexList.add(i);
                isAdd = true;
                break; }}else {
            isAdd = true;
            BarrageEntity barrageEntity = new BarrageEntity(name, msg, pic);
            barrageList.add(barrageEntity);
            mBarrageAdapter.notifyItemInserted(barrageList.size() - 1);
            indexList.add(barrageList.size() - 1);
        }

        if(! isAdd) {// Add it to the bullet-screen cache list if not successfully added
            barrageCache.add(newBarrageEntity(name, msg, pic)); }}}Copy the code

3. Barrage objects

package com.jiaji.dmview.barrage;

/**
 * Created by Administrator on 2016/6/6.
 */
public class BarrageEntity {
    private String pname;
    private String chatStr;
    private String pic;
    private boolean isLive;

    public BarrageEntity(String pname, String chatStr, String pic) {
        this.pname = pname;
        this.chatStr = chatStr;
        this.pic = pic;
        isLive = true;
    }

    public void change(String pname, String chatStr, String pic) {
        this.pname = pname;
        this.chatStr = chatStr;
        this.pic = pic;
        isLive = true;
    }

    public void over() {
        isLive = false;
    }

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public String getChatStr() {
        return chatStr;
    }

    public void setChatStr(String chatStr) {
        this.chatStr = chatStr;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) { this.pic = pic; }}Copy the code

Use 4.

package com.jiaji.dmview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.jiaji.dmview.barrage.BarrageUtil;

import java.util.Date;

public class MainActivity extends AppCompatActivity {
    private BarrageUtil mBarrageUtil;
    private RecyclerView rvBarrage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rvBarrage = (RecyclerView) findViewById(R.id.rvBarrage);
        mBarrageUtil = new BarrageUtil(this, rvBarrage);
    }

    public void onAddClick(View view) {
        mBarrageUtil.addBarrage(new Date().toString(), "Chat messages..."."Https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=150237755, & FM = 116 & 4294706681 gp = 0. JPG"); }}Copy the code

So I wrote this demo. At that time, I thought of only ① custom layout to add sub-layout of bullet screen and then add animation. ② This demo is because I thought of RecyclerView to achieve such animation is easier to write and understand. I hope I can help you realize a bullet screen idea. Internet pictures load using the Glide: compile ‘com. Making. Bumptech. Glide: Glide: 3.7.0’

The demo address

https://github.com/xujiaji/DMView