直播界面

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

/**

* 直播界面,用于对接直播功能

*/

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 如下:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/video_view"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:clickable="false"

android:focusable="false"

android:visibility="gone" />

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

滑动隐藏效果

需要实现的效果如下:

自定义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文件如下:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

android:id="@+id/vp_interactive"

android:layout_width="match_parent"

android:layout_height="match_parent" />

用户交互页 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:

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

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

android:layout_width="wrap_content"

android:layout_height="wrap_content">

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

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="十三妹哦"

android:textColor="@android:color/white"

android:textSize="12sp" />

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:gravity="center_vertical"

android:orientation="horizontal">

android:layout_width="35dp"

android:layout_height="20dp"

android:src="@drawable/hani_icon_tag_exp" />

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

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

android:id="@+id/hlv_audience"

android:layout_width="match_parent"

android:layout_height="45dp"

android:layout_marginLeft="10dp" />

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

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

android:layout_width="20dp"

android:layout_height="10dp"

android:src="@drawable/molive_icon_charm_lv_20" />

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="5dp"

android:text="小时榜单第5名"

android:textColor="#fff"

android:textSize="10sp" />

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

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

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

android:id="@+id/fl_bottom"

android:layout_width="match_parent"

android:layout_height="70dp"

android:layout_alignParentStart="true"

android:layout_alignParentBottom="true">

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

android:id="@+id/tv_chat"

android:layout_width="40dp"

android:layout_height="70dp"

android:gravity="center"

android:text="聊天"

android:textColor="#333"

android:textSize="10sp" />

android:layout_width="0dp"

android:layout_height="1dp"

android:layout_weight="1" />

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

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

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

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

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

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center_vertical"

android:orientation="horizontal">

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

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

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:

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/transparent"

android:orientation="vertical">

在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 :

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/fl_root"

android:layout_width="match_parent"

android:layout_height="match_parent" />

用户交互页实现

MagicTextView动画效果

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减速插值器:

android:duration="500"

android:fromXDelta="-100%p"

android:interpolator="@android:anim/decelerate_interpolator"

android:toYDelta="0%p">

/**

* 刷礼物的方法

*/

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加速差值器

android:duration="500"

android:fromYDelta="0%p"

android:interpolator="@android:anim/accelerate_interpolator"

android:toYDelta="-100%p">

/**

* 移除礼物列表里的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 outerShadows;

private ArrayList innerShadows;

private WeakHashMap> 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();

innerShadows = new ArrayList();

if (canvasStore == null) {

canvasStore = new WeakHashMap>();

}

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

}

}

}

总结

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

android动画送礼物,Android仿直播类app赠送礼物功能相关推荐

  1. 直播类app开发,如何实现直播的相关功能

    上篇文章完成了直播的简单业务,我们可以慢慢完善这个直播类app开发, 例如附近直播,直播礼物,直播回放, 当然直播类app开发的实际业务要比我说的复杂,博主这里提供一个思路 附近直播 现在直播类app ...

  2. Android仿直播特效之刷礼物

    一.概述 继续咱们的直播之旅,过段时间再把推流拉流写上博客,暂时还是UI特效,先上图 二.创建我们的BaseActivity和BaseFrag /*** @author 刘洋巴金* @date 201 ...

  3. android 开发打赏布局,Android自定义View模仿虎扑直播界面的打赏按钮功能

    Android自定义View模仿虎扑直播界面的打赏按钮功能 发布时间:2020-09-28 12:15:53 来源:脚本之家 阅读:77 作者:shenhuniurou 前言 作为一个资深篮球爱好者, ...

  4. Android动画之旅-Android动画基本介绍

    在上一篇博客中,我们简单了解了在Android中的动画特效,小伙伴们是不是意犹未尽呀.还没有看的猛戳这里:Android动画之旅一开篇动画简介 本篇博客,将和大家一起来分析Android中的四大动画, ...

  5. 在线视频直播类app软件如何开发?

    最近两年,要说手机APP开发最火的行业非视频直播APP莫属,不少企业通过直播视频手机软件获得了巨大的成功,也就使得后期不少企业进入视频直赔软件开发的行业,可是开发一个直播视频APP并不是那么简单的,虽 ...

  6. iOS 直播类APP开发流程

    (一) iOS 直播类APP开发流程分解: 1 . 音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放显示 1.数据采集: 摄像机及拾音器收集视频及音频数据,此时得 ...

  7. 关于直播类app中的推流、拉流技术

    虽然直播app在2016年的时候就很火了,但是对于我这样的技术菜逼来说,从来没有真正的去了解过,而对于这方面的技术,我就更是不了解了.由于最近的项目中可能会用到直播之类的功能,所以就赶紧来恶补一下这方 ...

  8. 分析一下,直播类app开发需要哪些技术

    直播技术总结 直播总结 1.概述 关于直播类app开发的技术文章不少,成体系的不多.我们将用这篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播类 ...

  9. 直播类app开发,实现一个进度条效果

    进度条是很多软件中必须存在的效果,今天我们就看看直播类app开发实现的进度条效果. 效果&使用 图例分别为: 修改读条起点为y轴正方向 消失性读条 正常读条 使用: 1 在xml中添加控件 & ...

最新文章

  1. 创建第二个 local network - 每天5分钟玩转 OpenStack(84)
  2. Xamarin XAML语言教程构建ControlTemplate控件模板 (三)
  3. Java Web HelloWorld!
  4. oracle 信用检查,Oracle EBS 信用(Credit)额度(1)-基础设置
  5. chrome源代码目录结构简介
  6. sam服务器是什么_使用SAM CLI将机器学习模型部署到无服务器后端
  7. 数据结构与算法--数字在排序数组中出现次数
  8. 为什么CAP不能同时满足的简单理解
  9. selenium python grid
  10. CV学习笔记(六):均值滤波与高斯滤波
  11. php 读取excel表数据
  12. 能源巨头BP称已经测试了“内部”代币
  13. Mac上如何修改itunes的默认备份地址
  14. linux视频播放器软件下载,360万能播放器Linux版
  15. Go Playground exercise
  16. 时空人文之旅(二):夜话“时空”——从盘古开天辟地说起
  17. 海康威视2020届校园招聘内推(内含内推码)
  18. MATLAB2014b画极坐标散点图
  19. pyvisa.errors.VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed
  20. Flink实战之实时风控规则引擎

热门文章

  1. CAP 2.4版本发布,支持版本隔离特性
  2. .NET Core使用skiasharp文字头像生成方案(基于docker发布)
  3. miui替换官方文件解决无服务器,miui 关掉云服务器
  4. 【MATLAB统计分析与应用100例】案例014:matlab读取Excel数据,调用stepwise函数作交互式逐步回归分析
  5. ASP.NET MVC入门(一)---MVC的Hello World
  6. Java连接SQL Server类MyDBase的实现
  7. SQL Server CLR 集成简介
  8. linux之ip route命令
  9. 栈和队列之用java实现栈
  10. 【iVX 初级工程师培训教程 10篇文拿证】05 画布及飞机大战游戏制作