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

直播界面

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

/**

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

*/

public class LiveFrag extends Fragment {

private ImageView img_thumb;

private VideoView video_view;

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.frag_live, null);

img_thumb = view.findViewById(R.id.img_thumb);

img_thumb.setVisibility(View.GONE);

video_view = view.findViewById(R.id.video_view);

video_view.setVisibility(View.VISIBLE);

video_view.setVideoURI(Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.video_1));

video_view.start();

video_view.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

@Override

public void onCompletion(MediaPlayer mp) {

video_view.setVideoURI(Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.video_1));

//或 //mVideoView.setVideoPath(Uri.parse(_filePath));

video_view.start();

}

});

return view;

}

}

布局文件 frag_live.xml 如下:

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

滑动隐藏效果

需要实现的效果如下:

6ce71635f6f9ba8369a7647e40fe60d5.gif

自定义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代码在文章最后展示。

我们先实现如下动画效果

dc05632ec167b4d1d0c735e1944989c0.gif

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

}

});

礼物进入时动画

a060aef13d27972c816c9cbb84643af2.gif

进入动画设置为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;

}

}

礼物移出动画

实现的效果如下:

659b849e3875bc820785fc037ee7c2bf.gif

礼物移出时使用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);

}

cd70c9c09d95c4d59bc2a6e32d886517.gif

聊天实现

2c437a9d98517c9f76e3bf8503b32fad.gif

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赠送礼物功能,希望对大家有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302104.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一张纸一幅图,竟然提高了10倍的学习和工作效率!?

人类大脑的容量远远超出一般人的想象&#xff0c;时到21世纪的今天&#xff0c;我们对大脑的运用远远不够。大脑机能的使用率基于我们的思维模式&#xff0c;而思维导图正是开发大脑中最有效的利器&#xff01;之前小木给大家推荐了一套基于少儿大脑思维开发的书籍——东尼博赞…

C# 消息队列之MSMQ

首先说一下&#xff0c;消息队列 (MSMQ Microsoft Message Queuing)是MS提供的服务&#xff0c;也就是Windows操作系统的功能&#xff0c;并不是.Net提供的。消息队列&#xff08;MSMQ&#xff09;技术使得运行于不同时间的应用程序能够在各种各样的网络和可能暂时脱机的系统之…

惊呆了!竟然还有这样的操作!

2018已成为过去&#xff0c;2019的序幕已经悄然打开&#xff01;2019开年钜惠盛大来袭&#xff01;&#xff08;福利在最后&#xff01;&#xff01;&#xff01;&#xff09;Python专场Python快速入门实战&#xff08;59.9&#xff09;人工智能的浪潮下&#xff0c;Python因其…

使用 .NET 升级助手将.NET Framework应用迁移到.NET 5

从.NET Framework 迁移到.NET 5 犹如搬家&#xff0c;我们都知道搬家是很痛苦的&#xff0c;我们请求搬家公司来减轻我们的压力&#xff0c;.NET 升级助手 的作用就类似我们聘请的搬家公司&#xff0c;帮助我们处理繁重乏味的迁移工作。.NET 升级助手是一个全局命令行工具&…

java 逐行写入csv_go|使用go读写CSV文件

生成CSV文件package mainimport ("encoding/csv""log""os")var data [][]string{{"tom", "18", "beijing"}, {"jon", "19", "shanghai"}}func main() {file, err : os.Create(&qu…

用android ndk编译ffmpeg,AndroidNDK交叉编译FFMPEG

1. 简介在进行安卓音视频开发时&#xff0c;利用安卓NDK工具交叉编译FFmpeg&#xff0c;生成可供安卓平台调用的FFmpeg库是最基础的工作。本篇文章介绍了利用NDK进行FFmpeg编译的具体过程&#xff0c;以及如何在Linux和windows系统下交叉编译出适用于不同安卓平台的FFmpeg库文件…

第106届印度科学大会:牛顿和爱因斯坦的理论惨遭印度科学家驳斥

全世界只有3.14 % 的人关注了数据与算法之美昨天&#xff0c;一年一度的印度科学大会&#xff08;Indian Science Congress&#xff09;落下了帷幕。第106届印度科学大会的主要成员合影一般来说&#xff0c;这种学术性会议是学者们进行学术交流的最佳时期&#xff0c;而且能参加…

润乾报表分组求和_实现报表数据预先计算

报表应用中&#xff0c;如果数据量较大或计算过程较复杂&#xff0c;往往会导致报表数据源准备过慢&#xff0c;从而影响报表性能。这种情况下可以预先计算报表需要的数据&#xff0c;在呈现时直接引用&#xff0c;使得用户在访问报表时可以迅速地获得响应。一、当前的手段及弊…

“鸭梨”大吗?

大家好&#xff0c;我是Z哥。“今天你卷了吗&#xff1f;”。在这个越来越内卷的时代&#xff0c;我想你可能时不时&#xff0c;甚至经常会觉得压力好大啊&#xff0c;那些来自生活的压力、工作的作压力、社交的压力、……来看看你有下面的这些情况不&#xff1f;如果有的话&am…

android复选按钮,Android的复选框的详细开发案例分析

在本教程中&#xff0c;我们将向您展示如何创建XML文件中的3个复选框&#xff0c;并论证了听者的使用检查–选中或取消选中复选框的状态。P.S这个项目是在Eclipse 3.7开发&#xff0c;并与Android 2.3.3测试。1。自定义字符串Open “res/values/strings.xml” file, add some u…

颠覆传统,仅银行卡大小充电宝,10000mAh可登机,手机党出门随身必备!

说到充电宝&#xff0c;不知道有没有人跟小木有同样的烦恼。5000毫安容量小&#xff0c;不够用。10000毫安容量够用&#xff0c;但又大又笨重&#xff0c;拿在手里充电超累的&#xff0c;还占地方&#xff01;尤其是旅行出差的时候&#xff0c;这种感觉更加明显&#xff0c;权宜…

玩转Github —— Octotree Chrome插件

&#xff08;为了隐私&#xff0c;手动马赛克&#xff09;兄弟萌&#xff0c;好久不见。最近一直在忙着其他的事情&#xff0c;也一直没有公众号的更新&#xff0c;看到了很多小伙伴说不更新了&#xff0c;也是比较惭愧&#xff0c;还是以工作为主的&#xff01;毕竟都是要恰饭…

android 获取app自启动权限状态_央视批手机App权限问题:频繁自启动 搜集个人隐私触目惊心...

近日据央视新闻报道&#xff0c;有网友反映自己手机上安装的App很多存在频繁自启动、访问、读取手机信息的现象。其中一款名为“优学院”的移动教学软件十多分钟读取近25000次手机照片和文件&#xff1b;而腾讯“TIM”一小时内尝试自启动近七千次&#xff0c;并不断尝试读取通讯…

女程序员怀孕7个月坚持上班敲代码

全世界只有3.14 % 的人关注了数据与算法之美在男女比例失调的程序员行业里&#xff0c;程序媛的工作日常是怎么的呢&#xff1f;程序媛遇到bug时是不是有很多程序员帮忙呢&#xff1f;程序媛对加班有什么看法呢&#xff1f;女生当程序媛好不好呢&#xff1f;下面一起来看看作者…

大数据的关键技术

在大数据时代&#xff0c;传统的数据处理方法还适用吗&#xff1f; 大数据环境下的数据处理需求 大数据环境下数据来源非常丰富且数据类型多样&#xff0c;存储和分析挖掘的数据量庞大&#xff0c;对数据展现的要求较高&#xff0c;并且很看重数据处理的高效性和可用性。 传统数…

adf盖怎么打开_罐头好吃盖难开,学会这几招,再不靠蛮力了,女生也轻松拧开...

家里储备的罐头怎么开&#xff1f;学会这几招&#xff0c;不用蛮力&#xff0c;女生也轻松拧开。这段时期家里肯定储备了很多罐头食品&#xff0c;像水果罐头、腌菜罐头、果酱、调料酱罐头等等&#xff0c;家里的青菜水果不足了&#xff0c;可以临时用罐头食品顶上几天&#xf…

在 Ubuntu 上安装 .NET SDK 或 .NET 运行时

在wsl Ubuntu 20.04上面安装dotnet链接https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-ubuntuUbuntu 支持 .NET。本文介绍如何在 Ubuntu 上安装 .NET。如果 Ubuntu 版本不受支持&#xff0c;则该版本不再支持 .NET。如果要开发 .NET 应用&#xff0c;请安装 SDK&…

skype linux 安装,Ubuntu 12.04 AMD64 安装 Skype

每次安装新版总是有些问题。一个个来解决。1&#xff1a; sudo apt-get install ia32-libs lib32asound2 libqt4-core libqt4-gui2&#xff1a; wget -O skype_Ubuntu-current_amd64.deb http://www.skype.com/go/getskype-linux-beta-ubuntu-643&#xff1a; sudo dpkg -i sky…

在学术论文投稿时你遇到过最奇葩的审稿意见是什么?

全世界只有3.14 % 的人关注了数据与算法之美论文投稿时&#xff0c;总会收到点奇葩审稿意见。就连大名鼎鼎的爱因斯坦也被美国《物理评论》期刊的审稿人怼过&#xff0c;审稿人认为文章的内容和结论存在严重问题&#xff0c;附上了 10 页审稿意见。物理大佬爱因斯坦当然不干了&…

WPF Treeview第三层横向排列

WPF 第三级横向排列效果&#xff0c;左侧使用WrapPanel&#xff0c;右侧使用StackPanel,效果见下图&#xff1a;代码如下&#xff1a;Mainwindow的xaml如下:<Window x:Class"WPFDemos.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presen…