Android仿直播特效之刷礼物

一、概述

继续咱们的直播之旅,过段时间再把推流拉流写上博客,暂时还是UI特效,先上图


二、创建我们的BaseActivity和BaseFrag

/**
 * @author 刘洋巴金
 * @date 2017-5-3
 * 
 * 基类
 * */
public abstract class BaseActivity extends FragmentActivity implements OnClickListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(getLayoutId());
		
		initBase();
		initView();
		initData();
		initListener();
	}

	/**
	 * 设置子类getLayoutId
	 * */
	public abstract int getLayoutId();
	
	/**
	 * 基类初始化
	 * */
	public void initBase() {
	}
	
	/**
	 * 子类初始化View
	 * */
	public void initView() {
	}

	/**
	 * 子类初始化数据
	 * */
	public void initData() {
	}
	
	/**
	 * 子类初始化监听
	 * */
	public void initListener() {
	}
	
	@Override
	public void onClick(View v) {
	}
}

baseFrag

/**
 * @author 刘洋巴金
 * @date 2017-5-3
 * 
 * Frag基类
 * */
public abstract class BaseFrag extends Fragment implements OnClickListener{

	public View view;
	public Context myContext;
	
	@Override
	public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

		view = inflater.inflate(getLayoutId(), null);
		
		// 初始化
		initBase();
		initView();
		initData();
		initListener();
		return view;
	}
	
	/**
	 * 设置子类getLayoutId
	 * */
	public abstract int getLayoutId();
	
	/**
	 * 基类初始化
	 * */
	public void initBase() {
		
		myContext = getActivity();
	}
	
	/**
	 * 子类初始化View
	 * */
	public void initView() {
	}

	/**
	 * 子类初始化数据
	 * */
	public void initData() {
	}
	
	/**
	 * 子类初始化监听
	 * */
	public void initListener() {
	}
	
	@Override
	public void onClick(View v) {
	}
}
和baseActivity差不多

这个就是我们的基类,然后是我们的主类

/**
 * @author 刘洋巴金
 * @date 2017-5-3
 * 
 * 主页
 * */
public class MainActivity extends BaseActivity {

	@Override
	public int getLayoutId() {
		
		return R.layout.activity_main;
	}

	@Override
	public void initData() {

		super.initData();

		// 加载直播fragment
		LiveFrag liveFrag = new LiveFrag();
		getSupportFragmentManager().beginTransaction().add(R.id.fl_root, liveFrag).commit();

        // 加载
		new InteractiveFrag().show(getSupportFragmentManager(), "InteractiveFrag");
	}
}
经过封装简单了很多吧?然后是加载fragment,LiveFrag就是我们的直播frag,现在目前为止就是一个图片,以后增加拉流等相关功能,然后加载我们的用户交互InteractiveFrag。

扫描二维码关注公众号,回复: 8939227 查看本文章

它是继承DialogFragment,这个不明白的可以百度查询,而这个也不是必须,可以自定义,

这里最主要的逻辑就是加了一个viewpager, 共加载了2个fragment,一个是我们交互用的,一个是透明的,这样是为了滑动隐藏我们的交互的功能,如上图最后的操作。EmptyFrag背景设置为透明,无任何逻辑

/**
 * 观众功能交互页面, 滑动隐藏效果
 * 
 * @author 刘洋巴金
 * @date 2017-5-3
 */
public class InteractiveFrag extends DialogFragment{

	public View view;
	public Context myContext;
	private ViewPager vp_interactive;
	private LayerFrag layerFrag;
	
	@Override
	public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

		view = inflater.inflate(R.layout.frag_interactive, null);
		
		// 初始化
		initView();
		initData();
		initListener();
		return view;
	}
	
	/**
	 * 初始化View
	 * */
	public void initView() {
		
		vp_interactive = (ViewPager)view.findViewById(R.id.vp_interactive);
	}

	/**
	 * 初始化数据
	 * */
	public void initData() {
		
        // EmptyFrag:什么都没有
        // LayerFrag:交互界面
        // 这样就达到了滑动隐藏交互的需求
		vp_interactive.setAdapter(new FragmentPagerAdapter(getChildFragmentManager()) {
			
			@Override
			public int getCount() {

				return 2;
			}
			
			@Override
			public Fragment getItem(int position) {

				if (position == 0){
					
					return new EmptyFrag(); // 返回空界面的fragment
				}else if (position == 1){
					
					return layerFrag = new LayerFrag(); // 返回交互界面的frag
				}else{ // 设置默认
					
					return new EmptyFrag();
				}
			}
		});
		// 设置默认显示交互界面
		vp_interactive.setCurrentItem(1);
		
		// 同时将界面改为resize已达到软键盘弹出时Fragment不会跟随移动
		getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
	}
	
	/**
	 * 初始化监听
	 * */
	public void initListener() {
		
		vp_interactive.setOnPageChangeListener(new OnPageChangeListener() {
			
			@Override
			public void onPageSelected(int position) {
				
				if(position == 0){

//					layerFrag.hideKeyboard();
				}
			}
			
			@Override
			public void onPageScrolled(int position, float arg1, int arg2) {
			}
			
			@Override
			public void onPageScrollStateChanged(int position) {
			}
		});
	}
	
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
    	
    	// 设置DialogFragment的样式,这里的代码最好还是用我的,大家不要改动
        Dialog dialog = new Dialog(getActivity(), R.style.MainDialog){
           
        	@Override
            public void onBackPressed() {
                super.onBackPressed();
            
                getActivity().finish();
            }
        };
        return dialog;
    }
}
三、实现我们的交互页面

/**
 * 用户交互页
 * 
 * @author 刘洋巴金
 * @date 2017-5-3
 */
public class LayerFrag extends BaseFrag{

	private NumberAnim giftNumberAnim;
    private List<String> messageData = new LinkedList<>();
	private MessageAdapter messageAdapter;
	private ListView lv_message;
	private HorizontalListView hlv_audience;
	private LinearLayout ll_gift_group;
	private TranslateAnimation outAnim;
	private TranslateAnimation inAnim;
	private LinearLayout ll_inputparent;
	private Button tv_chat;
	private EditText et_chat;
	private LinearLayout ll_anchor;
	private RelativeLayout rl_num;
    
	@Override
	public int getLayoutId() {
		// TODO Auto-generated method stub
		return R.layout.frag_layer;
	}
	
	@Override
	public void initView() {
		// TODO Auto-generated method stub
		super.initView();
		
		lv_message = (ListView)view.findViewById(R.id.lv_message);
		hlv_audience = (HorizontalListView)view.findViewById(R.id.hlv_audience);
		ll_gift_group = (LinearLayout)view.findViewById(R.id.ll_gift_group);
		ll_inputparent = (LinearLayout)view.findViewById(R.id.ll_inputparent);
		tv_chat = (Button)view.findViewById(R.id.tv_chat);
		et_chat = (EditText)view.findViewById(R.id.et_chat);
		ll_anchor = (LinearLayout)view.findViewById(R.id.ll_anchor);
		rl_num = (RelativeLayout)view.findViewById(R.id.rl_num);
	}
	
	@Override
	public void initData() {
		// TODO Auto-generated method stub
		super.initData();
		
		initAudience(); // 初始化观众
		initMessage(); // 初始化评论
		clearTiming(); // 开启定时清理礼物列表
		initAnim(); // 初始化动画
	}	
进行初始化和变量声明,首先初始化右上角的观众,其实就是个GridView,这里不再概述

/**
 * 初始化观众列表
 * */
private void initAudience() {

	hlv_audience.setAdapter(new AudienceAdapter(myContext));
}
然后初始化评论,左下角底部的评论列表,listview不再概述

/**
 * 初始化评论列表
 * */
private void initMessage() {
		
        for(int x = 0; x < 20; x++){
        	
            messageData.add("刘洋巴金: 主播好漂亮啊" + x);
        }
        messageAdapter = new MessageAdapter(getActivity(), messageData);
        lv_message.setAdapter(messageAdapter);
        lv_message.setSelection(messageData.size());
}
初始化动画

/**
 * 初始化动画
 * */
private void initAnim() {

	giftNumberAnim = new NumberAnim(); // 初始化数字动画
	inAnim = (TranslateAnimation) AnimationUtils.loadAnimation(getActivity(), R.anim.gift_in); // 礼物进入时动画
        outAnim = (TranslateAnimation) AnimationUtils.loadAnimation(getActivity(), R.anim.gift_out); // 礼物退出时动画
}

礼物的数字动画,就是简单的放大

/**
 * 送的礼物后面的数字动画
 * */
public class NumberAnim{
		
	private Animator lastAnimator;
		
	public void showAnimator(View v){
		
		if (lastAnimator != null) {
			lastAnimator.removeAllListeners();
			lastAnimator.cancel();
			lastAnimator.end();
		}

		ObjectAnimator animScaleX = ObjectAnimator.ofFloat(v, "scaleX", 1.3f, 1.0f);
		ObjectAnimator animScaleY = ObjectAnimator.ofFloat(v, "scaleY", 1.3f, 1.0f);
		AnimatorSet animSet = new AnimatorSet();
		animSet.playTogether(animScaleX, animScaleY);
		animSet.setDuration(200);
		lastAnimator = animSet;
		animSet.start();
	}
}
设置礼物的动画集合,使它放大1.3倍

clearTiming为礼物清理,3秒后自动清理,这个放在后面讲,先开始刷礼物

@Override
public void onClick(View v) {
	// TODO Auto-generated method stub
	super.onClick(v);
	switch (v.getId()) {
	 case R.id.btn_gift01: // 礼物1,送香皂
             showGift("gift01");
             break;
         case R.id.btn_gift02: // 礼物2,送玫瑰
             showGift("gift02");
             break;
         case R.id.btn_gift03: // 礼物3,送爱心
             showGift("gift03");
             break;
         case R.id.btn_gift04: // 礼物4,送蛋糕
             showGift("gift04");
             break;
4个按钮的点击事件

然后是刷礼物

/**
 * 刷礼物
 * */
private void showGift(String tag) {

	View newGiftView = ll_gift_group.findViewWithTag(tag);
		
	// 是否有该tag类型的礼物
	if(newGiftView == null){
			
		// 判断礼物列表是否已经有3个了,如果有那么删除掉一个没更新过的, 然后再添加新进来的礼物,始终保持只有3个
		if(ll_gift_group.getChildCount() >= 3){
				
			// 获取前2个元素的最后更新时间
			View giftView01 = ll_gift_group.getChildAt(0);
			ImageView iv_gift01 = (ImageView)giftView01.findViewById(R.id.iv_gift);
			long lastTime1 = (long) iv_gift01.getTag();
			
			View giftView02 = ll_gift_group.getChildAt(1);
			ImageView iv_gift02 = (ImageView)giftView02.findViewById(R.id.iv_gift);
			long lastTime2 = (long) iv_gift02.getTag();
			
			if (lastTime1 > lastTime2) { // 如果第二个View显示的时间比较长
	               
				removeGiftView(1);
                        } else { // 如果第一个View显示的时间长
                                removeGiftView(0);
	            }
		}
			
		// 获取礼物
		newGiftView = getNewGiftView(tag);
		ll_gift_group.addView(newGiftView);
			
		// 播放动画
		newGiftView.startAnimation(inAnim);
		final MagicTextView mtv_giftNum = (MagicTextView) newGiftView.findViewById(R.id.mtv_giftNum);
		inAnim.setAnimationListener(new AnimationListener() {
				
			@Override
			public void onAnimationStart(Animation animation) {
			}
				
			@Override
			public void onAnimationRepeat(Animation animation) {
			}
				
			@Override
			public void onAnimationEnd(Animation animation) {
				// TODO Auto-generated method stub
				giftNumberAnim.showAnimator(mtv_giftNum);
			}
		});
	}else{
		...
	}
}
1.这段是核心代码,首先评论去上方我们定义了一个LinearLayout,ll_gift_group,
2.然后根据点击事件传入tag,在 ll_gift_group查询是否有该tag的子控件,也就是是否有该种类的礼物,如果没有,判断当前不同种类是否已经3个了,如果是,那么移除一个最先更新的礼物。

3.位置空出来了,生成我们的礼物

/**
 * 获取礼物
 * */
private View getNewGiftView(String tag) {
		
	// 添加标识, 该view若在layout中存在,就不在生成(用于findViewWithTag判断是否存在)
	View giftView = LayoutInflater.from(myContext).inflate(R.layout.item_gift, null);
	giftView.setTag(tag);
		  
	// 添加标识, 记录生成时间,回收时用于判断是否是最新的,回收最老的
	ImageView iv_gift = (ImageView)giftView.findViewById(R.id.iv_gift);
	iv_gift.setTag(System.currentTimeMillis());
		
	// 添加标识,记录礼物个数
	MagicTextView mtv_giftNum = (MagicTextView) giftView.findViewById(R.id.mtv_giftNum);
	mtv_giftNum.setTag(1);
	mtv_giftNum.setText("x1");
	    
        switch (tag){
             case "gift01":
            	 iv_gift.setImageResource(GiftIcon[0]);
                 break;
             case "gift02":
            	 iv_gift.setImageResource(GiftIcon[1]);
                 break;
             case "gift03":
            	 iv_gift.setImageResource(GiftIcon[2]);
                 break;
             case "gift04":
            	 iv_gift.setImageResource(GiftIcon[3]);
                 break;
        }
        
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        lp.topMargin = 10;
        giftView.setLayoutParams(lp);
      
	return giftView;
}
1.生成我们的礼物控件,然后绑定tag进行标识,就是点击事件传递过来的tag,

2.找其子控件,再绑定更新时间,因为要回收更新最早的控件,保留最后更新的

3.找其子控件,添加标识,记录该种礼物的个数

4.设置礼物图片

5.设置大小返回

好继续回到我们的showGift方法

4.生成礼物后,添加到 ll_gift_group中,并执行进入动画,MagicTextView为我们自定义的礼物数量的字体样式,这个没有规定可随意自定义

5.礼物进入动画执行完毕后,再执行数字动画,就是放大

好了,然后我们又刷了一个礼物。

根据

View newGiftView = ll_gift_group.findViewWithTag(tag);
判读是否该类型的礼物还没有被清理掉

如果是,那么我们走else

View newGiftView = ll_gift_group.findViewWithTag(tag);
		
// 是否有该tag类型的礼物
if(newGiftView == null){
			
	...
}else{
	// 如果列表中已经有了该类型的礼物,则不再新建,直接拿出
	// 更新标识,记录最新修改的时间,用于回收判断
	ImageView iv_gift = (ImageView)newGiftView.findViewById(R.id.iv_gift);
	iv_gift.setTag(System.currentTimeMillis());
			
	// 更新标识,更新记录礼物个数
        MagicTextView mtv_giftNum = (MagicTextView) newGiftView.findViewById(R.id.mtv_giftNum);
	int giftCount = (int) mtv_giftNum.getTag() + 1; // 递增
        mtv_giftNum.setText("x" + giftCount);
        mtv_giftNum.setTag(giftCount);
	giftNumberAnim.showAnimator(mtv_giftNum);
}
1.首先拿出礼物控件的子控件,更新它的更新时间

2.然后再拿出其子控件更新他的礼物数量的标识。

3.更改UI,执行数字动画

这样礼物就刷出去了,但是为了用户体验,礼物要3秒钟之后消失

/**
 * 定时清理礼物列表信息
 */
private void clearTiming() {
		
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
			
       @Override
       public void run() {

		int childCount = ll_gift_group.getChildCount();
		long nowTime = System.currentTimeMillis();
		for (int i = 0; i < childCount; i++) {
					
			View childView = ll_gift_group.getChildAt(i);
    		        ImageView iv_gift = (ImageView)childView.findViewById(R.id.iv_gift);
			long lastUpdateTime = (long) iv_gift.getTag();
					
			// 更新超过3秒就刷新
			if(nowTime - lastUpdateTime >= 3000){
				removeGiftView(i);
			}
		}
	}
    }, 0, 3000);
}
这个就比较简单了,拿出 ll_gift_group中全部的控件,遍历他们,取出他们最后更新的时间,然后对比当前时间,如果谁的时间超过3秒了,那么执行移除

/**
 * 移除礼物列表里的giftView
 * */
private void removeGiftView(final int index) {

	// 移除列表,外加退出动画
	final View removeGiftView = ll_gift_group.getChildAt(index);
	outAnim.setAnimationListener(new AnimationListener() {
			
		@Override
		public void onAnimationStart(Animation animation) {
		}
			
		@Override
		public void onAnimationRepeat(Animation animation) {
		}
			
		@Override
		public void onAnimationEnd(Animation animation) {
			// TODO Auto-generated method stub
			ll_gift_group.removeViewAt(index);
		}
	});

	// 开启动画,因为定时原因,所以可能是在子线程
	getActivity().runOnUiThread(new Runnable() {
			
		@Override
		public void run() {
				
			removeGiftView.startAnimation(outAnim);
		}
	});
}
移除礼物,然后执行退出动画。

好了刷礼物就到这吧,不明白的话,底下评论

四、demo

Android仿直播特效之刷礼物

发布了30 篇原创文章 · 获赞 78 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/u010302765/article/details/71617446