AcgDialogueFragment 負責展示單條動漫台詞,支援:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_acg_dialogue, container, false);
int id = (Integer) getArguments().getSerializable(EXTRA_ACG_ID);
mAcg = AcgLab.get(getActivity()).getAcg(id);
// 1. 台詞文本:標點換行
mDialogueTextView = (TextView) v.findViewById(R.id.dialogue_tv);
String dialogue = mAcg.getDialogue()
.replace(",", "\n")
.replace("。", "\n")
.replace("?", "\n")
.replace("!", "\n");
mDialogueTextView.setText(dialogue);
// 2. 角色名
mPersonageTextView = (TextView) v.findViewById(R.id.personage_tv);
if (!mAcg.getPersonage().isEmpty())
mPersonageTextView.setText("--" + mAcg.getPersonage());
else
mPersonageTextView.setText("");
// 3. 頭像圖片與點擊事件
mHeadImageView = (ImageView) v.findViewById(R.id.avatar_iv);
mRippleImageView = (ImageView) v.findViewById(R.id.ripple_iv);
if (mAcg.getId() < AcgListFragment.SYSTEM_DIALODUE_LENGHT) {
// 系統預設:資源 ID
mHeadImageView.setImageResource(Integer.parseInt(mAcg.getHead()));
mHeadImageView.setOnClickListener(v -> {
// 波紋動畫
Animation anim = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_ripple);
mRippleImageView.startAnimation(anim);
// 播放音效 (資源 ID)
AcgDialogueFragment.getPlayer().play(getActivity(), Integer.parseInt(mAcg.getSound()));
});
} else {
// 用戶自定義:本地文件路徑
File file = new File(mAcg.getHead());
if (mAcg.getHead().isEmpty() || !file.exists()) {
mHeadImageView.setImageResource(R.drawable.saber); // 默認頭像
} else {
Bitmap bmp = PictureUtils.getScaledBitmapFromPath(getActivity(), mAcg.getHead());
bmp = PictureUtils.getRoundBitmap(bmp); // 圓形裁剪
mHeadImageView.setImageBitmap(bmp);
}
mHeadImageView.setOnClickListener(v -> {
Animation anim = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_ripple);
mRippleImageView.startAnimation(anim);
if (mAcg.getId() < AcgListFragment.SYSTEM_DIALODUE_LENGHT) {
AcgDialogueFragment.getPlayer().play(getActivity(), Integer.parseInt(mAcg.getSound()));
} else {
if (mAcg.getSound().isEmpty()) {
Toast.makeText(getActivity(), R.string.tip_not_sound, Toast.LENGTH_SHORT).show();
} else {
AcgDialogueFragment.getPlayer().play(getActivity(), mAcg.getSound()); // 本地文件路徑
}
}
});
}
return v;
}
getAcg(id)。getScaledBitmapFromPath(Context, path):按屏幕密度縮放加載,防止 OOM。getRoundBitmap(Bitmap):使用 BitmapShader 或 Canvas.clipPath 實現圓形頭像。MediaPlayer / SoundPool,play(Context, int resId) 與 play(Context, String filePath) 統一調用入口。
<FrameLayout ...>
<ImageView android:id="@+id/avatar_iv" ... />
<ImageView android:id="@+id/ripple_iv" android:src="@drawable/ripple" android:visibility="invisible" />
<TextView android:id="@+id/dialogue_tv" android:textSize="16sp" ... />
<TextView android:id="@+id/personage_tv" android:textSize="14sp" android:textColor="#888" ... />
</FrameLayout>
<set xmlns:android="https://schemas.android.com/apk/res/android">
<scale android:fromXScale="0.5" android:toXScale="2.0"
android:fromYScale="0.5" android:toYScale="2.0"
android:pivotX="50%" android:pivotY="50%"
android:duration="600" />
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="600" />
</set>
mHeadImageView.setOnClickListener 處理,邏輯內部分發。play(Context, Object),下層自動識別資源 ID 或文件路徑。exists() 校驗、縮放、圓形裁剪,避免大圖 OOM 且統一風格。