搜索
简帛阁>技术文章>Android仿直播类app赠送礼物功能

Android仿直播类app赠送礼物功能

直播界面

实现的是播放本地的视频文件:

/**
 * 直播界面,用于对接直播功能
 */
public class LiveFrag extends Fragment {

 private ImageView img_thumb;
 private VideoView video_view;

 @Nullable
 @Override
 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 View view = inflater.inflate(R.layout.frag_live, null);
 img_thumb = view.findViewById(R.id.img_thumb);
 img_thumb.setVisibility(View.GONE);
 video_view = view.findViewById(R.id.video_view);
 video_view.setVisibility(View.VISIBLE);
 video_view.setVideoURI(Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.video_1));
 video_view.start();
 video_view.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
 @Override
 public void onCompletion(MediaPlayer mp) {
 video_view.setVideoURI(Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.video_1));
 //或 //mVideoView.setVideoPath(Uri.parse(_filePath));
 video_view.start();
 }
 });
 return view;
 }
}

布局文件 frag_live.xml 如下:

<?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">
 <VideoView
 android:id="@+id/video_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:clickable="false"
 android:focusable="false"
 android:visibility="gone" />
 <ImageView
 android:id="@+id/img_thumb"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:clickable="false"
 android:focusable="false"
 android:scaleType="centerCrop"
 android:src="@mipmap/img_video_1"
 android:visibility="visible" />
</LinearLayout>

滑动隐藏效果

需要实现的效果如下:

自定义DialogFragment,使用ViewPager,第一个为空的Fragment,第二个为我们需要的Fragment,左右滑动来切换显示和隐藏效果。

观众功能交互页面 InteractiveFrag 如下:

/**
 * 观众功能交互页面, 滑动隐藏效果
 */
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();
 return view;
 }

 /**
 * 初始化View
 */
 public void initView() {
 vp_interactive = 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);
 }

 @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;
 }
}

frag_interactive.xml文件如下:

<?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" >

 <androidx.viewpager.widget.ViewPager
 android:id="@+id/vp_interactive"
 android:layout_width="match_parent"
 android:layout_height="match_parent" />
</LinearLayout>

用户交互页 LayerFrag:

public class LayerFrag extends Fragment {
 @Nullable
 @Override
 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 return inflater.inflate(R.layout.frag_layer, null);
 }
}

frag_layer:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical">
 <LinearLayout
 android:id="@+id/ll_anchor"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:gravity="center_vertical"
 android:orientation="horizontal"
 android:paddingLeft="10dp"
 android:paddingTop="10dp">
 <RelativeLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content">
 <LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerVertical="true"
 android:background="@drawable/bg_radius_top_black"
 android:gravity="center_vertical"
 android:orientation="vertical"
 android:paddingLeft="55dp"
 android:paddingTop="2dp"
 android:paddingRight="10dp"
 android:paddingBottom="2dp">
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="十三妹哦"
 android:textColor="@android:color/white"
 android:textSize="12sp" />
 <LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:gravity="center_vertical"
 android:orientation="horizontal">
 <ImageView
 android:layout_width="35dp"
 android:layout_height="20dp"
 android:src="@drawable/hani_icon_tag_exp" />
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="5dp"
 android:text="17万"
 android:textColor="@android:color/white"
 android:textSize="10sp" />
 </LinearLayout>
 </LinearLayout>
 <com.hongx.zhibo.utils.CircleImageView
 android:id="@+id/lv_anchorIcon"
 android:layout_width="50dp"
 android:layout_height="50dp"
 android:src="@drawable/zf"
 app:border_color="@color/colorWhite"
 app:border_width="1dp" />
 </RelativeLayout>
 <com.hongx.zhibo.utils.HorizontalListView
 android:id="@+id/hlv_audience"
 android:layout_width="match_parent"
 android:layout_height="45dp"
 android:layout_marginLeft="10dp" />
 </LinearLayout>
 <RelativeLayout
 android:id="@+id/rl_num"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_below="@+id/ll_anchor"
 android:layout_marginTop="5dp"
 android:paddingLeft="10dp"
 android:paddingRight="10dp">
 <LinearLayout
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:background="@drawable/bg_radius_bottom_pink"
 android:gravity="center_vertical"
 android:paddingLeft="10dp"
 android:paddingTop="2dp"
 android:paddingRight="10dp"
 android:paddingBottom="2dp">
 <ImageView
 android:layout_width="20dp"
 android:layout_height="10dp"
 android:src="@drawable/molive_icon_charm_lv_20" />
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="5dp"
 android:text="小时榜单第5名"
 android:textColor="#fff"
 android:textSize="10sp" />
 </LinearLayout>
 <TextView
 android:id="@+id/tv_momocode"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentRight="true"
 android:layout_centerVertical="true"
 android:background="@drawable/bg_radius_top_black"
 android:paddingLeft="10dp"
 android:paddingTop="2dp"
 android:paddingRight="10dp"
 android:paddingBottom="2dp"
 android:text="MoMo: 12345678"
 android:textColor="@android:color/white"
 android:textSize="10sp" />
 </RelativeLayout>
 <LinearLayout
 android:id="@+id/ll_gift_group"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_above="@+id/lv_message"
 android:layout_marginTop="10dp"
 android:layout_marginBottom="10dp"
 android:animateLayoutChanges="true"
 android:gravity="top"
 android:orientation="vertical" />
 <ListView
 android:id="@+id/lv_message"
 android:layout_width="230dp"
 android:layout_height="150dp"
 android:layout_above="@+id/fl_bottom"
 android:layout_marginLeft="10dp"
 android:cacheColorHint="#00000000"
 android:divider="@null"
 android:dividerHeight="5dp"
 android:listSelector="#00000000"
 android:scrollbarStyle="outsideOverlay"
 android:scrollbars="none"
 android:transcriptMode="normal" />
 <FrameLayout
 android:id="@+id/fl_bottom"
 android:layout_width="match_parent"
 android:layout_height="70dp"
 android:layout_alignParentStart="true"
 android:layout_alignParentBottom="true">
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@android:color/transparent"
 android:gravity="center_vertical"
 android:orientation="horizontal"
 android:paddingLeft="10dp"
 android:paddingRight="10dp">
 <Button
 android:id="@+id/tv_chat"
 android:layout_width="40dp"
 android:layout_height="70dp"
 android:gravity="center"
 android:text="聊天"
 android:textColor="#333"
 android:textSize="10sp" />
 <View
 android:layout_width="0dp"
 android:layout_height="1dp"
 android:layout_weight="1" />
 <Button
 android:id="@+id/btn_gift01"
 android:layout_width="40dp"
 android:layout_height="70dp"
 android:layout_marginRight="5dp"
 android:gravity="center"
 android:text="送香皂"
 android:textColor="#333"
 android:textSize="12sp" />
 <Button
 android:id="@+id/btn_gift02"
 android:layout_width="40dp"
 android:layout_height="70dp"
 android:layout_marginRight="5dp"
 android:gravity="center"
 android:text="送玫瑰"
 android:textColor="#333"
 android:textSize="12sp" />
 <Button
 android:id="@+id/btn_gift03"
 android:layout_width="40dp"
 android:layout_height="70dp"
 android:layout_marginRight="5dp"
 android:gravity="center"
 android:text="送爱心"
 android:textColor="#333"
 android:textSize="12sp" />
 <Button
 android:id="@+id/btn_gift04"
 android:layout_width="40dp"
 android:layout_height="70dp"
 android:layout_marginRight="5dp"
 android:gravity="center"
 android:text="送蛋糕"
 android:textColor="#333"
 android:textSize="12sp" />
 </LinearLayout>
 <LinearLayout
 android:id="@+id/ll_inputparent"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_marginTop="5dp"
 android:background="@android:color/white"
 android:paddingLeft="10dp"
 android:paddingRight="10dp"
 android:visibility="gone">
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center_vertical"
 android:orientation="horizontal">
 <EditText
 android:id="@+id/et_chat"
 android:layout_width="0dp"
 android:layout_height="wrap_content"
 android:layout_weight="1"
 android:background="@android:color/white"
 android:hint="在此输入你要说的话!"
 android:maxLength="30"
 android:paddingTop="10dp"
 android:paddingBottom="10dp"
 android:textColor="#888889"
 android:textColorHint="#c8c8c8"
 android:textSize="12sp" />
 <TextView
 android:id="@+id/tv_send"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="10dp"
 android:background="@android:color/holo_blue_bright"
 android:paddingLeft="10dp"
 android:paddingTop="5dp"
 android:paddingRight="10dp"
 android:paddingBottom="5dp"
 android:text="发送"
 android:textColor="@android:color/white"
 android:textSize="12sp" />
 </LinearLayout>
 </LinearLayout>
 </FrameLayout>
</RelativeLayout>

EmptyFrag:

/**
 * 空的fragment
 */
public class EmptyFrag extends Fragment {
 @Nullable
 @Override
 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 return inflater.inflate(R.layout.frag_empty, null);
 }
}

frag_empty.xml:

<?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:background="@android:color/transparent"
 android:orientation="vertical">
</LinearLayout>

在MainActivity中使用FrameLayout布局,将观众功能交互页面 InteractiveFrag 覆盖在 直播页面LiveFrag上面。

MainActivity:

public class MainActivity extends AppCompatActivity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 // 加载直播fragment
 LiveFrag liveFrag = new LiveFrag();
 getSupportFragmentManager().beginTransaction().add(R.id.fl_root, liveFrag).commit();
 // 加载
 new InteractiveFrag().show(getSupportFragmentManager(), "InteractiveFrag");
 }
}

activity_main.xml :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 <FrameLayout
 android:id="@+id/fl_root"
 android:layout_width="match_parent"
 android:layout_height="match_parent" />
</RelativeLayout>

用户交互页实现

MagicTextView动画效果

MagicTextView代码在文章最后展示。

我们先实现如下动画效果

<com.hongx.zhibo.utils.MagicTextView
 android:id="@+id/mtv_giftNum"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerVertical="true"
 android:layout_marginLeft="5dp"
 android:layout_toRightOf="@+id/rlparent"
 android:includeFontPadding="false"
 android:text="x1"
 android:textColor="@android:color/holo_red_dark"
 android:textSize="30sp"
 android:textStyle="bold"
 app:strokeColor="@android:color/white"
 app:strokeJoinStyle="miter"
 app:strokeWidth="2" />

动画:

 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();
 }
 }

 mtv_giftNum.setText("x" + count);
 giftNumberAnim = new NumberAnim(); // 初始化数字动画 
 mtv_giftNum.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 count++;
 mtv_giftNum.setText("x" + count);
 giftNumberAnim.showAnimator(mtv_giftNum);
 }
 });

礼物进入时动画

进入动画设置为decelerate_interpolator减速插值器:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
 android:duration="500"
 android:fromXDelta="-100%p"
 android:interpolator="@android:anim/decelerate_interpolator"
 android:toYDelta="0%p">
</translate>

 /**
 * 刷礼物的方法
 */
 private void showGift(String tag) {
 View newGiftView = ll_gift_group.findViewWithTag(tag);
 // 是否有该tag类型的礼物
 if (newGiftView == null) {
 // 获取礼物
 newGiftView = getNewGiftView(tag);
 ll_gift_group.addView(newGiftView);

 // 播放动画
 newGiftView.startAnimation(inAnim);
 final MagicTextView mtv_giftNum = newGiftView.findViewById(R.id.mtv_giftNum);
 inAnim.setAnimationListener(new Animation.AnimationListener() {

 @Override
 public void onAnimationStart(Animation animation) {
 }

 @Override
 public void onAnimationRepeat(Animation animation) {
 }

 @Override
 public void onAnimationEnd(Animation animation) {
 giftNumberAnim.showAnimator(mtv_giftNum);
 }
 });
 } else {
 // 如果列表中已经有了该类型的礼物,则不再新建,直接拿出
 // 更新标识,记录最新修改的时间,用于回收判断
 ImageView iv_gift = newGiftView.findViewById(R.id.iv_gift);
 iv_gift.setTag(System.currentTimeMillis());

 // 更新标识,更新记录礼物个数
 MagicTextView mtv_giftNum = 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);
 }
 }

 /**
 * 获取礼物
 */
 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 = giftView.findViewById(R.id.iv_gift);
 iv_gift.setTag(System.currentTimeMillis());

 // 添加标识,记录礼物个数
 MagicTextView mtv_giftNum = 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;
 }

 @Override
 public void onClick(View 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;
 }
 }

礼物移出动画

实现的效果如下:

礼物移出时使用accelerate_interpolator加速差值器

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
 android:duration="500"
 android:fromYDelta="0%p"
 android:interpolator="@android:anim/accelerate_interpolator"
 android:toYDelta="-100%p">
</translate>

 /**
 * 移除礼物列表里的giftView
 */
 private void removeGiftView(final int index) {
 // 移除列表,外加退出动画
 final View removeGiftView = ll_gift_group.getChildAt(index);
 outAnim.setAnimationListener(new Animation.AnimationListener() {

 @Override
 public void onAnimationStart(Animation animation) {
 }

 @Override
 public void onAnimationRepeat(Animation animation) {
 }

 @Override
 public void onAnimationEnd(Animation animation) {
 ll_gift_group.removeViewAt(index);
 }
 });

 // 开启动画,因为定时原因,所以可能是在子线程
 getActivity().runOnUiThread(new Runnable() {
 @Override
 public void run() {
 removeGiftView.startAnimation(outAnim);
 }
 });
 }

如果显示的礼物大于3种,就将最早的那种礼物移除:

// 是否有该tag类型的礼物
 if (newGiftView == null) {
 // 判断礼物列表是否已经有3个了,如果有那么删除掉一个没更新过的, 然后再添加新进来的礼物,始终保持只有3个
 if (ll_gift_group.getChildCount() >= 3) {
 // 获取前2个元素的最后更新时间
 View giftView01 = ll_gift_group.getChildAt(0);
 ImageView iv_gift01 = giftView01.findViewById(R.id.iv_gift);
 long lastTime1 = (long) iv_gift01.getTag();

 View giftView02 = ll_gift_group.getChildAt(1);
 ImageView iv_gift02 = giftView02.findViewById(R.id.iv_gift);
 long lastTime2 = (long) iv_gift02.getTag();

 if (lastTime1 > lastTime2) { // 如果第二个View显示的时间比较长
 removeGiftView(1);
 } else { // 如果第一个View显示的时间长
 removeGiftView(0);
 }
 }
...

开启定时清理礼物列表

礼物显示超过一定时间,自动将礼物在礼物列表中移除:

 /**
 * 定时清理礼物列表信息
 */
 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);
 }

聊天实现

case R.id.tv_chat:// 聊天
 tv_chat.setVisibility(View.GONE);
 ll_inputparent.setVisibility(View.VISIBLE);
 ll_inputparent.requestFocus(); // 获取焦点
 showKeyboard();
 break;
 case R.id.tv_send:// 发送消息
 String chatMsg = et_chat.getText().toString();
 if (!TextUtils.isEmpty(chatMsg)) {
 messageData.add("小明: " + chatMsg);
 et_chat.setText("");
 messageAdapter.NotifyAdapter(messageData);
 lv_message.setSelection(messageData.size());
 }
 hideKeyboard();
 break;

 /**
 * 显示软键盘
 */
 private void showKeyboard() {
 InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
 imm.showSoftInput(et_chat, InputMethodManager.SHOW_FORCED);
 }

 /**
 * 隐藏软键盘
 */
 public void hideKeyboard() {
 InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
 imm.hideSoftInputFromWindow(et_chat.getWindowToken(), 0);
 }

 view.setOnClickListener(new View.OnClickListener() {

 @Override
 public void onClick(View v) {
 if (ll_inputparent.getVisibility() == View.VISIBLE) {
 tv_chat.setVisibility(View.VISIBLE);
 ll_inputparent.setVisibility(View.GONE);
 hideKeyboard();
 }
 }
 });

 // 软键盘监听
 SoftKeyBoardListener.setListener(getActivity(), new SoftKeyBoardListener.OnSoftKeyBoardChangeListener() {
 @Override
 public void keyBoardShow(int height) {/*软键盘显示:执行隐藏title动画,并修改listview高度和装载礼物容器的高度*/

 // 输入文字时的界面退出动画
 AnimatorSet animatorSetHide = new AnimatorSet();
 ObjectAnimator leftOutAnim = ObjectAnimator.ofFloat(rl_num, "translationX", 0, -rl_num.getWidth());
 ObjectAnimator topOutAnim = ObjectAnimator.ofFloat(ll_anchor, "translationY", 0, -ll_anchor.getHeight());
 animatorSetHide.playTogether(leftOutAnim, topOutAnim);
 animatorSetHide.setDuration(300);
 animatorSetHide.start();
 // 改变listview的高度
 dynamicChangeListviewH(90);
 dynamicChangeGiftParentH(true);
 }

 @Override
 public void keyBoardHide(int height) {/*软键盘隐藏:隐藏聊天输入框并显示聊天按钮,执行显示title动画,并修改listview高度和装载礼物容器的高度*/
 tv_chat.setVisibility(View.VISIBLE);
 ll_inputparent.setVisibility(View.GONE);
 // 输入文字时的界面进入时的动画
 AnimatorSet animatorSetShow = new AnimatorSet();
 ObjectAnimator leftInAnim = ObjectAnimator.ofFloat(rl_num, "translationX", -rl_num.getWidth(), 0);
 ObjectAnimator topInAnim = ObjectAnimator.ofFloat(ll_anchor, "translationY", -ll_anchor.getHeight(), 0);
 animatorSetShow.playTogether(leftInAnim, topInAnim);
 animatorSetShow.setDuration(300);
 animatorSetShow.start();

 // 改变listview的高度
 dynamicChangeListviewH(150);
 dynamicChangeGiftParentH(false);
 }
 });

 /**
 * 动态的修改listview的高度
 */
 private void dynamicChangeListviewH(int heightPX) {
 ViewGroup.LayoutParams layoutParams = lv_message.getLayoutParams();
 layoutParams.height = DisplayUtil.dip2px(getActivity(), heightPX);
 lv_message.setLayoutParams(layoutParams);
 }

 /**
 * 动态修改礼物父布局的高度
 */
 private void dynamicChangeGiftParentH(boolean showhide) {
 if (showhide) {// 如果软键盘显示中
 if (ll_gift_group.getChildCount() != 0) {

 // 判断是否有礼物显示,如果有就修改父布局高度,如果没有就不作任何操作
 ViewGroup.LayoutParams layoutParams = ll_gift_group.getLayoutParams();
 layoutParams.height = ll_gift_group.getChildAt(0).getHeight();
 ll_gift_group.setLayoutParams(layoutParams);
 }
 } else {
 // 如果软键盘隐藏中
 // 就将装载礼物的容器的高度设置为包裹内容
 ViewGroup.LayoutParams layoutParams = ll_gift_group.getLayoutParams();
 layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
 ll_gift_group.setLayoutParams(layoutParams);
 }
 }

MagicTextView代码

/**
 * 该自定义view是用于显示礼物数字的,加了些效果,内发光,阴影等
 */
public class MagicTextView extends TextView {
 private ArrayList<Shadow> outerShadows;
 private ArrayList<Shadow> innerShadows;
 private WeakHashMap<String, Pair<Canvas, Bitmap>> canvasStore;
 private Canvas tempCanvas;
 private Bitmap tempBitmap;
 private Drawable foregroundDrawable;
 private float strokeWidth;
 private Integer strokeColor;
 private Join strokeJoin;
 private float strokeMiter;
 private int[] lockedCompoundPadding;
 private boolean frozen = false;
 public MagicTextView(Context context) {
 super(context);
 init(null);
 }
 public MagicTextView(Context context, AttributeSet attrs) {
 super(context, attrs);
 init(attrs);
 }
 public MagicTextView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 init(attrs);
 }
 public void init(AttributeSet attrs) {
 outerShadows = new ArrayList<Shadow>();
 innerShadows = new ArrayList<Shadow>();
 if (canvasStore == null) {
 canvasStore = new WeakHashMap<String, Pair<Canvas, Bitmap>>();
 }
 if (attrs != null) {
 TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MagicTextView);
 String typefaceName = a.getString(R.styleable.MagicTextView_typeface);
 if (typefaceName != null) {
 Typeface tf = Typeface.createFromAsset(getContext().getAssets(), String.format("fonts/%s.ttf", typefaceName));
 setTypeface(tf);
 }
 if (a.hasValue(R.styleable.MagicTextView_foreground)) {
 Drawable foreground = a.getDrawable(R.styleable.MagicTextView_foreground);
 if (foreground != null) {
 this.setForegroundDrawable(foreground);
 } else {
 this.setTextColor(a.getColor(R.styleable.MagicTextView_foreground, 0xff000000));
 }
 }
 if (a.hasValue(R.styleable.MagicTextView_innerShadowColor)) {
 this.addInnerShadow(a.getFloat(R.styleable.MagicTextView_innerShadowRadius, 0),
 a.getFloat(R.styleable.MagicTextView_innerShadowDx, 0),
 a.getFloat(R.styleable.MagicTextView_innerShadowDy, 0),
 a.getColor(R.styleable.MagicTextView_innerShadowColor, 0xff000000));
 }
 if (a.hasValue(R.styleable.MagicTextView_outerShadowColor)) {
 this.addOuterShadow(a.getFloat(R.styleable.MagicTextView_outerShadowRadius, 0),
 a.getFloat(R.styleable.MagicTextView_outerShadowDx, 0),
 a.getFloat(R.styleable.MagicTextView_outerShadowDy, 0),
 a.getColor(R.styleable.MagicTextView_outerShadowColor, 0xff000000));
 }
 if (a.hasValue(R.styleable.MagicTextView_strokeColor)) {
 float strokeWidth = a.getFloat(R.styleable.MagicTextView_strokeWidth, 1);
 int strokeColor = a.getColor(R.styleable.MagicTextView_strokeColor, 0xff000000);
 float strokeMiter = a.getFloat(R.styleable.MagicTextView_strokeMiter, 10);
 Join strokeJoin = null;
 switch (a.getInt(R.styleable.MagicTextView_strokeJoinStyle, 0)) {
 case (0):
 strokeJoin = Join.MITER;
 break;
 case (1):
 strokeJoin = Join.BEVEL;
 break;
 case (2):
 strokeJoin = Join.ROUND;
 break;
 }
 this.setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter);
 }
 }
 }
 public void setStroke(float width, int color, Join join, float miter) {
 strokeWidth = width;
 strokeColor = color;
 strokeJoin = join;
 strokeMiter = miter;
 }
 public void setStroke(float width, int color) {
 setStroke(width, color, Join.MITER, 10);
 }
 public void addOuterShadow(float r, float dx, float dy, int color) {
 if (r == 0) {
 r = 0.0001f;
 }
 outerShadows.add(new Shadow(r, dx, dy, color));
 }
 public void addInnerShadow(float r, float dx, float dy, int color) {
 if (r == 0) {
 r = 0.0001f;
 }
 innerShadows.add(new Shadow(r, dx, dy, color));
 }
 public void clearInnerShadows() {
 innerShadows.clear();
 }
 public void clearOuterShadows() {
 outerShadows.clear();
 }
 public void setForegroundDrawable(Drawable d) {
 this.foregroundDrawable = d;
 }
 public Drawable getForeground() {
 return this.foregroundDrawable == null ? this.foregroundDrawable : new ColorDrawable(this.getCurrentTextColor());
 }
 @Override
 public void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 freeze();
 Drawable restoreBackground = this.getBackground();
 Drawable[] restoreDrawables = this.getCompoundDrawables();
 int restoreColor = this.getCurrentTextColor();
 this.setCompoundDrawables(null, null, null, null);
 for (Shadow shadow : outerShadows) {
 this.setShadowLayer(shadow.r, shadow.dx, shadow.dy, shadow.color);
 super.onDraw(canvas);
 }
 this.setShadowLayer(0, 0, 0, 0);
 this.setTextColor(restoreColor);
 if (this.foregroundDrawable != null && this.foregroundDrawable instanceof BitmapDrawable) {
 generateTempCanvas();
 super.onDraw(tempCanvas);
 Paint paint = ((BitmapDrawable) this.foregroundDrawable).getPaint();
 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
 this.foregroundDrawable.setBounds(canvas.getClipBounds());
 this.foregroundDrawable.draw(tempCanvas);
 canvas.drawBitmap(tempBitmap, 0, 0, null);
 tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
 }
 if (strokeColor != null) {
 TextPaint paint = this.getPaint();
// paint.setTextAlign(Paint.Align.CENTER);
 paint.setStyle(Style.STROKE);
 paint.setStrokeJoin(strokeJoin);
 paint.setStrokeMiter(strokeMiter);
 this.setTextColor(strokeColor);
 paint.setStrokeWidth(strokeWidth);
 super.onDraw(canvas);
 paint.setStyle(Style.FILL);
 this.setTextColor(restoreColor);
 }
 if (innerShadows.size() > 0) {
 generateTempCanvas();
 TextPaint paint = this.getPaint();
 for (Shadow shadow : innerShadows) {
 this.setTextColor(shadow.color);
 super.onDraw(tempCanvas);
 this.setTextColor(0xFF000000);
 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
 paint.setMaskFilter(new BlurMaskFilter(shadow.r, BlurMaskFilter.Blur.NORMAL));
 tempCanvas.save();
 tempCanvas.translate(shadow.dx, shadow.dy);
 super.onDraw(tempCanvas);
 tempCanvas.restore();
 canvas.drawBitmap(tempBitmap, 0, 0, null);
 tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
 paint.setXfermode(null);
 paint.setMaskFilter(null);
 this.setTextColor(restoreColor);
 this.setShadowLayer(0, 0, 0, 0);
 }
 }
 if (restoreDrawables != null) {
 this.setCompoundDrawablesWithIntrinsicBounds(restoreDrawables[0], restoreDrawables[1], restoreDrawables[2], restoreDrawables[3]);
 }
 this.setBackgroundDrawable(restoreBackground);
 this.setTextColor(restoreColor);
 unfreeze();
 }
 private void generateTempCanvas() {
 String key = String.format("%dx%d", getWidth(), getHeight());
 Pair<Canvas, Bitmap> stored = canvasStore.get(key);
 if (stored != null) {
 tempCanvas = stored.first;
 tempBitmap = stored.second;
 } else {
 tempCanvas = new Canvas();
 tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
 tempCanvas.setBitmap(tempBitmap);
 canvasStore.put(key, new Pair<Canvas, Bitmap>(tempCanvas, tempBitmap));
 }
 }
 public void freeze() {
 lockedCompoundPadding = new int[]{
 getCompoundPaddingLeft(),
 getCompoundPaddingRight(),
 getCompoundPaddingTop(),
 getCompoundPaddingBottom()
 };
 frozen = true;
 }
 public void unfreeze() {
 frozen = false;
 }
 @Override
 public void requestLayout() {
 if (!frozen) super.requestLayout();
 }
 @Override
 public void postInvalidate() {
 if (!frozen) super.postInvalidate();
 }
 @Override
 public void postInvalidate(int left, int top, int right, int bottom) {
 if (!frozen) super.postInvalidate(left, top, right, bottom);
 }
 @Override
 public void invalidate() {
 if (!frozen) super.invalidate();
 }
 @Override
 public void invalidate(Rect rect) {
 if (!frozen) super.invalidate(rect);
 }
 @Override
 public void invalidate(int l, int t, int r, int b) {
 if (!frozen) super.invalidate(l, t, r, b);
 }
 @Override
 public int getCompoundPaddingLeft() {
 return !frozen ? super.getCompoundPaddingLeft() : lockedCompoundPadding[0];
 }
 @Override
 public int getCompoundPaddingRight() {
 return !frozen ? super.getCompoundPaddingRight() : lockedCompoundPadding[1];
 }
 @Override
 public int getCompoundPaddingTop() {
 return !frozen ? super.getCompoundPaddingTop() : lockedCompoundPadding[2];
 }
 @Override
 public int getCompoundPaddingBottom() {
 return !frozen ? super.getCompoundPaddingBottom() : lockedCompoundPadding[3];
 }
 public static class Shadow {
 float r;
 float dx;
 float dy;
 int color;

 public Shadow(float r, float dx, float dy, int color) {
 this.r = r;
 this.dx = dx;
 this.dy = dy;
 this.color = color;
 }
 }
}

Github:https://github.com/345166018/AndroidUI/tree/master/HxZhibo

总结

以上所述是小编给大家介绍的Android仿直播类app赠送礼物功能,希望对大家有所帮助!

PassayearlearninginHangzhou,yifenfeiarrivalhometownNingboatfinallyLeaveNingbooneyear,yifenfeihaveman
C(n,m)从n个字符中选取m个字符,获得所有的组合用编程实现C(n,m)组合问题,可以用递归的方法的解决,将n个字符排列成一条流水线,然后从第一个字符开始选取,并且将已经选取的字符进行保存,如果已经
A(M,N)是从M个不同元素中选N个元素的排列数(区分元素排列先后顺序)C(M,N)是从M个不同元素中选N个元素的组合数(不区分元素排列先后顺序)C(n,m)n!/m!(nm)!故C(4,1)4!/3
易语言求N次方,希望大家都能学懂。非无私奉献,拒绝看经验不回复,学懂了记得按照下面做:在底部点击相关按钮,给我投票和点赞。有心得或者疑问,点击相关按钮反馈。具体操作请看图。1、易语言新建一个wind
如果一个scanf的格式串以\n结尾,那么在读取完后还会阻塞等待,比如:inta;scanf(%d\n,&a);这种情况,输入一个数字然后敲下回车后,程序还是阻塞着的。为什么呢?在仔细读了一遍《C程
vara{<!>n:1};varba;axa{<!>n:2};consolelog(a,b)解析器在接受到aax{n:2}这样的语句后,会这样做:找到a和ax的指针。如果已有指
直播系统app源码,借助django实现显示图片功能在settingspy中添加如下代码MEDIA_URL'/media/'MEDIA_ROOTospathjoin(BASE_DIR,'media')
SA+A^2+A^3+…+A^kA是一个n*n矩阵SampleInput224//nkMOD0111SampleOutput1223先求I+A+A^2+A^3+…+A^kI为单位矩阵我们来设置这样一个
本文为大家分享了iOS直播APP开发流程,供大家参考,具体内容如下一音视频处理的一般流程:数据采集→数据编码→数据传输(流媒体服务器)→解码数据→播放显示1、数据采集:摄像机及拾音器收集视频及音频数
1、推流:即构、阿里云直播、七牛云等即构科技由腾讯QQ团队创业,是市面暂时较好的推流SDK,但是费用太高,可以先做个对比。但美颜效果,连麦功能,狼人杀模式等确实相较其他SDK有很大的优势。阿里云直播