Modify variables in an instance from another class Android - java

I simplified this for brevity; hopefully this example isn't actually functional. I'm creating and doing things with a variable, then I'm having another class do some stuff, then that class refers back to the original and tells it to do more stuff with that variable.
I've done exactly this with views. I simply pass the activity and then when I need to use it I use activity.findViewById(id) to do stuff. With variables, you can't just do activity.variable. I tried using a getter (as shown in this example), but maybe I'm still just doing it wrong or it can't be done how I'd like:
public class MyActivity {
private int test;
public void onCreate(Bundle savedInstanceState) {
test = 5;
int data = 100;
//Pass something to it
new NotAnActivity().func(MyActivity.this,data);
}
public int gettest() {
return test;
}
public void func(Activity instance, int response) {
int test = new MyActivity().gettest();
//Do stuff with test
}
}
public class NotAnActivity {
public void func(Activity instance, int data) {
//Do stuff with data
int response = 20;
//Try to pass information back
new MyActivity().func(instance,response);
}
}

You can't use a activity.gettest() because you're passing the superclass Activity between classes. To have access to the gettest() method you need to pass the specific child activity (MyActivity extends Activity, pass MyActivity instead of Activity) or you can cast to your specific activity.
((MyActivity)activity).getter();
So here, instead of:
public void func(Activity instance, int data) {
//Do stuff with data
int test = ((MyActivity)instance).gettest();
}
or
public void func(MyActivity instance, int data) {
//Do stuff with data
int test = instance.gettest();
}
It's not a good idea to instantiate your activities yourself new A()

public class MainActivity extends AppCompatActivity {
private int test;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test = 5;
int data = 100;
new NotAnActivity().func(this,data);
}
public int gettest() {
return test;
}
public void func(MainActivity instance, int response) {
//int test = new MainActivity().gettest();
int test = instance.gettest();
Log.v("variable", "test = " + test);
}
}
class NotAnActivity {
public void func(MainActivity instance, int data) {
//Do stuff with data
int response = 20;
//Try to pass information back
instance.func(instance,response);
}
}
You can try it. Your mistake is [new MyActivity()]

Related

How to add to array in main?

I have created an array which I wanted to control from main. My code runs, but I don't know how to add integers to the array from the main class. Also as each ConcreteSubject has its own storage array, how would i change this to store them all in the same array?
public class ConcreteSubject extends AbstractSpy
{
private AbstractSpy[] spies = new AbstractSpy[10];
private int i = 0;
public void addSpy(AbstractSpy s) {
if (i < spies.length) {
spies[i] = s;
System.out.println("spy added at index " + i);
i++;
}
}
}
public class TestClass
{
public static void main(String[] args) {
ConcreteSubject cs = new ConcreteSubject();
AbstractSpy spies = new AbstractSpy() {
#Override
public void addSpy(AbstractSpy spies) {
}
};
cs.addSpy(cs);
spies.addSpy(spies);
}
}
It seems like your program logic is a little borked. This bit in particular doesn't make much sense:
***AbstractSpy spies = new AbstractSpy() {
#Override
public void addSpy(AbstractSpy spies) {
}
};
cs.addSpy(cs);
***spies.addSpy(spies);
What you're doing is creating TWO AbstractSpy instances, one named cs and one named spies. On that last line you're adding spies to itself! That doesn't help you at all.
Note that AbstractSpy is the most granular unit in your setup - it shouldn't have an addSpy() method and its own internal array, it should be the thing that's added to something else's array!
Here's the same code, but cleaned up a bit:
public abstract class AbstractSpy { }
public class ConcreteSpy extends AbstractSpy { }
public class ConcreteSubject {
private AbstractSpy[] spies = new AbstractSpy[10];
private int i = 0;
public void addSpy(AbstractSpy spy) {
if (i < spies.length)
{
spies[i] = spy;
System.out.println("spy added at index " + i);
i++;
}
}
}
public class TestClass {
public static void main(String[] args) {
ConcreteSubject cs = new ConcreteSubject();
AbstractSpy spy = new ConcreteSpy();
cs.addSpy(spy);
}
}
The big difference here is that ConcreteSpy is an implementation of AbstractSpy that you can add to your ConcreteSubject's array of spies. I think you might have been confused by Java's insistence that you can't create an instance of an abstract class on its own unless you supply an anonymous class that inherits from the abstract class.

Detecting when a value changed in a specific variable in Android

I have an int variable called mCurrentIndex. I want to do something when its value changes.
For example:
public class MainActivity extends Activity{
private int mCurrentIndex = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Apps logic.
}
public onCurrentIndexValueChange(){
button.setClickable(true);
}
}
One approach to solving this is using Android's LiveData.
In your situation, since you'd like to observe an int, you can do something like this:
public MutableLiveData<Integer> mCurrentIndex = new MutableLiveData<>();
To change your value, you would do this:
mCurrentIndex.setValue(12345); // Replace 12345 with your int value.
To observe the changes you would do the following:
mCurrentIndex.observe(this, new Observer<Integer>() {
#Override
public void onChanged(#Nullable final Integer newIntValue) {
// Update the UI, in this case, a TextView.
mNameTextView.setText("My new current index is: " + newIntValue);
}
};);
}
This approach is useful because you can define a ViewModel to segregate your logic from your Activity while having the UI observe and reflect the changes that occur.
By separating your logic out to the ViewModel, your logic becomes more easily testable since writing tests for your ViewModel is relatively easier than writing tests for your Activity.
For more information on LiveData check out this link.
you can use a setter function
private int mCurrentIndex = 1;
public void setCurrentIndex(int newValueFormCurrentIndex)
{
if(newValueFormCurrentIndex != mCurrentIndex)
{
onCurrentIndexValueChange();
mCurrentIndex = newValueFormCurrentIndex;
}
}
If the scope on mCurrentIndex is only in MainActivity then I think it would be best just to create getter and setter methods for mCurrentIndex and at the end of the setter method call onCurrentIndexValueChange()
public int getIndex(){
return this.mCurrentIndex;
}
public void setIndex(int value){
this.mCurrentIndex = value;
this.onCurrentIndexValueChange();
}

How to convert type Class<?> to int

I am trying to convert this data type to call out the method later on in another class to switch around layouts being made in other methods such as recipe1Layout(); by the index number of a class that has a field of a Class<?> Array.
Here is the getItem() method
public int getItem(){
int index = 0;
for(int i = 0; i < 100; i++) {
try{
index = recipe.getClass().getField("Classes").get(i);
} catch(Exception e){
}
}
return index;
}
Here is the Recipe Class
public class Recipes {
public Class<?>[] Classes = {
ChileConLecheActivity.class,
ArrozActivity.class,
EnchiladasActivity.class,
SopaActivity.class
};
}
The type of Class needs to be here because I have other uses for the recipe class.
For example, making a new instance of all classes to later on be called out to make adjustments to all the classes with one method.
The only thing I can think of is converting the type Class to an int so I can call out the method returning the index number I can do something like recipe.
index = Integer.parseInt(Classes[I].getName().toString());
But this is where I am asking for help I have no idea how to get rid of the error in the logcat.
The error shows up as
IndexOutOfArrayException
First off, stop using reflection. Use a public static array.
public class Recipes {
public static final Class<?>[] CLASSES = {
ChileConLecheActivity.class,
ArrozActivity.class,
EnchiladasActivity.class,
SopaActivity.class
};
}
Then, assuming your recipe instance has a field of what Class<Activity> it is assigned to, then, you would want something like this
public int getItem(){
int index = -1;
for(int i = 0 ; i < Recipe.CLASSES.length; i++) {
if (recipe.getActivityClass().equals(Recipe.CLASSES[i]) {
index = i;
break;
}
}
return index;
}
However, under certain situations, coupling one Activity class to any single Recipe instance, probably isn't a good idea.
I am trying to convert this data type to call out the method later on in another class to switch around layouts being made in other methods
if I understand what you are trying to do, you want a some mapping structure to some classes which have some pre-defined layouts.
Generally, this can be done with enums and OOP patterns
Have some base classes like this
public interface Layoutable {
int getLayout();
}
public enum Recipe {
ChileConLeche(R.layout.chile_con_leche),
Arroz(R.layout.arroz),
Enchiladas(R.layout.enchiladas),
Sopa(R.layout.sopa)
int layout;
Recipe(int layout) { this.layout = layout };
}
Ideally, you would want to use Fragments, but here is an example of an Activity structure
public abstract class RecipeActvity extends AppCompatActivity implements Layoutable {
protected Recipe recipe;
protected int getLayout() { return recipe.layout; }
}
public class ChileConLecheActivity extends RecipeActvity {
public ChileConLecheActivity() {
this.recipe = Recipe.ChileConLeche;
}
#Override
public void onCreate(...) {
setContentView(getLayout());
}
}
You can also combine this with a Map<Recipe, Class<RecipeActivity>>, from which you would use map.get(Recipe.ChileConCarne) to get the respective class element, for which you can startActivity() with

Java: How to do TextView.setText in another package?

I'm new to Java, I'm trying to build something in Android Studio.
The point is to 'push' a value for TextView baseTickVar in class BaseScreen, from another PACKAGE, where the class CoreFunctionality resides. Each time the tickNumber increases I want that shown in the TextView, so it's not a one time setting of text.
I have tried interfaces, but interfacing won't allow variables, only constants.
I've tried TextView.setText from the CoreFunctionality package, but that gave a nullpointerException and declaring the TextView to counter that didn't seem to help.
public class BaseScreen extends Activity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_screen);
// some irrelevant code here so i left it out.
TextView baseTickVar = (TextView)findViewById(R.id.baseTickVar);
baseTickVar.setText("1"); // just to not have it empty...
}
Now I want to set value of baseTickVar with a variable from the other package CoreFunctionality
public class CoreFunctionality extends Activity implements Runnable {
Thread tickThread = null;
volatile boolean playingGalactic;
long lastTick;
public int tickNumber;
int tickLength;
TextView baseTickVar;
public void controlTicks() {
tickLength = 2000;
long timeThisTick = (System.currentTimeMillis() - lastTick);
long timeToWait = tickLength - timeThisTick;
if (timeToWait > 0) {
try {
tickThread.sleep(timeToWait);
} catch (InterruptedException e) {
}
}
lastTick = System.currentTimeMillis();
}
#Override
public void run() {
while (playingGalactic) {
controlTicks();
tickNumber++;
Log.i("Tick number ", "" + tickNumber);
updateTick();
}
}
private void updateTick() {
// this is the whole point...
baseTickVar.setText("" + tickNumber);
}
public void resume() {
playingGalactic = true;
tickThread = new Thread(this);
tickThread.start();
}
I guess your BaseScreen is the main screen and CoreFunctionality is some component that is doing some work. Actually CoreFunctionality does not need to be Activity, it better fits to be a service.
You have to somehow pass reference to baseTickVar to the CoreFunctionality.
It is not allowed to mess with UI elements (such as TextView) from within another thread. You should consider using some inter-thread communication (such as Message).
Make BaseScreen to extend Handler or make a Handler object in it then override
public void handleMessage(Message msg) {
baseTickVar.setText("" + msg.obj);
}
In CoreFunctionality
private void updateTick() {
Message msg=new Message();
msg.obj=tickNumber;
h.sendMessage(msg);
}
Of course you'll have to pass the h reference to CoreFunctionality.
Maybe not 100% accurate but it should work with little tweaking.
Hope this will help.

Should I use an anonymous inner class to simulate 'out' parameters in Java?

I'm still a relative newbie when it comes to Java, coming mainly from a C# background.
I was discussing the lack of 'out' parameters in Java methods with a colleague and how to work around this. He suggested creating a structure/class to hold the various parameters and passing it back.
Sometimes this feels 'wrong' to me - especially if I have a special method that I want to use to return a subset of parameters from a larger class.
So I wondered about using anonymous inline classes instead to achieve this. Code sample below.
Is this a sensible approach? Just wondering what the perceived wisdom is on this.
public class MyClass {
Patient myPatient = null;
// An interface to enable us to return these variables in a single call
public interface VitalStatsResponse { public void returnStats(int bloodPressure, int heartRate); }
public class Patient {
int bloodPressure = 100;
int heartRate = 280;
// Lots of other variables here
public void calculateVitalStats(VitalStatsResponse response)
{
response.returnStats((bloodPressure * 2), (heartRate / 10) ;
}
}
public void doWork()
{
// We want the patient's blood pressure and heart rate returned by a single method call, so use an anonymous inline class
myPatient.calculateVitalStats(new VitalStatsResponse() {
#Override
public void returnStats(int bloodPressure, int heartRate) {
// Handle returned variables here
}
});
}
}
I would go for the simple solution of creating a VitalStats object. If you need the VitalStatus of a patient, then VitalStats is a concept in your application that can be represented as an Object.
public class VitalStatus {
final int bloodPressure;
final int heartRate;
public VitalStats(int bloodPressure, int heartRate) {
this.bloodPressure = bloodPressure;
this.heartRate = heartRate;
}
}
public class Patient {
int bloodPressure = 100;
int heartRate = 280;
// Other variables
public VitalStatus getVitalStatus() {
return new VitalStats(bloodPressured * 2, heartRate / 2);
}
}
Out params is a procedural solution for return times. Java primarily fits the Object Oriented paradigm of programming and as such don't be afraid to make objects. This fits with the S in SOLID if your class is doing a lot of complex things see if you can break it down into smaller more manageable pieces.
I would also use "class to hold the parameters" over "inline anonymous inner class"
public class MyClass implements VitalStatsResponse{
Patient myPatient = null;
private ArrayList<VitalStatsResponse> response;
void MyClass(ArrayList<VitalStatsResponse> response) {
this.response = response;
}
public class Patient {
int bloodPressure = 100;
int heartRate = 280;
// Lots of other variables here
public void calculateVitalStats()
{
for(int i = 0; i < response.length; i++) {
// call returnStats method of every registered callback
response.get(i).returnStats((bloodPressure * 2), (heartRate / 10) ;
}
}
}
// any client can register/unregister callback via these methods
void registerResponse(VitalStatsResponse response) {
this.response.add(response);
}
void unRegisterResponse(VitalStatsResponse response) {
this.response.remove(response);
}
public void doWork()
{
// We want the patient's blood pressure and heart rate returned by a single method call, so use an anonymous inline class
myPatient.calculateVitalStats();
}
public void returnStats(int bloodPressure, int heartRate) {
// implement the body according to this class requirement
}
}

Categories