Introduction to the

The end of the year is coming, to everyone worship an early year, I wish everyone a good luck in the year of the chicken ~ ha ha dirty classmates face the wall! Without further ado, the effect implemented today is floating View when live streaming. I wonder if the SDK of live streaming all provides UI. Let’s take a look at a rendering:





Realize the effect drawing

(Of course, the live video is the main, here do not care about the video, the focus is the View, here just play a video at random.)

  1. The first is a ListView that slides across the top to show the members of the room.
  2. ListView automatically scrolls to the bottom;
  3. Custom brush gift View, automatically disappear over a period of time;
  4. DialogFragment+ViewPager+GridView;
  5. [Fixed] Listen for events that pop up on the keyboard when clicking on a comment
  6. [Fixed] Heart-sending effects are displayed at the bottom of the broadcast (third-party tyrant: Heartlayout is used here)
  7. VedioView plays video;

The implementation process

The first thing to remember is that the video is at the bottom, and above the video is the View floating, so I’m using the ViewPager to right swipe to “hide” the View, which is actually toggling. Very simple layout:

<?xml version="1.0" encoding="utf-8"? >
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/wether_bg"
    android:orientation="vertical">
    <cm.wzh.live.view.MyVideoView
        android:id="@+id/video"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:background="@null"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v4.view.ViewPager>
</RelativeLayout>Copy the code

Next, implement two fragments, one is a blank Fragment (NoFragment) and the other is a ChatFragment with various special effects, and then fill it into the ViewPager. Of course, it also needs to realize the video playback. Very simple VedioView, I won’t say more about ~

// Read the video from the Raw resource file
final String uri = ("android.resource://" + this.getPackageName() + "/raw/vedio");
videoView.setVideoURI(Uri.parse(uri));
videoView.start();
// The video can be looped
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp) {
        videoView.setVideoURI(Uri.parse(uri)); videoView.start(); }});Copy the code

Let’s look at the main chatfragments:

package cm.wzh.live.ui;

import android.content.Context;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.VideoView;

import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;

import cm.wzh.live.R;
import cm.wzh.live.adapter.MemberAdapter;
import cm.wzh.live.adapter.MessageAdapter;
import cm.wzh.live.entity.Gift;
import cm.wzh.live.entity.Member;
import cm.wzh.live.entity.Message;
import cm.wzh.live.utils.CharUtils;
import cm.wzh.live.view.FragmentDialog;
import cm.wzh.live.view.FragmentGiftDialog;
import cm.wzh.live.view.GiftItemView;
import cm.wzh.live.view.HorizontialListView;
import cm.wzh.live.view.MyVideoView;
import tyrantgit.widget.HeartLayout;

/** * author: Administrator on 2016/12/26 09:35 * description: File description * version: version */
public class ChatFragment extends Fragment  implements View.OnClickListener.View.OnLayoutChangeListener {
    private HorizontialListView listview ;
    private ListView messageList ;
    private GiftItemView giftView ;
    private MemberAdapter mAdapter ;
    private MessageAdapter messageAdapter ;
    private ArrayList<Member> members ;
    private ArrayList<Message> messages ;
    private ArrayList<Gift> gifts ;
    private HeartLayout heartLayout ;
    private Random mRandom ;
    private Timer mTimer = new Timer();
    private View sendView,menuView ,topView;
    private EditText sendEditText ;
    // Screen height
    private int screenHeight = 0;
    // Set the height threshold of the software disk
    private int keyHeight = 0;
    private View rootView ;


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_chat,null.false);
        initView(view);
        initData();
        return view;
    }

    private void initView(View view) {
        mRandom = new Random();
        listview = (HorizontialListView) view.findViewById(R.id.list);
        mAdapter = new MemberAdapter(getActivity());
        listview.setAdapter(mAdapter);
        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<? > adapterView, View view,int i, long l) { showDialog(mAdapter.datas.get(i)); }}); messageList = (ListView) view.findViewById(R.id.list_message); messageAdapter =new MessageAdapter(getActivity());
        messageList.setAdapter(messageAdapter);
        giftView = (GiftItemView) view.findViewById(R.id.gift_item_first);
        heartLayout = (HeartLayout)view.findViewById(R.id.heart_layout);
        handler.postDelayed(runnable, 2000);// Execute runnable every 5 seconds.
        view.findViewById(R.id.send_message).setOnClickListener(this);
        view.findViewById(R.id.gift).setOnClickListener(this);
        sendView = view.findViewById(R.id.layout_send_message);
        menuView = view.findViewById(R.id.layout_bottom_menu);
        topView = view.findViewById(R.id.layout_top);
        sendEditText = (EditText) view.findViewById(R.id.send_edit);
        // Get the screen height
        screenHeight = getActivity().getWindowManager().getDefaultDisplay().getHeight();
        // Set the threshold to 1/3 of the screen height
        keyHeight = screenHeight/3;
        rootView = view.findViewById(R.id.activity_main);
        rootView.addOnLayoutChangeListener(this);
    }

    private void showDialog(Member m) {
        FragmentDialog.newInstance(m.name, m.sig, "Sure"."Cancel".- 1.false.new FragmentDialog.OnClickBottomListener() {
            @Override
            public void onPositiveClick(a) {}@Override
            public void onNegtiveClick(a) {

            }
        }).show(getChildFragmentManager(),"dialog");

    }

    Handler handler=new Handler();
    Runnable runnable=new Runnable() {
        @Override
        public void run(a) {
            if(messages! =null){
                Message m = new Message();
                m.img = "http://v1.qzone.cc/avatar/201503/06/18/27/54f981200879b698.jpg%21200x200.jpg" ;
                m.name=CharUtils.getRandomString(8); m.level = (int)(Math.random()*100+1); m.message= CharUtils.getRandomString(20);
                messages.add(m);
                messageAdapter.notifyDataSetChanged();
                messageList.setSelection(messageAdapter.getCount()- 1);
            }
            handler.postDelayed(this.1000); }}; Handler heartHandler=new Handler();
    Runnable heartRunnable=new Runnable() {
        @Override
        public void run(a) {
            heartLayout.post(new Runnable() {
                @Override
                public void run(a) { heartLayout.addHeart(randomColor()); }}); heartHandler.postDelayed(this.1000); }};@Override
    public void onPause(a) {
        super.onPause();
        heartHandler.removeCallbacks(heartRunnable);
    }

    @Override
    public void onResume(a) {
        super.onResume();
        heartHandler.postDelayed(heartRunnable, 2000);
    }

    private int randomColor(a) {
        return Color.rgb(mRandom.nextInt(255), mRandom.nextInt(255), mRandom.nextInt(255));
    }
    /** * add some data */
    private void initData(a) {
        members = new ArrayList<>();
        for (int i=0; i<18; i++){ Member m =new Member();
            m.img = "http://www.ld12.com/upimg358/allimg/c150808/143Y5Q9254240-11513_lit.png" ;
            m.name="Baby" ;
            m.sig = "This guy is lazy and has nothing left!";
            members.add(m);
        }
        mAdapter.setDatas(members);


        messages = new ArrayList<>();
        for (int i=0; i<18; i++){ Message m =new Message();
            m.img = "http://www.ld12.com/upimg358/allimg/c150808/143Y5Q9254240-11513_lit.png" ;
            m.name="Baby" ;
            m.level = i ;
            m.message="Nuggets is the highest quality technology sharing community in China, inviting rare earth users as co-editor to share high-quality technical dry goods." ;
            messages.add(m);
        }
        messageAdapter.setDatas(messages);

        gifts = new ArrayList<>();
    }

    @Override
    public void onDestroy(a) {
        super.onDestroy();
        mTimer.cancel();
        handler.removeCallbacks(runnable);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId() ;
        if (id==R.id.send_message){
            sendView.setVisibility(View.VISIBLE);
            menuView.setVisibility(View.GONE);
            topView.setVisibility(View.GONE);
            sendEditText.requestFocus();
            InputMethodManager inputManager =
                    (InputMethodManager)sendEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            inputManager.showSoftInput(sendEditText, 0);
        }else if (id==R.id.gift){
            FragmentGiftDialog.newInstance().setOnGridViewClickListener(new FragmentGiftDialog.OnGridViewClickListener() {
                @Override
                public void click(Gift gift) {
                    gift.name="Literati";
                    gift.giftName = "Here's a little gift for you." ;
                    if(! gifts.contains(gift)){ gifts.add(gift); giftView.setGift(gift); } giftView.addNum(1);
                }
            }).show(getChildFragmentManager(),"dialog"); }}@Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        // The soft keyboard is now considered to spring up whenever the control pushes the Activity up more than 1/3 of the screen height
        if(oldBottom ! =0&& bottom ! =0 &&(oldBottom - bottom > keyHeight)){
            sendView.setVisibility(View.VISIBLE);
            menuView.setVisibility(View.GONE);
            topView.setVisibility(View.GONE);
// toast.makeText (mainactivity.getActivity (), "Listen for soft keyboard up..." , Toast.LENGTH_SHORT).show();
        }else if(oldBottom ! =0&& bottom ! =0 &&(bottom - oldBottom > keyHeight)){
            sendView.setVisibility(View.GONE);
            menuView.setVisibility(View.VISIBLE);
            topView.setVisibility(View.VISIBLE);
// toast.makeText (mainactivity.getActivity (), "Listen when the software disk closes..." , Toast.LENGTH_SHORT).show();}}}Copy the code

Here you can see that the View that brushes the gift has been wrapped as a GiftItemView.

package cm.wzh.live.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Handler;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.ArrayList;

import cm.wzh.live.R;
import cm.wzh.live.entity.Gift;

/** * author: Administrator on 2016/12/27 09:34 * description: File description * version: version */
public class GiftItemView extends LinearLayout {

    private ImageView avatar ;
    private TextView name ;
    private TextView giftName ;
    private TextView giftNumTv ;
    private ImageView giftIv ;
    private Gift gift ;

    private int giftNum = 1 ;
    private boolean isShow = false ;

    public GiftItemView(Context context) {
        this(context,null);
    }

    public GiftItemView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public GiftItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(a) {
        setOrientation(VERTICAL);
        setVisibility(INVISIBLE);
        LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        setLayoutParams(lp);
        View convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_gift_message,null.false);
        avatar = (ImageView) convertView.findViewById(R.id.avatar);
        giftIv = (ImageView) convertView.findViewById(R.id.gift_type);
        name = (TextView) convertView.findViewById(R.id.name);
        giftName = (TextView) convertView.findViewById(R.id.gift_name);
        giftNumTv = (TextView) convertView.findViewById(R.id.gift_num);
        addView(convertView);
    }

    public void setGift(Gift gift) {
        this.gift = gift;
        refreshView();
    }

    /** * Set the gift number to enlarge and restore the View *@param view
     * @param duration
     */
    public void scaleView(View view,long duration){
        AnimatorSet animatorSet = new AnimatorSet();// Combine animation
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX".2f, 1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY".2f, 1f);
        animatorSet.setDuration(duration);
        animatorSet.setInterpolator(new LinearInterpolator());
        animatorSet.play(scaleY).with(scaleX);// Both animations start at the same time
        animatorSet.start();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if(onAnimatorListener! =null){ onAnimatorListener.onAnimationEnd(gift); }}@Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                if(onAnimatorListener! =null){ onAnimatorListener.onAnimationStart(animation); }}}); }/** * Refresh view */
    public void refreshView(a){
        if (gift==null) {return;
        }
        giftNum = gift.num ;
        if(! TextUtils.isEmpty(gift.img)){ Glide.with(getContext()).load(gift.img).placeholder(R.drawable.default_head).into(avatar);  }else {
            avatar.setImageResource(R.drawable.default_head);
        }
        name.setText(gift.name);
        giftName.setText(gift.giftName);
        giftNumTv.setText("x"+gift.num);
        giftIv.setImageResource(gift.giftType);
        scaleView(giftNumTv,200);
    }

    /** * Continuously click to send gifts when the number zooming effect *@param num
     */
    public void addNum(int num){
        giftNum += num ;
        giftNumTv.setText("x"+giftNum);
        scaleView(giftNumTv,200);
        handler.removeCallbacks(runnable);
        if(! isShow()){ show(); }handler.postDelayed(runnable, 3000);
    }
    Handler handler=new Handler();
    Runnable runnable=new Runnable() {
        @Override
        public void run(a) {
            isShow = false ;
            giftNum = 0; setVisibility(INVISIBLE); }};/** * Display view and start timer */
    public void show(a){
        isShow = true ;
        setVisibility(VISIBLE);
        handler.postDelayed(runnable, 3000);
    }

    public boolean isShow(a) {
        return isShow;
    }

    private OnAnimatorListener onAnimatorListener ;

    public void setOnAnimatorListener(OnAnimatorListener onAnimatorListener) {
        this.onAnimatorListener = onAnimatorListener;
    }

    public interface OnAnimatorListener{
        public void onAnimationEnd(Gift gift);
        public void onAnimationStart(Animator animation); }}Copy the code

Handler+Runnable is used to execute timed tasks. When a gift is sent continuously, it is re-timed. ObjectAnimator is used to animate the number of gifts. Automatic scrolling for ListViews is a timed task that adds each Item to the ListView collection and then uses ListView.setSelection (int Adapter.getCount ()) to locate the last Item.

Interested can take a look at the source code: Github download address github has apK files can be downloaded to experience. If you have any questions, please point them out, thank you ~