一、概述
继续咱们的直播之旅,过段时间再把推流拉流写上博客,暂时还是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。
它是继承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