Play sequence of animations on one view from onAnimationEnd - java
I'm trying to play a sequence of more than 1 animations on the same progress bar. I am actually using a library called AnimateHorizontalProgressBar which allows the progress bars to fill up with an animation. The bar is an experience bar in a game, and if the player gets enough experience at one time, I want the bar to fill up as many times as there are level ups. (e.g. a level 1 character going to level 3 would have their experience bar fill up 2 full times, resetting whenever it gets full, then once more for however much progress they made from level 3 to level 4)
The solution I tried was to set an AnimateProgressListener on the AnimateHorizontalProgressBar, and in the onAnimationEnd function, to call the next animation to be played. The list containing these animations is an arraylist of custom POJOs which hold a reference to the view (the progress bar), and the percent to set it to.
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;
}
});
}
}
I added the whole class. Forgive the messy code.
The problem is that after the first animation which I start explicitly, it runs the onAnimationEnd function, and hits the line to run the next animation, but it never starts. The onAnimationStart function is never even called for the 2nd animation. Any ideas on why this might be?
I'll also add this takes place in a dialog fragment. Not sure if that is relevant however.
Related
Android GraphView and Mediaplayer falling out of synchronization
I'm working on a project, where I need to show the pitches of the words (from the song) in a GraphView. I wrote a program that would get the pitches from a song and create a .txt file from it. Then I wrote a class, that reads the file and creates a list from it. So in the end I would have a list that consists of words and word contains pitches. In the MakePitchesList you can see what the pitches output from a song looks like. I have 0,14:23281,61 on every line. The first part of the line is timeOccurance meaning, when this pitch was heard and the second part is the pitch itself. So in this example the timeOccurance would be 0,14 and pitch at that given time 23281,61. Here are the three classes that make a wordsList out of a pitch .txt file. public class Pitch{ float occuranceTime; float pitch; public void setOccuranceTime(float occuranceTime) { this.occuranceTime = occuranceTime; } public float getOccuranceTime() { return occuranceTime; } public void setPitch(float pitch) { this.pitch = pitch; } public float getPitch() { return pitch; } } public class MakePitchesList { String[] pitches; List<Pitch> listOfPitches = new ArrayList<Pitch>(); public List<Pitch> getListOfPitches(){ getPitches(); for (String pitchString: pitches) { Pitch pitch = new Pitch(); makeListOfPitches(pitch, pitchString); } return listOfPitches; } public void makeListOfPitches(Pitch pitch, String pitchString){ pitch.setPitch(getPitchesInfo(pitchString, 1)); pitch.setOccuranceTime(getPitchesInfo(pitchString, 0)); listOfPitches.add(pitch); } public String[] getPitches() { pitches = pitchesRaw.split("\\r?\\n"); return pitches; } private float getPitchesInfo(String pitch, int position){ String[] frequencyAndTime = pitch.split("\\:"); if(position == 0){ return Float.parseFloat(frequencyAndTime[0].replace(',', '.')); } if(position == 1){ return Float.parseFloat(frequencyAndTime[1].replace(',', '.')); } else return 0; } String pitchesRaw = "0,14:23281,61\n" + "0,23:53,65\n" + "0,37:72,53\n" + "0,56:86,09\n" + "0,60:88,58\n" + "0,65:87,45\n" + "0,70:87,11\n" + "0,74:89,56\n" + "0,79:96,22\n" + "0,84:23288,24\n" + "0,88:103,92\n" + "0,93:107,46\n" + "0,98:108,02\n" + "1,02:107,51\n" + "1,07:104,92\n" + "1,11:105,94\n" + "1,16:106,40\n" + "1,21:104,43\n" + "1,25:104,93\n" + "1,30:108,01\n" + "1,35:316,81\n" + "1,39:103,98\n" + "1,44:23297,42\n" + "1,49:23357,42\n" + "1,53:23359,74\n" + "1,58:23393,04\n" + "1,63:23244,18\n" + "1,67:23220,51\n" + "1,72:23250,06\n" + "1,76:23288,84\n" + "1,81:23241,81\n" + "1,86:23295,22\n" + "1,90:23268,04\n" + "1,95:23252,78\n" + "2,00:23224,22\n" + "2,04:23429,71\n" + "2,09:23214,58\n" + "2,14:23240,70\n" + "2,18:23237,71\n" + "2,23:23231,22\n" + "2,28:23222,77\n" + "2,32:23239,73\n" + "2,37:23235,98\n" + "2,41:23222,16\n" + "2,46:23224,01\n" + "2,51:23214,26\n" + "2,55:23223,20\n" + "2,60:23234,11\n" + "2,65:23221,65\n" + "2,69:23213,45\n" + "2,74:23217,44\n" + "2,79:23235,93\n" + "2,83:11122,79\n" + "2,88:23234,58\n" + "2,93:23229,52\n" + "2,97:23255,48\n" + "3,02:23254,44\n" + "3,07:23355,41\n" + "3,44:105,48\n" + "3,48:115,45\n" + "3,53:117,78\n" + "3,58:127,36\n" + "3,62:131,24\n" + "3,67:130,33\n" + "3,72:131,93\n" + "3,76:127,32\n" + "3,81:117,18\n" + "3,85:117,80\n" + "3,90:117,15\n" + "3,95:121,04\n" + "3,99:131,22\n" + "4,04:130,38\n" + "4,09:130,34\n" + "4,13:129,57\n" + "4,18:120,38\n" + "4,23:121,06\n" + "4,32:100,12\n" + "4,37:23483,16\n" + "4,41:112,95\n" + "4,46:23448,04\n" + "4,50:23396,09\n" + "4,55:23292,90\n" + "4,60:117,21\n" + "4,64:116,58\n" + "4,69:116,62\n" + "4,74:119,18\n" + "4,78:131,19\n" + "4,83:130,34\n" + "4,88:129,59\n" + "4,92:132,64\n" + "4,97:129,68\n" + "5,02:132,71\n" + "5,06:133,57\n" + "5,11:128,94\n" + "5,15:131,09\n" + "5,20:132,75\n" + "5,25:129,68\n" + "5,29:131,26\n" + "5,34:131,22\n" + "5,39:130,38\n" + "5,43:146,01\n" + "5,48:140,43\n" + "5,57:23450,16\n" + "5,62:130,46\n" + "5,67:132,02\n" + "5,71:23243,22\n" + "5,76:23456,28\n" + "5,85:23246,64\n" + "5,90:23274,97\n" + "5,94:23310,30\n" + "5,99:23229,71\n" + "6,08:23214,33\n" + "6,13:23221,53\n" + "6,18:23263,48\n" + "6,22:23213,17\n" + "6,27:23235,04\n" + "6,32:23222,02\n" + "6,36:23214,90\n" + "6,41:23230,05\n" + "6,46:23212,55\n" + "6,50:23221,33\n" + "6,55:23226,70\n" + "6,59:23217,07\n" + "6,64:23272,07\n" + "6,69:11102,74\n" + "6,73:23263,38\n" + "6,78:23217,53\n" + "6,97:23243,63\n" + "7,11:23214,11\n" + "7,15:23229,58\n" + "7,20:23225,70\n" + "7,24:23244,82\n" + "7,29:23243,09\n" + "7,34:23249,66\n" + "7,38:23226,67\n" + "7,43:23246,31\n" + "7,48:23258,55\n" + "7,52:23230,34\n" + "7,57:23225,60\n" + "7,62:23280,25\n" + "7,66:23238,08\n" + "7,71:23221,47\n" + "7,85:117,87\n" + "7,89:117,19\n" + "7,94:117,21\n" + "7,99:117,21\n" + "8,03:116,57\n" + "8,08:119,10\n" + "8,13:44,01\n" + "8,17:129,52\n" + "8,22:132,72\n" + "8,27:143,19\n" + "8,31:141,13\n" + "8,36:139,35\n" + "8,45:132,82\n" + "8,50:129,76\n" + "8,54:130,43\n" + "8,68:94,20\n" + "8,78:132,70\n" + "8,82:130,43\n" + "8,87:129,60\n" + "8,92:130,56\n" + "8,96:128,92\n" + "9,01:119,19\n" + "9,06:118,45\n" + "9,10:103,41\n" + "9,15:103,41\n" + "9,20:103,89\n" + "9,24:106,46\n" + "9,29:214,93\n" + "9,33:23427,95\n" + "9,38:23356,01\n" + "9,43:106,41\n" + "9,47:100,57\n" + "9,52:106,39\n" + "9,57:104,40\n" + "9,61:99,70\n" + "9,66:106,42\n" + "9,71:103,50\n" + "9,75:104,47\n" + "9,80:106,97\n" + "9,85:99,68\n" + "9,89:23454,22\n" + "9,94:23299,56\n" + "9,98:23275,30\n" + "10,03:23222,72\n" + "10,08:23246,09\n" + "10,12:23221,14\n" + "10,17:23240,54\n" + "10,22:23246,81\n" + "10,26:23224,74\n" + "10,31:23249,41\n" + "10,36:23214,79\n" + "10,40:23213,46\n" + "10,45:23259,51\n" + "10,50:23217,39\n" + "10,54:23215,36\n" + "10,59:23224,87\n" + "10,63:23242,27\n" + "10,68:23270,82\n" + "10,73:23243,19\n" + "10,77:23222,75\n" + "10,82:23268,78\n" + "10,87:23321,62\n" + "10,91:23259,65\n" + "11,05:23226,24\n" + "11,10:23222,92\n" + "11,15:23218,83\n" + "11,19:23211,71\n" + "11,24:11112,28\n" + "11,28:23261,03\n" + "11,33:23265,31\n" + "11,38:23245,92\n" + "11,42:57,09\n" + "11,61:103,45\n" + "11,66:103,91\n" + "11,70:102,02\n" + "11,75:107,96\n" + "11,80:105,43\n" + "11,84:104,46\n" + "11,89:116,64\n" + "11,94:115,99\n" + "11,98:114,77\n" + "12,03:121,72\n" + "12,07:123,16\n" + "12,12:125,12\n" + "12,17:128,85\n" + "12,21:120,37\n" + "12,26:116,52\n" + "12,31:130,55\n" + "12,35:131,06\n" + "12,40:131,89\n" + "12,45:128,88\n" + "12,49:23397,75\n" + "12,59:118,45\n" + "12,63:116,54\n" + "12,68:119,70\n" + "12,72:115,45\n" + "12,77:115,30\n" + "12,82:119,86\n" + "12,86:116,59\n" + "12,91:114,13\n" + "12,96:119,04\n" + "13,00:118,47\n" + "13,05:115,38\n" + "13,10:128,92\n"; } public class MakeWordsList { List<Pitch> pitches; public List<List<Pitch>> getWordsList(List<Pitch> pitchList) { return makeWordsList(pitchList); } List<Pitch> oneWord = new ArrayList<>(); List<List<Pitch>> wordList = new ArrayList<>(); public List<List<Pitch>> makeWordsList(List<Pitch> pitchList){ pitches = pitchList; int pauseCounter = 0; for (int i = 0; i < pitchList.size(); i++) { if(pitchList.get(i).getPitch() > 10000){ pauseCounter++; } else { if(pauseCounter > 0){ if(pauseCounter >= 5){ wordList.add(oneWord); oneWord = new ArrayList<>(); } pauseCounter = 0; } oneWord.add(pitchList.get(i)); } } if(oneWord.size() > 0){ wordList.add(oneWord); } return wordList; } } Now from that wordsList I create a scrollable GraphView that on paper would look something like this. Now I have set the boundaries for the GraphView: MinX = -0.8; MaxX = 0.4. This way I'm showing 1 sec at a time on screen. Then I start a thread so it would change the coordinates of the GraphView by 0.1 every 100ms, which should add up as 1 every sec. Here it is in code: public class MainActivity extends AppCompatActivity { final static double GRAPH_STARTING_X = -0.8; final static double GRAPH_ENDING_X = 0.4; List<java.util.List<Pitch>> wordsList; private Viewport graphViewPort; private Handler handler; private Runnable runnable; private GraphView graph; private DataPoint[] points; private LineGraphSeries<DataPoint> series; private int seriesNr; private double orderNr = GRAPH_STARTING_X; private ImageButton playRecordButton; private List<Pitch> pitchesList; private int songIndex; private MediaPlayer mediaPlayer; private boolean isPlaying = false; #SuppressLint("ClickableViewAccessibility") #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Get Textview and Button playRecordButton = (ImageButton) findViewById(R.id.playRecord); //Initialize pitches and words MakePitchesList listOfPithes = new MakePitchesList(); MakeWordsList listOfWords = new MakeWordsList(); pitchesList = listOfPithes.getListOfPitches(); wordsList = listOfWords.getWordsList(pitchesList); //Initialize graph graph = (GraphView) findViewById(R.id.graph); initGraph(); //ViewPort graphViewPort = graph.getViewport(); //Handler handler = new Handler(); playRecordButton.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View view) { if(!isPlaying){ playMedia(songIndex); //Start moving graph //Moved to PlayMedia - setOnPreparedListen // drawAndMoveGraph(); isPlaying = true; } else if(isPlaying){ //Move graph to starting position resetGraph(); //Stop Playing audio stopAudio(); isPlaying = false; } } }); } //Moves the graph every 100ms by 0.1 which results in 1 every second private void drawAndMoveGraph() { runnable = new Runnable() { public void run() { graphViewPort.setMinX(orderNr); graphViewPort.setMaxX(orderNr + 1); graph.invalidate(); if (pitchesList.size() != orderNr) { orderNr = orderNr + 0.1; } handler.postDelayed(this, 100); } }; runnable.run(); } //Load up and paint the graph private void initGraph() { for (int i = 0; i < wordsList.size(); i++) { seriesNr = 0; points = new DataPoint[wordsList.get(i).size()]; for (Pitch pitch : wordsList.get(i)) { points[seriesNr] = new DataPoint(pitch.getOccuranceTime(), pitch.getPitch()); seriesNr++; } series = new LineGraphSeries<>(points); series.setThickness(15); graph.addSeries(series); } //VocalTestPoints - Just for vocal testing PointsGraphSeries<DataPoint> series = new PointsGraphSeries<>(new DataPoint[]{ new DataPoint(0, 50), new DataPoint(0, 75), new DataPoint(0, 100), new DataPoint(0, 125), new DataPoint(0, 150), new DataPoint(0, 175), new DataPoint(0, 200), new DataPoint(0, 225), new DataPoint(0, 250), new DataPoint(0, 275), }); series.setSize(5); series.setColor(Color.YELLOW); graph.addSeries(series); // set manual X bounds graph.getViewport().setYAxisBoundsManual(true); graph.getViewport().setMinY(-50); graph.getViewport().setMaxY(400); graph.getViewport().setXAxisBoundsManual(true); graph.getViewport().setMinX(GRAPH_STARTING_X); graph.getViewport().setMaxX(GRAPH_ENDING_X); graph.getViewport().setScrollable(true); } // Play the chosen song and start moving the graph private void playMedia(int songIndex) { Uri uri = Uri.parse("android.resource://com.example.thermaltakei7.testsinging/" + R.raw.perfect); mediaPlayer = new MediaPlayer(); //Reset so that the MediaPlayer is not pointing to another data source mediaPlayer.reset(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); try { mediaPlayer.setDataSource(getApplicationContext(), uri); } catch (IOException e) { e.printStackTrace(); } mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { #Override public void onPrepared(MediaPlayer mp) { mediaPlayer.start(); drawAndMoveGraph(); } }); } //Stop audio from playing private void stopAudio() { mediaPlayer.stop(); mediaPlayer.release(); } //Reset graph to its initial position private void resetGraph() { handler.removeCallbacks(runnable); graphViewPort.setMinX(GRAPH_STARTING_X); graphViewPort.setMaxX(GRAPH_ENDING_X); graph.invalidate(); orderNr = GRAPH_STARTING_X; } } To sum up what the code does: it gets the wordsList from the pitches, initializes the graph, starts listening button click. When button is clicked, it starts moving graph as described above and plays audio. The problem is that the audio and moving graphView go out of sync. For the first 10sec or so it works like it should but then the lines start falling behind. I would really appreciate if someone took their time to understand all of this mess. Maybe picture of the actual final graphView will also help. . Blue line is the first incoming word. The audio's timing and line should meet at "0". I have also created a small sample that only contains the classes and topics mentioned above. It can be accessed from here: https://github.com/richardboss/testSinging This code containst a song in its raw folder so it's a bit bigger than it should be, but this was the fastest way to show a demo of what I was trying to say. Any ideas, suggestions, help is really welcome. Or comments on the code style.
Put two values in one Arraylist, then read them in one for loop
Is it possible to read the value of valueAfterMovingAverage and the value of FrontalSpeed from my arraylist list during the for loop? The technical issue is in my for loop I guess. It is reading only the size of valueAfterMovingAverage but not the values of FrontalSpeed that are stored in list array. So the for loop reads only the value of the first array. I should highlight that the two value's size (I mean the two variable array) aren't the same. How can I write their (into my text file) values in my for loop. The current results in my text file have the second value repeated and that is false. It has to be changed based on the reading from sensor accelerometer. float valueAfterMovingAverage; double TotalAccelerate; ArrayList<Float> list = new ArrayList<Float>(); #Override public final void onSensorChanged(SensorEvent event) { // The light sensor returns a single value. // Many sensors return 3 values, one for each axis. if (isListening) { double xx = event.values[0]; double yy = event.values[1]; double zz = event.values[2]; TotalAccelerate = Math.round(Math.sqrt(Math.pow(xx, 2) + Math.pow(yy, 2) + Math.pow(zz, 2))); Log.i(DEBUG, "Accelerometer = " + TotalAccelerate); FrontalSpeed = Math.round(Math.sqrt(Math.pow(xx, 2) + Math.pow(zz, 2))); Log.i(DEBUG, "list values " + listPeaks); MovingAverage ma = new MovingAverage(2); ma.newNum(TotalAccelerate); valueAfterMovingAverage = (float) ma.getAvg(); sensorText.setText(String.valueOf(valueAfterMovingAverage)); Log.i(DEBUG, "Moving avg: " + valueAfterMovingAverage); list.add(valueAfterMovingAverage); list.add(FrontalSpeed); Log.i(DEBUG,"Size of List: "+ list.size()); } } #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ... OnStore = (ToggleButton) findViewById(R.id.onStore); OnStore.setOnClickListener(new OnClickListener() { #Override public void onClick(View view) { if (OnStore.isChecked()) { // // set listening flag to true // isListening = true; Toast.makeText(getApplicationContext(), "Start...", Toast.LENGTH_LONG).show(); } else if (!OnStore.isChecked()) { // // set listening flag to false // isListening = false; try { for (double valueFromList : list) { String space = "\n"; byte[] convert = space.getBytes(); fileOutputStream.write(convert); String finalData; finalData = String.valueOf(valueFromList); String line = finalData + " " + FrontalSpeed + "\n"; Log.i(DEBUG, "speed: " + FrontalSpeed); fileOutputStream.write(line.getBytes()); Log.i(DEBUG, "ans: " + finalData); } fileOutputStream.flush(); fileOutputStream.close(); Toast.makeText(getApplicationContext(), "Message saving", Toast.LENGTH_LONG).show(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } Toast.makeText(getApplicationContext(), "Message Stopped.", Toast.LENGTH_LONG).show(); } } });
I would go with two separate Arrayslist's. One for holding the amount of valueAfterMovingAverage and another for FrontalSpeed. Just a suggestion.
How to get the index of the clicked ItemizedIconOverlay in OSM
How do i get the index position of the ItemizedIconOverlay<OverlayItem> anotherItemizedIconOverlay when the user taps the icon? For example, when a user taps/clicks the first icon it should get the integer 0 List<GeoPoint> nodes = nodeCoordinates(); ArrayList<OverlayItem> anotherOverlayItemArray = new ArrayList<>(); Drawable newMarker = getResources().getDrawable(R.drawable.marker_node); for(int i = 0; i < nodes.size(); i++) { anotherOverlayItemArray.add(new OverlayItem("Road", "Nodes", nodes.get(i))); anotherOverlayItemArray.get(i).setMarker(newMarker); } ItemizedIconOverlay<OverlayItem> anotherItemizedIconOverlay = new ItemizedIconOverlay<>( this, anotherOverlayItemArray, null); map.getOverlays().add(anotherItemizedIconOverlay);
There's an example here https://github.com/osmdroid/osmdroid/blob/master/OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/data/SampleMilitaryIconsItemizedIcons.java ack, formatting issues.... ` itemOverlay = new ItemizedOverlayWithFocus<>(new ArrayList(), new ItemizedIconOverlay.OnItemGestureListener() { #Override public boolean onItemSingleTapUp(final int index, final OverlayItem item) { Toast.makeText( context, "Item '" + item.getTitle() + "' (index=" + index + ") got single tapped up", Toast.LENGTH_LONG).show(); return true; } #Override public boolean onItemLongPress(final int index, final OverlayItem item) { Toast.makeText( context, "Item '" + item.getTitle() + "' (index=" + index + ") got long pressed", Toast.LENGTH_LONG).show(); return false; } }, context); `
List View Footer is leaving spaces and not getting removed
I am using list view footer which is just a button. Whenever i am updating the list with load more button it add extra space at the bottom. Also i am trying to remove the footer when all d data is loaded. Initialing the button in oncreate btnLoadMore = new Button(getActivity()); btnLoadMore.setText("Load More"); btnLoadMore.setWidth(700); btnLoadMore.setHeight(30); Then in post Execute mainAdapter = new SimpleAdapter( getActivity(), mainfooditems, R.layout.list_mealiteml, new String[] {KEY_FLFOODNAME,KEY_FLCALORIES,KEY_FLFOODID,KEY_FLFAT,KEY_FLCARBS,KEY_FLPROTEIN,KEY_FLBRAND,KEY_FLSERVING}, new int[] { R.id.nameone,R.id.uiduid,R.id.mtvfoodid,R.id.fatoutputval,R.id.carbsoutval,R.id.proteinsoutval,R.id.tvbrandnameone,R.id.tvservingone }); // updating listview listMain.addFooterView(btnLoadMore); listMain.setAdapter(mainAdapter); mainAdapter.notifyDataSetChanged(); state3(); And state3 is as follows: public void state3(){ search.clearFocus(); setListViewHeightBasedOnChildren(listMain); setListViewHeightBasedOnChildren(listCustom); setListViewHeightBasedOnChildren(listRecent); tvError.setVisibility(View.INVISIBLE); tvMessage.setVisibility(View.INVISIBLE); finalfoodidint=Integer.parseInt(finalfoodid); if(finalfoodidint > oldlastid){ System.out.println("1.5 + " + finalfoodidint); System.out.println("1.6 + " + oldlastid); } else if(finalfoodidint == oldlastid){ //listMain.removeFooterView(btnLoadMore); listMain.removeFooterView(btnLoadMore); System.out.println("1.7 + " + finalfoodidint); System.out.println("1.8 + " + oldlastid); } else{ listMain.removeFooterView(btnLoadMore); System.out.println("1.9 + " +finalfoodidint); System.out.println("1.10 + " + oldlastid); } }
Try calling notifyDataSetChanged() on the list when you remove an header. Try adding this line at the end of public void state3() listMain.getAdapter().notifyDataSetChanged();
I changed my code in public void state3 to public void state3(){ search.clearFocus(); listMain.removeFooterView(btnLoadMore); tvError.setVisibility(View.INVISIBLE); tvMessage.setVisibility(View.INVISIBLE); finalfoodidint=Integer.parseInt(finalfoodid); if(finalfoodidint > oldlastid){ listMain.addFooterView(btnLoadMore); System.out.println("1.5 + " + finalfoodidint); System.out.println("1.6 + " + oldlastid); } else if(finalfoodidint == oldlastid){ //listMain.removeFooterView(btnLoadMore); listMain.removeFooterView(btnLoadMore); System.out.println("1.7 + " + finalfoodidint); System.out.println("1.8 + " + oldlastid); } else{ listMain.removeFooterView(btnLoadMore); System.out.println("1.9 + " +finalfoodidint); System.out.println("1.10 + " + oldlastid); } listMain.setAdapter(mainAdapter); mainAdapter.notifyDataSetChanged(); setListViewHeightBasedOnChildren(listMain); setListViewHeightBasedOnChildren(listCustom); setListViewHeightBasedOnChildren(listRecent); } Also changed post execute to mainAdapter = new SimpleAdapter( getActivity(), mainfooditems, R.layout.list_mealiteml, new String[] {KEY_FLFOODNAME,KEY_FLCALORIES,KEY_FLFOODID,KEY_FLFAT,KEY_FLCARBS,KEY_FLPROTEIN,KEY_FLBRAND,KEY_FLSERVING}, new int[] { R.id.nameone,R.id.uiduid,R.id.mtvfoodid,R.id.fatoutputval,R.id.carbsoutval,R.id.proteinsoutval,R.id.tvbrandnameone,R.id.tvservingone }); // updating listview state3();
change your "setListViewHeightBasedOnChildren" method like below. public static void setListViewHeightBasedOnChildren(ListView listView) { ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) { // pre-condition return; } int totalHeight = 0; int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST); for (int i = 0; i < listAdapter.getCount(); i++) { View listItem = listAdapter.getView(i, null, listView); listItem.measure(desiredWidth, MeasureSpec.UNSPECIFIED); totalHeight += listItem.getMeasuredHeight(); } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(params); listView.requestLayout(); }
Adding a item from another activity to ExpandableListView
I want to add a item to the child Today from another activity to the ExpandableListView. The activity where I want to add it is named LocHistory, here is a the code to add something to the list: static void addListData(final Context context) { List<NewsItem> list = listDataChild.get("Today"); NewsItem newsData = new NewsItem(); newsData = new NewsItem(); newsData.setHeadline("11.11111, 1.1111"); newsData.setSpeed("1.11KM/H"); newsData.setDirection("111"); newsData.setDate("11-1-1111 11:11:11"); list.add(0, newsData); listDataChild.put("Today", list); } This is working when I have call the function in the same class (LocHistory). But when I call it in MainActivity like this: public class MainActivity extends Activity { Button button2; #Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button2 = (Button) this.findViewById(R.id.button2); button2.setOnClickListener(new OnClickListener() { public void onClick(View v) { LocHistory.addListData(getBaseContext()); } }); } } Then there is nothing added to the list. Is it possible to add a item from another activity to ExpandableListView? I want if there's something added that the class LocHistory is not going to open, so I think startActivity with a intent is not a option here (but i'm not sure). (The java sources can be found here: MainActivity.java, LocHistory.java, NewsItem.java and ExpandableListAdapter.java) Edit: As some guys on a other forum pointed out, I'm now using SharedPreferences. I'm using this code: static void addListData (int TimeStamp, final String lat, final String lng, final String speed, final String direction, final Context context){ int todaystamp = startOf("today"); int yesterdaystamp = startOf("yesterday"); String Datetime = DateFormat.format("dd-MM-yyyy kk:mm:ss", new Date(TimeStamp * 1000L)).toString(); SharedPreferences pref = context.getSharedPreferences("myPrefs", MODE_PRIVATE); SharedPreferences.Editor editor = pref.edit(); if (TimeStamp >= todaystamp) { editor.putString("Today", "*headline=" + lat + ", " + lng + ";speed=" + speed + ";direction=" + direction + ";date=" + Datetime + ";"); } else if (TimeStamp >= yesterdaystamp) { editor.putString("Yesterday", "*headline=" + lat + ", " + lng + ";speed=" + speed + ";direction=" + direction + ";date=" + Datetime + ";"); } else if (TimeStamp < yesterdaystamp) { editor.putString("Older", "*headline=" + lat + ", " + lng + ";speed=" + speed + ";direction=" + direction + ";date=" + Datetime + ";"); } editor.commit(); } But now I'm stuck with one problem, when I add a item to the SharedPreferences on the same key it will overwrite the previous data. How can I add data to the same key without overwriting the previous data? Is it maybe possible to first get the data and then join the item to the data after that add the data to the SharedPreferences?
For you last edit you can try this: static void addListData(int TimeStamp, final String lat, final String lng, final String speed, final String direction, final Context context) { int todaystamp = startOf("today"); int yesterdaystamp = startOf("yesterday"); String Datetime = DateFormat.format("dd-MM-yyyy kk:mm:ss", new Date(TimeStamp * 1000L)).toString(); String location = "*headline=" + lat + ", " + lng + ";speed=" + speed + ";direction=" + direction + ";date=" + Datetime + ";"; SharedPreferences pref = context.getSharedPreferences("myPrefs", MODE_PRIVATE); SharedPreferences.Editor editor = pref.edit(); if (TimeStamp >= todaystamp) { String today = pref.getString("Today",null); if (today != null) { StringBuilder str = new StringBuilder(today); str.insert(0, location + ", "); editor.putString("Today", str.toString()); } else { editor.putString("Today", location); } } else if (TimeStamp >= yesterdaystamp) { String yesterday = pref.getString("Yesterday",null); if (yesterday != null) { StringBuilder str = new StringBuilder(yesterday); str.insert(0, location + ", "); editor.putString("Yesterday", str.toString()); } else { editor.putString("Yesterday", location); } } else if (TimeStamp < yesterdaystamp) { String older = pref.getString("Older",null); if (older != null) { StringBuilder str = new StringBuilder(older); str.insert(0, location + ", "); editor.putString("Older", str.toString()); } else { editor.putString("Older", location); } } editor.commit(); } This will ensure that it not overrides the key but append if it exists. (This is done by checking whether the SharedPreferences is not null for a specific key). I use StringBuilder with the insert method in this case.