我正在尝试在同一进度条上播放超过1个动画的序列。我实际上正在使用一个名为 AnimateHorizontalProgressBar 的库,该库允许进度条充满动画。该栏是游戏中的体验栏,如果玩家一次获得足够的经验,我希望该栏可以填满尽可能多的次数。 (例如,从1级角色升级到3级角色时,他们的经验栏将填满2次,每次填满就重置一次,然后再从3级升级到4级时再进行一次重置)
我尝试的解决方案是在 AnimateHorizontalProgressBar 上设置AnimateProgressListener
,并在onAnimationEnd
函数中调用下一个要播放的动画。包含这些动画的列表是arraylist
的自定义POJO,其中包含对视图(进度条)的引用以及将其设置为的百分比。
public class MonsterBattleResolutionFragment extends MMOBaseFragment {
private static final String TAG = "Resolution Frag";
boolean isVictorious;
boolean playerCapturedMonster;
private String wildMonsterId;
ToolTipManager tooltips;
int monsterLevel;
private FirebaseFunctions mFunctions;
private ArrayList<String> monsterKeys;
private ArrayList<PlayerMonster> monstersList;
private BattleResolutionXpGainedAdapter battleResolutionXpGainedAdapter;
private Typeface typeface;
TextView victoryTextView;
private ListView xpGainedLayoutContainer;
private RecyclerView itemRecyclerView;
private PlayerItemAdapter playerItemAdapter;
ArrayList<PlayerItem> playerItems;
private FirebaseUser user;
private DatabaseReference mDatabase;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_monster_battle_resolution, container, false);
mFunctions = FirebaseFunctions.getInstance();
typeface = CustomTypeFace.getNovemberTypeface(getContext());
victoryTextView = view.findViewById(R.id.monster_battle_resolution_text_view);
victoryTextView.setTypeface(typeface);
tooltips = new ToolTipManager(getActivity());
itemRecyclerView = view.findViewById(R.id.monster_battle_resolution_item_recycler_view);
itemRecyclerView.setNestedScrollingEnabled(false);
itemRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
playerItems = new ArrayList<>();
playerItemAdapter = new PlayerItemAdapter(playerItems);
itemRecyclerView.setAdapter(playerItemAdapter);
isVictorious = getArguments().getBoolean("victory");
playerCapturedMonster = getArguments().getBoolean("playerCapturedMonster");
monsterLevel = getArguments().getInt("monsterLevel");
monsterKeys = getArguments().getStringArrayList("monsterKeys");
monstersList = getArguments().getParcelableArrayList("monstersList");
for (int i = 0; i < monstersList.size(); i++){
Log.e(TAG, monstersList.get(i).getMonsterStringName());
}
wildMonsterId = getArguments().getString("wildMonsterId");
ArrayList<String> playerMonsterIds = new ArrayList<>();
for (int i = 0; i < monstersList.size(); i++){
Log.e(TAG, "fb id is " + monstersList.get(i).getFirebaseId());
playerMonsterIds.add(monstersList.get(i).getFirebaseId());
}
Task<HashMap<String, Integer>> test = firebaseResolveCombat(wildMonsterId, playerMonsterIds);
test.addOnCompleteListener(new OnCompleteListener<HashMap<String, Integer>>() {
@Override
public void onComplete(@NonNull Task<HashMap<String, Integer>> task) {
Object gainedXp = task.getResult().get("experience");
Object items = task.getResult().get("items");
Object skills = task.getResult().get("skills");
Object newMonsterLevels = task.getResult().get("newMonsterLevels");
// gainedXp is the xp gained for each monster involved in the fight
if (gainedXp != null){
Log.e(TAG, "experience earned: " + gainedXp);
updateXpBars((int) gainedXp);
}
if (items != null){
Log.e(TAG, "items earned: " + items.toString());
HashMap<String, Integer> itemsEarnedMap = (HashMap<String, Integer>) items;
for (Map.Entry<String, Integer> item : itemsEarnedMap.entrySet()) {
Log.e(TAG, "key: " + item.getKey() + ", value: " + item.getValue());
// add new item to list for adapter. NOTE: as of now, key is a string, while value (quantity) is an int. slight issue on javascript side, fine for now.
playerItems.add(ItemDirectory.itemLookup(Integer.parseInt(item.getKey()), item.getValue()));
}
}
if (skills != null){
Log.e(TAG, "new skills: " + skills.toString());
HashMap<String, ArrayList<Integer>> newSkills = (HashMap<String, ArrayList<Integer>>) skills;
for (Map.Entry<String, ArrayList<Integer>> item : newSkills.entrySet()) {
for (Integer skill : item.getValue()) {
String skillName = SkillDirectory.skillLookup(skill).getName();
Log.e(TAG, item.getKey() + " learned skill " + skillName);
for (int i = 0; i < monstersList.size(); i++){
Log.e(TAG, "fb id is " + monstersList.get(i).getFirebaseId());
if (monstersList.get(i).getFirebaseId().equals(item.getKey())){
Toast.makeText(getContext(), monstersList.get(i).getMonsterStringName() + " learned skill " + skillName + "!", Toast.LENGTH_SHORT).show();
}
}
}
}
}
if (newMonsterLevels != null){
Log.e(TAG, "new levels: " + newMonsterLevels.toString());
}
}
});
xpGainedLayoutContainer = view.findViewById(R.id.monster_battle_resolution_xp_gained_layout_container);
battleResolutionXpGainedAdapter = new BattleResolutionXpGainedAdapter(getActivity(), R.layout.battle_resolution_xp_gained_layout, monstersList);
xpGainedLayoutContainer.setAdapter(battleResolutionXpGainedAdapter);
justifyListViewHeightBasedOnChildren(xpGainedLayoutContainer);
// if the player did not win, don't show items won, give them half xp
if (!isVictorious) {
victoryTextView.setText(R.string.defeat);
}
// listener for button to return to map
FloatingActionButton b = view.findViewById(R.id.monster_battle_resolution_return_to_map_button);
b.setOnClickListener(view1 -> {
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
});
return view;
}
private void updateXpBars(int xpPerMonster){
ArrayList<LevelUpBarAnimation> animationList = new ArrayList<>();
// First build the list of animations
Log.e(TAG, "There are " + battleResolutionXpGainedAdapter.getCount() + " xp bars to animate");
for (int i = 0; i < battleResolutionXpGainedAdapter.getCount(); i++){
int currentXp = monstersList.get(i).getExperience();
int level = monstersList.get(i).getLevel();
int newXp = currentXp + xpPerMonster;
int lastLevelXp = getExperienceRequiredForNextLevel(level-1);
boolean isLevelUp = false;
int xpNumerator = newXp - lastLevelXp;
int xpDenominator = getExperienceRequiredForNextLevel(level) - lastLevelXp;
Log.e(TAG, "Monster was level: " + level + ", and had " + currentXp + " xp");
Log.e(TAG, "now has " + newXp + " xp. level now: " + getLevelBasedOnExperience(newXp));
// In a loop, build up every animation which needs to be done on this particular xp bar. Loop is so multiple animations may be played for multiple level ups\
// do while because even when the monster does not level up, they still will get some xp so at least 1 anim needs to be played
do {
isLevelUp = false; // Need to reset to know when we have not leveled up
AnimateHorizontalProgressBar bar = xpGainedLayoutContainer.getChildAt(i).findViewById(R.id.battle_resolution_xp_gained_xp_bar);
bar.setMax(10000);
if (newXp > getExperienceRequiredForNextLevel(level)){
isLevelUp = true;
level++;
xpNumerator = xpDenominator; // Sets the xp bar to full before resetting at 0 for the next level
}
Log.e(TAG, "Added new anim for bar at position " + i + ", " + "out of 10000: " + (int) (((double) xpNumerator / (double) xpDenominator) * 10000.0));
// TODO: clean up messy cast
LevelUpBarAnimation levelUpBarAnimation = new LevelUpBarAnimation(bar, (int) (((double) xpNumerator / (double) xpDenominator) * 10000.0), level);
levelUpBarAnimation.setLevelUp(isLevelUp);
Log.e(TAG, "islevelup set to " + String.valueOf(levelUpBarAnimation.isLevelUp()) + ", going from level " + (level-1) + " to " + (level));
levelUpBarAnimation.setLevelTxtView(xpGainedLayoutContainer.getChildAt(i).findViewById(R.id.battle_resolution_xp_gained_level_text_view));
animationList.add(levelUpBarAnimation);
} while(isLevelUp);
}
final int[] animNumber = {0};
Log.e(TAG, "animlist size " + animationList.size() + " playing across " + battleResolutionXpGainedAdapter.getCount() + " xp bars");
for (int i = 0; i < battleResolutionXpGainedAdapter.getCount(); i++) {
AnimateHorizontalProgressBar bar = animationList.get(i).getXpBar();
bar.setAnimateProgressListener(new AnimateHorizontalProgressBar.AnimateProgressListener() {
@Override
public void onAnimationStart(int progress, int max) {
Log.e(TAG, "anim #" + animNumber[0] + " starting");
}
@Override
public void onAnimationEnd(int progress, int max) {
// If this xp bar filled up, play a flash sort of animation, and update the lvl text
if (animationList.get(animNumber[0]).isLevelUp()){
int newLvl = animationList.get(animNumber[0]).getLevel();
Log.e(TAG, "level up animation playing. anim number " + animNumber[0] + ", now level " + newLvl);
// Update the lvl txt
animationList.get(animNumber[0]).getLevelTxtView().setText("LVL: " + newLvl);
// Then set the progress on this bar back to 0
animationList.get(animNumber[0]).getXpBar().setProgress(0);
}
Log.e(TAG, "anim number " + animNumber[0] + " finished, next playing " + (animNumber[0] + 1));
// Do not attempt to play the next animation if this one was the last
if (animNumber[0] < animationList.size() - 1){
animNumber[0]++;
Log.e(TAG, "playing anim number " + animNumber[0] + " to " + animationList.get(animNumber[0]).getProgressPercent() + "% full");
animationList.get(animNumber[0]).getXpBar().setProgressWithAnim(animationList.get(animNumber[0]).getProgressPercent()); // TODO: this not getting called
}
}
});
}
Log.e(TAG, "playing anim number 0");
animationList.get(0).getXpBar().setProgressWithAnim(animationList.get(0).getProgressPercent());
}
// On receiving a new skill, this tooltip will appear momentarily to notify the player
public void showNewSkillTooltip(String skillName, View view) {
Log.e(TAG, "Showing new skill tooltip. skill learned: " + skillName);
// Dynamically build a layout here, as it cannot already have a parent layout if we want to assign it to the tooltip
LinearLayout tooltipLayout = new LinearLayout(getContext());
tooltipLayout.setOrientation(LinearLayout.VERTICAL);
TextView talentTitleText = new TextView(getContext());
talentTitleText.setTextSize(18f);
talentTitleText.setText(skillName);
tooltipLayout.addView(talentTitleText);
ToolTip toolTip = new ToolTip()
.withColor(getResources().getColor(R.color.grey_300)) //or whatever you want
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
.withContentView(tooltipLayout)
.withShadow();
tooltips.showToolTip(toolTip, view);
}
private Task<HashMap<String, Integer>> firebaseResolveCombat(String wildMonsterFirebaseId, ArrayList<String> playerMonsterIds) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("wildMonsterId", wildMonsterFirebaseId);
data.put("playerMonsterIds", playerMonsterIds);
return mFunctions
.getHttpsCallable("resolveWildMonsterCombat")
.call(data)
.continueWith(new Continuation<HttpsCallableResult, HashMap<String, Integer>>() {
@Override
public HashMap then(@NonNull Task<HttpsCallableResult> task) throws Exception {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
HashMap<String, Integer> result = (HashMap<String, Integer>) task.getResult().getData();
return result;
}
});
}
}
我增加了全班。原谅凌乱的代码。
问题在于,在我显式开始的第一个动画之后,它运行onAnimationEnd函数,并撞上一行以运行下一个动画,但它从未开始。第二动画甚至不会调用onAnimationStart函数。关于为什么会这样的任何想法?
我还将添加此操作在对话框片段中进行。不确定是否相关。