直播界面
实现的是播放本地的视频文件:
/**
* 直播界面,用于对接直播功能
*/
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赠送礼物功能,希望对大家有所帮助!