Do an action when a variable change - java

I'm a beginner in android development. I have a Fragment (FragA) in which I have a button that does a calculation and set the value to a variable (x) and pass this value to another fragment (FragB). Every time I press the button in FragA the value of "x" is passed to FragB.
Now, what I want is to do an action in FragB (refresh the view of a graph) each time the value of "x" is changed. If you need more information feel free to ask.
Thanks

I found a way to make a RealTime chart here. The important part is:
public class BH06_Graph_Frag extends Fragment{
private Runnable mTimer;
//i.e private LineGraphSeries<DataPoint> mSeries;
...
#Override
public void onResume() {
super.onResume();
mTimer = new Runnable() {
#Override
public void run() {
//reception of value "x"
//do something
// i.e mSeries.resetData(new DataPoint[] {
// new DataPoint(6, x) });
//
mHandler.postDelayed(this, 300);
}
};
mHandler.postDelayed(mTimer, 300);
}

Related

How can you change ViewPager2 position inside the ViewPager2Adapter?

I programmed a Vocabulary Trainer with Vocabulary Cards. The Vocabulary Cards are Entries in a Room Database created from an asset. I am displaying these Vocabulary Cards with ViewPager2 in an Activity. I have a 'correct' and a 'false' button and when the user clicks on either, I want to update the Vocabulary Card (-> The entry in the sqlite database) and automatically swipe to the next item of the ViewPager2.
If I implement the buttons in the ViewPager2Adapter, I can't find a way to change the position of the ViewPager2. If I implement the buttons in the activity the sqlite entry does not update properly (After it updates the entry, the activity is constantly refreshed, it seems like it never the leaves the OnClick methode of the button).
So is it possible to change the position of ViewPager2 from inside the ViewPager2Adpater?
Thanks for your help!
That is the relevant code if I have the buttons in my ViewPager2Adapter. Here I don't know how to change the position of the ViewPager2
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
holder.btn_false.setOnClickListener(v15 -> {
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
That is the relevant code if I have the buttons in the Activity. Here the update function triggers an infinite updating of the Activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
btn_correct_2.setOnClickListener(view -> {
int currentPos = viewpager2.getCurrentItem();
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
if (vocabularyCards.size() == currentPos){
Intent intent = new Intent(TestActivity.this, MainActivity.class);
startActivity(intent);
}else {
viewpager2.setCurrentItem(currentPos + 1);
}
VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
});
btn_false_2.setOnClickListener(view -> {
int currentPos = viewpager2.getCurrentItem();
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
if (vocabularyCards.size() == currentPos){
Intent intent = new Intent(TestActivity.this, MainActivity.class);
startActivity(intent);
}else {
viewpager2.setCurrentItem(currentPos + 1);
}
VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
});
Objects.requireNonNull(getSupportActionBar()).setTitle(getResources().getString(R.string.learn_new_words));
LiveData<List<VocabularyCard>> allNewCards = vocabularyViewModel.getAllNewCards(goal);
allNewCards.observe(this, vocabularyCards -> vocabularyViewModel.setCurrentCards(vocabularyCards));
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
viewPager2Adapter.setCurrentCards(vocabularyCards);
viewpager2.setAdapter(viewPager2Adapter);
viewpager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
});
The update function in the Room DAO is straightforward:
#Update
void updateSingleVocabularyCard(VocabularyCard vocabularyCard);
I left out all the code that is not relevant.
There are several ways to propagate an event from the adapter to the activity where you manage your cards using ViewPager2. Let's have a look how it can be done either using an interface or using the same view model. But in any case I strongly recommend you to update your database in a background thread to prevent any possible UI lags.
1. Using an interface
This option is more flexible since you can propagate events as well as pass data as parameters. You can also reuse this interface for other cases. As far as I See you have a holder that has 2 buttons for the users to make choices. So our event here would be something like ChoiceEventListener, let's call this interface like so. Then you'd have to add a method to handle this event from within anywhere you wanna hear this event, and let's call its handle method onChoice(). Finally we would need a variable to indicate what the choice is. Now that ready to implement, let's write the new interface...
ChoiceEventListener.java
public interface ChoiceEventListener {
void onChoice(VocabularyCard vocabularyCard, boolean choice);
}
The next thing to do is to implement this interface where you want to listen to this event. In this case it is in your activity. There are 2 ways to do this:
You make your activity to inherit its methods using the implements keyword
YourActivity.java
public class YourActivity extends AppCompatActivity implements ChoiceEventListener {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
// You must construct your adapter class with the listener
ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, this);
}
#Override
public void onChoice(VocabularyCard vocabularyCard, boolean choice) {
if(choice) {
// User pressed the correct button
}
else {
// User pressed the false button
}
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
}
}
You can implement it as an anonymous function
YourActivity.java
public class YourActivity extends AppCompatActivity {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
// You must construct your adapter class with the listener
ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, (vocabularyCard, choice) -> {
if(choice) {
// User pressed the correct button
}
else {
// User pressed the false button
}
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
});
}
}
Finally the ViewPager2Adapter class implementation would be something like this:
ViewPager2Adapter.java
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {
// Here is your listener to deliver the choice event to it
private final ChoiceEventListener listener;
// Constructor
public ViewPager2Adapter(/* Other params... */, ChoiceEventListener listener) {
/* Other inits */
this.listener = listener;
}
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
listener.onChoice(vocabularyCard, true); // true for correct
});
holder.btn_false.setOnClickListener(v15 -> {
listener.onChoice(vocabularyCard, false); // false for false :)
});
}
}
2. Use the ViewModel for inter-communication
In this option we use a LiveData object to make page switching. The only thing you need to know in your activity is the current position which you get it from the adapter class. Once you update it in the adapter, set the current position value in live data so that you can switch the page in your activity.
VocabularyViewModel.java
public class VocabularyViewModel extends ViewModel {
public MutableLiveData<Integer> mldCurrentPosition = new MutableLiveData<>(0);
}
YourActivity.java
public class YourActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
vocabularyViewModel.mldCurrentPosition().observe(this, currentPosition -> {
if(currenPosition == null) return; // ignore when null
viewpager2.setCurrentItem(currentPosition + 1);
}
}
}
Finally the ViewPager2Adapter class implementation would be something like this:
ViewPager2Adapter.java
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
// Then invoke switching to the next card
vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
});
holder.btn_false.setOnClickListener(v15 -> {
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
// Then invoke switching to the next card
vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
});
}
}

Possible reasons of not showing up the view on SurfaceView

I have my custom view that extends SurfaceView and here I have a spinner among others on it. I set its visibility to visible and invisible in turns but after one cycle of showing and hiding, next time it does not show up anymore. To verify what's wrong I draw on canvas text:
spiner3.getVisibility()
spiner3.getX()
spiner3.getY()
but nothing of these seem to be wrong I mean X and Y are on the within the screen and getVisibility returns View.VISIBLE. So what can be the other reason of not showing up if it is not visibility property and coordinations?
I did a workaround. In Activity that uses this view I added such code:
Handler mTimerHandler;
Timer timer = new Timer();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gra_layout);
vg = findViewById(R.id.content_frame);
refreshSpinner();
...
}
private void refreshSpinner() {
mTimerHandler = new Handler();
timer.schedule(new TimerTask() {
public void run() {
mTimerHandler.post(() -> {
vg.invalidate();
});
}
}, 0, 100);
}

android rotating screen causes text colour to change to default

In my android app where I am creating a tic tac toe game, I have this code below where if it's player one move then set their selection as X with a particular colour, else it must be player 2 so set text as O for their selection with a different colour.
#Override
public void onClick(View v) {
if (!((Button) v).getText().toString().equals("")) {
return;
}
if (playerOneMove) {
((Button) v).setText("X");
((Button) v).setTextColor(Color.parseColor("#e8e5e5"));
} else {
((Button) v).setText("O");
((Button) v).setTextColor(Color.parseColor("#737374"));
}
...
}
I have a problem though and it is in regards to when I rotate the screen. When I rotate the screen, the text for X and O both change to the default text colour android studio provides. I want to keep the colours for these text but I am not sure how to do that? I have made sure there is no global text colour set.
Below is my code that handles orientation changes:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("playerOneMove", playerOneMove);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
playerOneMove = savedInstanceState.getBoolean("playerOneMove");
}
You need to save the colours of each box of your tic tac toe board and retain them again on your layout configuration changes (i.e. device rotation).
You might consider looking into the answer here for a detailed explanation of your problem. You might check the developer documentation here for handling the configuration changes as well.
The key idea is to save the layout statues in variables which survives the configuration changes and update them accordingly in your onCreate or onCreateView function in case of Activity and Fragment respectively. However, in your case, you need to store a lot of data and on each configuration change, you need to restore them again which is not an efficient way to do that. I would like to recommend you look for other available options which survive the orientation or configuration changes of your layout.
I would strongly suggest implementing ViewModel in your case, which survives the application configuration change and handles the overall UI representation in the most effective way. The idea is to bind your UI elements with your ViewModel and then retain the UI elements each time from your ViewModel. It can be retained at the exact state until the Activity or Fragment finishes.
In your case, I would like to provide an example of how you can prepare a ViewModel. Let us consider your ViewModel is GameModel which saves the layout items of your board.
public class GameModel extends ViewModel {
public final LiveData<Game> gameLiveData = new LiveData<>();
public GameModel() {
// trigger game load.
}
void doAction() {
// depending on the action, do necessary business logic calls and update the gameLiveData.
}
}
public class Game {
public static final int CROSS = 1;
public static final int ZERO = 0;
public int pos1 = -1; // Default values are -1, when the position is yet to be played
public int pos2 = -1;
public int pos3 = -1;
public int pos4 = -1;
public int pos5 = -1;
public int pos6 = -1;
public int pos7 = -1;
public int pos8 = -1;
public int pos9 = -1;
}
Now from your Activity, you need to add an observer to your GameModel class to update the UI accordingly.
public class UserActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_activity_layout);
final GameModel viewModel = ViewModelProviders.of(this).get(GameModel.class);
viewModel.gameLiveData.observer(this, new Observer() {
#Override
public void onChanged(#Nullable Game gameData) {
// update ui.
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Pass parameters in doAction function to set the items in the Game class and update the ui accordingly inside the onChanged function.
viewModel.doAction();
}
});
}
}
Hope that helps!

Jface Dialog, How to retrieve correctly what button pressed the user?

I'm having troubles with a custom Dialog in Eclipse.
in the first place, I created a Class that extend Dialog.
public class ModificarGrupoBCDialog extends Dialog {
private static final int CANCELAR = 999;
private static final int MODIFICAR = 1;
...
somewhere I create the buttons...
protected void createButtonsForButtonBar(Composite parent) {
this.createButton(parent, MODIFICAR, "Modificar", true);
this.getButton(MODIFICAR).setEnabled(puedeAltaGrupoBC());
this.bt_ok = this.getButton(MODIFICAR);
this.createButton(parent, CANCELAR, "Cancelar", false);
Display display = window.getShell().getDisplay();
Image image = new Image(display, ModificarGrupoBCDialog.class.getResourceAsStream("/icons/modificar.png"));
this.getButton(MODIFICAR).setImage(image);
image = new Image(display, ModificarGrupoBCDialog.class.getResourceAsStream("/icons/cancelar.png"));
this.getButton(CANCELAR).setImage(image);
}
and when the user clicks...
protected void buttonPressed(int buttonId) {
switch (buttonId) {
case MODIFICAR:
// Some Code, for Change Button
break;
case CANCELAR:
setReturnCode(CANCELAR);
close();
break;
}
Finally, this is how I open and get the returnCode, in the caller object.
...
ModificarGrupoBCDialog modificarGrupoBC = new ModificarGrupoBCDialog(window.getShell(), window, gr_bc);
if (modificarGrupoBC.getReturnCode() == Window.OK) {
//... Some code on OK
} else {
//another code when cancel pressed.
}
;
as you can see, after trying a while, I have to write setReturnCode() in CANCELAR switch block, is that OK ?
I spect that Dialog class automatically asign the correct return code.
May be someone could point me to a good sample.
I'm reading Vogela's blog, and may be the solution is to override okPressed() method ?
Best Regards.
The standard dialog sets the return code in two places:
protected void okPressed() {
setReturnCode(OK);
close();
}
protected void cancelPressed() {
setReturnCode(CANCEL);
close();
}
so your code doing:
setReturnCode(xxxx);
close();
should be fine as long as the button id you are using does not match the Cancel or OK button ids.
You could also use the approach used by MessageDialog which simply does this:
protected void buttonPressed(int buttonId) {
setReturnCode(buttonId);
close();
}

I'm a little confused about how to work with variables correctly in Android

I'm feeling very, very stupid right now... I feel like I must be missing something really obvious.
I've encountered variations of this problem on multiple occasions now, however here is my current example -
When the activity is created, there will be a button marked Start and text set to --:-- next to it. I would like to have it so that when the button is pressed, a timer starts from one minute and displays the seconds remaining in the --:-- text as 00:59 etc. etc., and the text on the button changes to Pause. If the button is pressed when the timer is running, it pauses the timer and changes the text on the button to Start.
So I was using a boolean, timerRunning, in order to keep track of whether the timer was running or not. But if I try to change timerRunning within the onClickListener it gives an error and tells me to change timerRunning to final, and then once I do that it says "The final local variable timerRunning cannot be assigned, since it is defined in an enclosing type."
I'm sorry if this is unclear - I'm just really confused with where I should be declaring variables / how to access them etc. in Android, I don't really understand why I keep getting weird errors all the time.
Thanks.
public class Timing extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timing);
Button bStartJammer = (Button) findViewById(R.id.buttonStartJammer);
CountDownTimer cdtJammerTimer;
long lJammerTotalMS = 60000;
final boolean timerRunning = false;
bStartJammer.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
timerRunning = true;
}
});
}
}
Without source context, it's tough to visualize what you're doing.
How are you defining your click handler? If it's an anonymous class, you'll run into the final issues--is your activity or handler so complex that it makes a separate class completely necessary?
In the previous question my click handler was implemented by the activity, so it has access to that instance's variables. A much-abbreviated skeleton of what I had been doing before not using the CountDownTimer:
public class FooTimer extends Activity implements View.OnClickListener {
private CountDownTimer timer;
private TextView timerDisplay;
private Button pauseButton;
private boolean timerRunning = false;
private boolean timerDone = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
pauseButton = (Button) findViewById(R.id.pause_button);
pauseButton.setOnClickListener(this);
timerDisplay = (TextView) findViewById(R.id.timer_display);
timerDisplay.setText(String.format("%d:%02d:%02d", hours, minutes, seconds));
timer = newTimer();
timerRunning = true;
timer.start();
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.pause_button:
toggleTimer();
break;
}
}
private void toggleTimer() {
if (timerRunning) {
timer.cancel();
pauseButton.setText(getResources().getString(R.string.resume_label));
timerRunning = false;
} else if (timerDone) {
finishActivity(0);
} else {
seconds += 1;
timer = newTimer();
timer.start();
pauseButton.setText(getResources().getString(R.string.pause_label));
timerRunning = true;
}
}
private CountDownTimer newTimer() {
millis = (minutes * 60 * 1000) + ((seconds + 1) * 1000);
return new CountDownTimer(millis, 1000) {
public void onTick(long millisUntilFinished) {
timerDisplay.setText(String.format("%d:%02d:%02d", hours, minutes, seconds));
}
public void onFinish() {
timerDisplay.setText("Finished");
timerRunning = false;
timerDone = true;
pauseButton.setText(getResources().getString(R.string.back_label));
}
};
}
}
(I took a bunch of stuff out, and added some really early code back in, so it's a bit of a mish-mosh, but the ideas are there.)
Your new OnClickListener(){..} is actually an anonymous class: http://mindprod.com/jgloss/anonymousclasses.html
Anonymous classes have access to class (static) and instance fields of enclosing class. So a solution is to have timerRunning defined as a field, i.e. define it outside of onCreate() method.
its not starting the code anywhere, your just setting it to true that its a timer. watch the tutorials here and they should really help you out. cornboyzandroid
Some of his earlier videos really describe how to do a timer pretty clearly and step by step. And he helps with global and local variables. around episode 6 or 7 just check his page out.

Categories

Resources