안 드 로 이 드 생방송 앱 선물 증정 기능
로 컬 비디오 파일 재생:
/**
* ,
*/
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;
}
}
레이아웃 파일 fraglive.xml 는 다음 과 같 습 니 다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:focusable="false"
android:visibility="gone" />
<ImageView
android:id="@+id/img_thumb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:focusable="false"
android:scaleType="centerCrop"
android:src="@mipmap/img_video_1"
android:visibility="visible" />
</LinearLayout>
미끄럼 숨 김 효과실현 해 야 할 효 과 는 다음 과 같다.
DialogFragment 를 사용자 정의 합 니 다.ViewPager 를 사용 합 니 다.첫 번 째 는 빈 Fragment 이 고 두 번 째 는 우리 가 필요 로 하 는 Fragment 입 니 다.좌우 로 미 끄 러 지 며 디 스 플레이 와 숨 김 효 과 를 전환 합 니 다.
관중 기능 인 터 랙 션 페이지 InteractiveFrag 는 다음 과 같다.
/**
* ,
*/
public class InteractiveFrag extends DialogFragment {
public View view;
public Context myContext;
private ViewPager vp_interactive;
private LayerFrag layerFrag;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.frag_interactive, null);
//
initView();
initData();
return view;
}
/**
* View
*/
public void initView() {
vp_interactive = view.findViewById(R.id.vp_interactive);
}
/**
*
*/
public void initData() {
// EmptyFrag:
// LayerFrag:
//
vp_interactive.setAdapter(new FragmentPagerAdapter(getChildFragmentManager()) {
@Override
public int getCount() {
return 2;
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return new EmptyFrag(); // fragment
} else if (position == 1) {
return layerFrag = new LayerFrag(); // frag
} else { //
return new EmptyFrag();
}
}
});
//
vp_interactive.setCurrentItem(1);
// resize Fragment
getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// DialogFragment , ,
Dialog dialog = new Dialog(getActivity(), R.style.MainDialog) {
@Override
public void onBackPressed() {
super.onBackPressed();
getActivity().finish();
}
};
return dialog;
}
}
frag_interactive.xml 파일 은 다음 과 같 습 니 다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_interactive"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
사용자 대화 페이지 LayerFrag:
public class LayerFrag extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag_layer, null);
}
}
frag_layer:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_anchor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingTop="10dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@drawable/bg_radius_top_black"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="55dp"
android:paddingTop="2dp"
android:paddingRight="10dp"
android:paddingBottom="2dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" "
android:textColor="@android:color/white"
android:textSize="12sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="35dp"
android:layout_height="20dp"
android:src="@drawable/hani_icon_tag_exp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="17 "
android:textColor="@android:color/white"
android:textSize="10sp" />
</LinearLayout>
</LinearLayout>
<com.hongx.zhibo.utils.CircleImageView
android:id="@+id/lv_anchorIcon"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/zf"
app:border_color="@color/colorWhite"
app:border_width="1dp" />
</RelativeLayout>
<com.hongx.zhibo.utils.HorizontalListView
android:id="@+id/hlv_audience"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginLeft="10dp" />
</LinearLayout>
<RelativeLayout
android:id="@+id/rl_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/ll_anchor"
android:layout_marginTop="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_radius_bottom_pink"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:paddingTop="2dp"
android:paddingRight="10dp"
android:paddingBottom="2dp">
<ImageView
android:layout_width="20dp"
android:layout_height="10dp"
android:src="@drawable/molive_icon_charm_lv_20" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text=" 5 "
android:textColor="#fff"
android:textSize="10sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_momocode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_radius_top_black"
android:paddingLeft="10dp"
android:paddingTop="2dp"
android:paddingRight="10dp"
android:paddingBottom="2dp"
android:text="MoMo: 12345678"
android:textColor="@android:color/white"
android:textSize="10sp" />
</RelativeLayout>
<LinearLayout
android:id="@+id/ll_gift_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/lv_message"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:animateLayoutChanges="true"
android:gravity="top"
android:orientation="vertical" />
<ListView
android:id="@+id/lv_message"
android:layout_width="230dp"
android:layout_height="150dp"
android:layout_above="@+id/fl_bottom"
android:layout_marginLeft="10dp"
android:cacheColorHint="#00000000"
android:divider="@null"
android:dividerHeight="5dp"
android:listSelector="#00000000"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="none"
android:transcriptMode="normal" />
<FrameLayout
android:id="@+id/fl_bottom"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<Button
android:id="@+id/tv_chat"
android:layout_width="40dp"
android:layout_height="70dp"
android:gravity="center"
android:text=" "
android:textColor="#333"
android:textSize="10sp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Button
android:id="@+id/btn_gift01"
android:layout_width="40dp"
android:layout_height="70dp"
android:layout_marginRight="5dp"
android:gravity="center"
android:text=" "
android:textColor="#333"
android:textSize="12sp" />
<Button
android:id="@+id/btn_gift02"
android:layout_width="40dp"
android:layout_height="70dp"
android:layout_marginRight="5dp"
android:gravity="center"
android:text=" "
android:textColor="#333"
android:textSize="12sp" />
<Button
android:id="@+id/btn_gift03"
android:layout_width="40dp"
android:layout_height="70dp"
android:layout_marginRight="5dp"
android:gravity="center"
android:text=" "
android:textColor="#333"
android:textSize="12sp" />
<Button
android:id="@+id/btn_gift04"
android:layout_width="40dp"
android:layout_height="70dp"
android:layout_marginRight="5dp"
android:gravity="center"
android:text=" "
android:textColor="#333"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_inputparent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:background="@android:color/white"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<EditText
android:id="@+id/et_chat"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/white"
android:hint=" !"
android:maxLength="30"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textColor="#888889"
android:textColorHint="#c8c8c8"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@android:color/holo_blue_bright"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
android:text=" "
android:textColor="@android:color/white"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</RelativeLayout>
EmptyFrag:
/**
* fragment
*/
public class EmptyFrag extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag_empty, null);
}
}
frag_empty.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:orientation="vertical">
</LinearLayout>
MainActivity 에서 FrameLayout 레이아웃 을 사용 하여 시청자 기능 의 상호작용 페이지 인 InteractiveFrag 를 생방송 페이지 인 LiveFrag 에 덮어 씁 니 다.MainActivity:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// fragment
LiveFrag liveFrag = new LiveFrag();
getSupportFragmentManager().beginTransaction().add(R.id.fl_root, liveFrag).commit();
//
new InteractiveFrag().show(getSupportFragmentManager(), "InteractiveFrag");
}
}
activity_main.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/fl_root"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
사용자 대화 페이지 구현MagicTextView 애니메이션 효과
MagicTextView 코드 는 글 의 마지막 에 보 여 줍 니 다.
우 리 는 먼저 다음 과 같은 애니메이션 효 과 를 실현 합 니 다.
<com.hongx.zhibo.utils.MagicTextView
android:id="@+id/mtv_giftNum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/rlparent"
android:includeFontPadding="false"
android:text="x1"
android:textColor="@android:color/holo_red_dark"
android:textSize="30sp"
android:textStyle="bold"
app:strokeColor="@android:color/white"
app:strokeJoinStyle="miter"
app:strokeWidth="2" />
애니메이션:
public class NumberAnim {
private Animator lastAnimator;
public void showAnimator(View v) {
if (lastAnimator != null) {
lastAnimator.removeAllListeners();
lastAnimator.cancel();
lastAnimator.end();
}
ObjectAnimator animScaleX = ObjectAnimator.ofFloat(v, "scaleX", 1.3f, 1.0f);
ObjectAnimator animScaleY = ObjectAnimator.ofFloat(v, "scaleY", 1.3f, 1.0f);
AnimatorSet animSet = new AnimatorSet();
animSet.playTogether(animScaleX, animScaleY);
animSet.setDuration(200);
lastAnimator = animSet;
animSet.start();
}
}
mtv_giftNum.setText("x" + count);
giftNumberAnim = new NumberAnim(); //
mtv_giftNum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
count++;
mtv_giftNum.setText("x" + count);
giftNumberAnim.showAnimator(mtv_giftNum);
}
});
선물 입장 시 애니메이션애니메이션 입장 시 decelerate 로 설정interpolator 감속 플러그 인:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXDelta="-100%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toYDelta="0%p">
</translate>
/**
*
*/
private void showGift(String tag) {
View newGiftView = ll_gift_group.findViewWithTag(tag);
// tag
if (newGiftView == null) {
//
newGiftView = getNewGiftView(tag);
ll_gift_group.addView(newGiftView);
//
newGiftView.startAnimation(inAnim);
final MagicTextView mtv_giftNum = newGiftView.findViewById(R.id.mtv_giftNum);
inAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
giftNumberAnim.showAnimator(mtv_giftNum);
}
});
} else {
// , ,
// , ,
ImageView iv_gift = newGiftView.findViewById(R.id.iv_gift);
iv_gift.setTag(System.currentTimeMillis());
// ,
MagicTextView mtv_giftNum = newGiftView.findViewById(R.id.mtv_giftNum);
int giftCount = (int) mtv_giftNum.getTag() + 1; //
mtv_giftNum.setText("x" + giftCount);
mtv_giftNum.setTag(giftCount);
giftNumberAnim.showAnimator(mtv_giftNum);
}
}
/**
*
*/
private View getNewGiftView(String tag) {
// , view layout , ( findViewWithTag )
View giftView = LayoutInflater.from(myContext).inflate(R.layout.item_gift, null);
giftView.setTag(tag);
// , , ,
ImageView iv_gift = giftView.findViewById(R.id.iv_gift);
iv_gift.setTag(System.currentTimeMillis());
// ,
MagicTextView mtv_giftNum = giftView.findViewById(R.id.mtv_giftNum);
mtv_giftNum.setTag(1);
mtv_giftNum.setText("x1");
switch (tag) {
case "gift01":
iv_gift.setImageResource(GiftIcon[0]);
break;
case "gift02":
iv_gift.setImageResource(GiftIcon[1]);
break;
case "gift03":
iv_gift.setImageResource(GiftIcon[2]);
break;
case "gift04":
iv_gift.setImageResource(GiftIcon[3]);
break;
}
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.topMargin = 10;
giftView.setLayoutParams(lp);
return giftView;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_gift01: // 1,
showGift("gift01");
break;
case R.id.btn_gift02: // 2,
showGift("gift02");
break;
case R.id.btn_gift03: // 3,
showGift("gift03");
break;
case R.id.btn_gift04: // 4,
showGift("gift04");
break;
}
}
선물 이동 애니메이션실현 효 과 는 다음 과 같다.
선물 이동 시 accelerate 사용interpolator 가속 차 치 기
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromYDelta="0%p"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="-100%p">
</translate>
/**
* giftView
*/
private void removeGiftView(final int index) {
// ,
final View removeGiftView = ll_gift_group.getChildAt(index);
outAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
ll_gift_group.removeViewAt(index);
}
});
// , ,
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
removeGiftView.startAnimation(outAnim);
}
});
}
표시 되 는 선물 이 3 가지 이상 이면 최초의 선물 을 제거 합 니 다.
// tag
if (newGiftView == null) {
// 3 , , , 3
if (ll_gift_group.getChildCount() >= 3) {
// 2
View giftView01 = ll_gift_group.getChildAt(0);
ImageView iv_gift01 = giftView01.findViewById(R.id.iv_gift);
long lastTime1 = (long) iv_gift01.getTag();
View giftView02 = ll_gift_group.getChildAt(1);
ImageView iv_gift02 = giftView02.findViewById(R.id.iv_gift);
long lastTime2 = (long) iv_gift02.getTag();
if (lastTime1 > lastTime2) { // View
removeGiftView(1);
} else { // View
removeGiftView(0);
}
}
...
오픈 정시 선물 리스트 정리선물 은 일정 시간 이 지나 면 선물 목록 에서 자동 으로 삭 제 됩 니 다:
/**
*
*/
private void clearTiming() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
int childCount = ll_gift_group.getChildCount();
long nowTime = System.currentTimeMillis();
for (int i = 0; i < childCount; i++) {
View childView = ll_gift_group.getChildAt(i);
ImageView iv_gift = (ImageView) childView.findViewById(R.id.iv_gift);
long lastUpdateTime = (long) iv_gift.getTag();
// 3
if (nowTime - lastUpdateTime >= 3000) {
removeGiftView(i);
}
}
}
}, 0, 3000);
}
채 팅 실현
case R.id.tv_chat://
tv_chat.setVisibility(View.GONE);
ll_inputparent.setVisibility(View.VISIBLE);
ll_inputparent.requestFocus(); //
showKeyboard();
break;
case R.id.tv_send://
String chatMsg = et_chat.getText().toString();
if (!TextUtils.isEmpty(chatMsg)) {
messageData.add(" : " + chatMsg);
et_chat.setText("");
messageAdapter.NotifyAdapter(messageData);
lv_message.setSelection(messageData.size());
}
hideKeyboard();
break;
/**
*
*/
private void showKeyboard() {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(et_chat, InputMethodManager.SHOW_FORCED);
}
/**
*
*/
public void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(et_chat.getWindowToken(), 0);
}
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ll_inputparent.getVisibility() == View.VISIBLE) {
tv_chat.setVisibility(View.VISIBLE);
ll_inputparent.setVisibility(View.GONE);
hideKeyboard();
}
}
});
//
SoftKeyBoardListener.setListener(getActivity(), new SoftKeyBoardListener.OnSoftKeyBoardChangeListener() {
@Override
public void keyBoardShow(int height) {/* : title , listview */
//
AnimatorSet animatorSetHide = new AnimatorSet();
ObjectAnimator leftOutAnim = ObjectAnimator.ofFloat(rl_num, "translationX", 0, -rl_num.getWidth());
ObjectAnimator topOutAnim = ObjectAnimator.ofFloat(ll_anchor, "translationY", 0, -ll_anchor.getHeight());
animatorSetHide.playTogether(leftOutAnim, topOutAnim);
animatorSetHide.setDuration(300);
animatorSetHide.start();
// listview
dynamicChangeListviewH(90);
dynamicChangeGiftParentH(true);
}
@Override
public void keyBoardHide(int height) {/* : , title , listview */
tv_chat.setVisibility(View.VISIBLE);
ll_inputparent.setVisibility(View.GONE);
//
AnimatorSet animatorSetShow = new AnimatorSet();
ObjectAnimator leftInAnim = ObjectAnimator.ofFloat(rl_num, "translationX", -rl_num.getWidth(), 0);
ObjectAnimator topInAnim = ObjectAnimator.ofFloat(ll_anchor, "translationY", -ll_anchor.getHeight(), 0);
animatorSetShow.playTogether(leftInAnim, topInAnim);
animatorSetShow.setDuration(300);
animatorSetShow.start();
// listview
dynamicChangeListviewH(150);
dynamicChangeGiftParentH(false);
}
});
/**
* listview
*/
private void dynamicChangeListviewH(int heightPX) {
ViewGroup.LayoutParams layoutParams = lv_message.getLayoutParams();
layoutParams.height = DisplayUtil.dip2px(getActivity(), heightPX);
lv_message.setLayoutParams(layoutParams);
}
/**
*
*/
private void dynamicChangeGiftParentH(boolean showhide) {
if (showhide) {//
if (ll_gift_group.getChildCount() != 0) {
// , ,
ViewGroup.LayoutParams layoutParams = ll_gift_group.getLayoutParams();
layoutParams.height = ll_gift_group.getChildAt(0).getHeight();
ll_gift_group.setLayoutParams(layoutParams);
}
} else {
//
//
ViewGroup.LayoutParams layoutParams = ll_gift_group.getLayoutParams();
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
ll_gift_group.setLayoutParams(layoutParams);
}
}
MagicTextView 코드
/**
* view , , ,
*/
public class MagicTextView extends TextView {
private ArrayList<Shadow> outerShadows;
private ArrayList<Shadow> innerShadows;
private WeakHashMap<String, Pair<Canvas, Bitmap>> canvasStore;
private Canvas tempCanvas;
private Bitmap tempBitmap;
private Drawable foregroundDrawable;
private float strokeWidth;
private Integer strokeColor;
private Join strokeJoin;
private float strokeMiter;
private int[] lockedCompoundPadding;
private boolean frozen = false;
public MagicTextView(Context context) {
super(context);
init(null);
}
public MagicTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public MagicTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public void init(AttributeSet attrs) {
outerShadows = new ArrayList<Shadow>();
innerShadows = new ArrayList<Shadow>();
if (canvasStore == null) {
canvasStore = new WeakHashMap<String, Pair<Canvas, Bitmap>>();
}
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MagicTextView);
String typefaceName = a.getString(R.styleable.MagicTextView_typeface);
if (typefaceName != null) {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), String.format("fonts/%s.ttf", typefaceName));
setTypeface(tf);
}
if (a.hasValue(R.styleable.MagicTextView_foreground)) {
Drawable foreground = a.getDrawable(R.styleable.MagicTextView_foreground);
if (foreground != null) {
this.setForegroundDrawable(foreground);
} else {
this.setTextColor(a.getColor(R.styleable.MagicTextView_foreground, 0xff000000));
}
}
if (a.hasValue(R.styleable.MagicTextView_innerShadowColor)) {
this.addInnerShadow(a.getFloat(R.styleable.MagicTextView_innerShadowRadius, 0),
a.getFloat(R.styleable.MagicTextView_innerShadowDx, 0),
a.getFloat(R.styleable.MagicTextView_innerShadowDy, 0),
a.getColor(R.styleable.MagicTextView_innerShadowColor, 0xff000000));
}
if (a.hasValue(R.styleable.MagicTextView_outerShadowColor)) {
this.addOuterShadow(a.getFloat(R.styleable.MagicTextView_outerShadowRadius, 0),
a.getFloat(R.styleable.MagicTextView_outerShadowDx, 0),
a.getFloat(R.styleable.MagicTextView_outerShadowDy, 0),
a.getColor(R.styleable.MagicTextView_outerShadowColor, 0xff000000));
}
if (a.hasValue(R.styleable.MagicTextView_strokeColor)) {
float strokeWidth = a.getFloat(R.styleable.MagicTextView_strokeWidth, 1);
int strokeColor = a.getColor(R.styleable.MagicTextView_strokeColor, 0xff000000);
float strokeMiter = a.getFloat(R.styleable.MagicTextView_strokeMiter, 10);
Join strokeJoin = null;
switch (a.getInt(R.styleable.MagicTextView_strokeJoinStyle, 0)) {
case (0):
strokeJoin = Join.MITER;
break;
case (1):
strokeJoin = Join.BEVEL;
break;
case (2):
strokeJoin = Join.ROUND;
break;
}
this.setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter);
}
}
}
public void setStroke(float width, int color, Join join, float miter) {
strokeWidth = width;
strokeColor = color;
strokeJoin = join;
strokeMiter = miter;
}
public void setStroke(float width, int color) {
setStroke(width, color, Join.MITER, 10);
}
public void addOuterShadow(float r, float dx, float dy, int color) {
if (r == 0) {
r = 0.0001f;
}
outerShadows.add(new Shadow(r, dx, dy, color));
}
public void addInnerShadow(float r, float dx, float dy, int color) {
if (r == 0) {
r = 0.0001f;
}
innerShadows.add(new Shadow(r, dx, dy, color));
}
public void clearInnerShadows() {
innerShadows.clear();
}
public void clearOuterShadows() {
outerShadows.clear();
}
public void setForegroundDrawable(Drawable d) {
this.foregroundDrawable = d;
}
public Drawable getForeground() {
return this.foregroundDrawable == null ? this.foregroundDrawable : new ColorDrawable(this.getCurrentTextColor());
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
freeze();
Drawable restoreBackground = this.getBackground();
Drawable[] restoreDrawables = this.getCompoundDrawables();
int restoreColor = this.getCurrentTextColor();
this.setCompoundDrawables(null, null, null, null);
for (Shadow shadow : outerShadows) {
this.setShadowLayer(shadow.r, shadow.dx, shadow.dy, shadow.color);
super.onDraw(canvas);
}
this.setShadowLayer(0, 0, 0, 0);
this.setTextColor(restoreColor);
if (this.foregroundDrawable != null && this.foregroundDrawable instanceof BitmapDrawable) {
generateTempCanvas();
super.onDraw(tempCanvas);
Paint paint = ((BitmapDrawable) this.foregroundDrawable).getPaint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
this.foregroundDrawable.setBounds(canvas.getClipBounds());
this.foregroundDrawable.draw(tempCanvas);
canvas.drawBitmap(tempBitmap, 0, 0, null);
tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
}
if (strokeColor != null) {
TextPaint paint = this.getPaint();
// paint.setTextAlign(Paint.Align.CENTER);
paint.setStyle(Style.STROKE);
paint.setStrokeJoin(strokeJoin);
paint.setStrokeMiter(strokeMiter);
this.setTextColor(strokeColor);
paint.setStrokeWidth(strokeWidth);
super.onDraw(canvas);
paint.setStyle(Style.FILL);
this.setTextColor(restoreColor);
}
if (innerShadows.size() > 0) {
generateTempCanvas();
TextPaint paint = this.getPaint();
for (Shadow shadow : innerShadows) {
this.setTextColor(shadow.color);
super.onDraw(tempCanvas);
this.setTextColor(0xFF000000);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
paint.setMaskFilter(new BlurMaskFilter(shadow.r, BlurMaskFilter.Blur.NORMAL));
tempCanvas.save();
tempCanvas.translate(shadow.dx, shadow.dy);
super.onDraw(tempCanvas);
tempCanvas.restore();
canvas.drawBitmap(tempBitmap, 0, 0, null);
tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
paint.setXfermode(null);
paint.setMaskFilter(null);
this.setTextColor(restoreColor);
this.setShadowLayer(0, 0, 0, 0);
}
}
if (restoreDrawables != null) {
this.setCompoundDrawablesWithIntrinsicBounds(restoreDrawables[0], restoreDrawables[1], restoreDrawables[2], restoreDrawables[3]);
}
this.setBackgroundDrawable(restoreBackground);
this.setTextColor(restoreColor);
unfreeze();
}
private void generateTempCanvas() {
String key = String.format("%dx%d", getWidth(), getHeight());
Pair<Canvas, Bitmap> stored = canvasStore.get(key);
if (stored != null) {
tempCanvas = stored.first;
tempBitmap = stored.second;
} else {
tempCanvas = new Canvas();
tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
tempCanvas.setBitmap(tempBitmap);
canvasStore.put(key, new Pair<Canvas, Bitmap>(tempCanvas, tempBitmap));
}
}
public void freeze() {
lockedCompoundPadding = new int[]{
getCompoundPaddingLeft(),
getCompoundPaddingRight(),
getCompoundPaddingTop(),
getCompoundPaddingBottom()
};
frozen = true;
}
public void unfreeze() {
frozen = false;
}
@Override
public void requestLayout() {
if (!frozen) super.requestLayout();
}
@Override
public void postInvalidate() {
if (!frozen) super.postInvalidate();
}
@Override
public void postInvalidate(int left, int top, int right, int bottom) {
if (!frozen) super.postInvalidate(left, top, right, bottom);
}
@Override
public void invalidate() {
if (!frozen) super.invalidate();
}
@Override
public void invalidate(Rect rect) {
if (!frozen) super.invalidate(rect);
}
@Override
public void invalidate(int l, int t, int r, int b) {
if (!frozen) super.invalidate(l, t, r, b);
}
@Override
public int getCompoundPaddingLeft() {
return !frozen ? super.getCompoundPaddingLeft() : lockedCompoundPadding[0];
}
@Override
public int getCompoundPaddingRight() {
return !frozen ? super.getCompoundPaddingRight() : lockedCompoundPadding[1];
}
@Override
public int getCompoundPaddingTop() {
return !frozen ? super.getCompoundPaddingTop() : lockedCompoundPadding[2];
}
@Override
public int getCompoundPaddingBottom() {
return !frozen ? super.getCompoundPaddingBottom() : lockedCompoundPadding[3];
}
public static class Shadow {
float r;
float dx;
float dy;
int color;
public Shadow(float r, float dx, float dy, int color) {
this.r = r;
this.dx = dx;
this.dy = dy;
this.color = color;
}
}
}
Github: https://github.com/345166018/AndroidUI/tree/master/HxZhibo 총결산
위 에서 말 한 것 은 편집장 이 소개 한 안 드 로 이 드 모방 라 이브 앱 선물 기능 입 니 다.도움 이 되 셨 으 면 좋 겠 습 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.