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


参考: https://www.jianshu.com/p/2505c2c5ac0e

可以先查看这篇文章:Android使用RecyclerView实现抖音主界面

直播界面

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

/**
 * 直播界面,用于对接直播功能
 */
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

发布了391 篇原创文章 · 获赞 58 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/hongxue8888/article/details/104135163
今日推荐