I'm having an inner class within another inner class, in which I'm trying to use final variable outside both inner classes. Here's the code:
final View v = inflater.inflate(R.layout.fragment_floor_plan, container, false); //final variable
final Button button0 = v.findViewById(R.id.button21);
button0.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final PhotoView photoView = v.findViewById(R.id.photo_view); //works fine here
photoView.setAlpha(0f);
System.out.println(photoView.isZoomable());
System.out.println(photoView.VISIBLE);
photoView1.animate().alpha(0f).setDuration(250);
photoView.animate().alpha(1f).setDuration(250);
photoView.bringToFront();
photoView.setOnScaleChangeListener(new OnScaleChangedListener() {
#Override
public void onScaleChange(float scaleFactor, float focusX, float focusY) {
if (photoView.getScale() <= photoView.getMinimumScale() + 0.1f) {
LinearLayout linearLayout = v.findViewById(R.id.linearLayout); //doesn't work here
linearLayout.bringToFront();
}
}
}
}
});
How to get it to work inside OnScaleChangedListener?
Now that I look at it, your problem might be that the View v is being passed as a parameter to the onClick method of the OnClickListener, and the code below might be trying to access instead of your top-level View variable.
My recommendation would be to refactor your variable names so that you are referring to the correct variable. If the passed-in View is the one you actually want to use, it should be declared as final in the method signature:
public void onClick(final View v) {
If the variable in the enclosing scope of the nested anonymous class is declared final or effectively final, you should just be able to access it from within any level of nested inner anonymous classes.
To ensure this I made a little example and tried it out myself:
import java.util.function.*;
public class Main {
public static void main(String[] args) {
final int test = 11;
Runnable runnable = new Runnable() {
public void run() {
Runnable runnable = new Runnable() {
public void run() {
System.out.println(test);
}
};
runnable.run();
}
};
runnable.run();
}
}
As you can see the second runnable is nested within another runnable, and running the code accesses the integer test and prints it as it should.
Related
I have a value inside my app which constantly increases, the handler which does that is in a static method inside my "EventHandlerClass.java".
I now want to show this value on my TextView inside my MainActivity.
Here is my static method with the handler:
public static void pointsCounter() {
handler = new Handler(Looper.getMainLooper());
runnable = new Runnable() {
public void run() {
points = points + 5;
String pointMsg = "Points: " + points;
MainActivity.coinsTextView.setText(pointMsg);
handler.postDelayed(this, 1000);
}
};
handler.postDelayed(runnable, 1000);
}
This pointsCounter Method gets called from another static method inside the EventHandlerClass.java.
It increases the points value +5 every second and I want it to get displayed in a TextView.
What is the right way to do it?
Because when I try it this way I have to make the coinsTextView in the MainActivity static, we all know you cannot reference non static variable from static context.
When I do so it tells me "Do not place Android context classes in static fields; this is a memory leak".
So my question is how can I update my UI element from the static method without risking a memory leak?
What is the right way to do it?
as you said Never place static View or Context in your application since it will cause unexcepted memory leaks, however if you still want to use static TextView in your application you can wrap the TextView in a WeakReference :
WeakReference: a weak reference is a reference not strong enough to keep the object in memory. If we try to determine if the object is strongly referenced and it happened to be through WeakReferences, the object will be garbage-collected.
this an example how to use it :
public class MainActivity extends AppCompatActivity {
private static WeakReference<TextView> viewWeakReference;
private static Handler handler;
private static int points;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textV);
viewWeakReference = new WeakReference<>(textView);
pointsCounter();
}
public static void pointsCounter() {
handler = new Handler(Looper.getMainLooper());
Runnable runnable = new Runnable() {
public void run() {
points = points + 5;
String pointMsg = "Points: " + points;
viewWeakReference.get().setText(pointMsg);
handler.postDelayed(this, 1000);
}
};
handler.postDelayed(runnable, 1000);
}
}
I hate The application may be doing too much work on its main thread, skipped XXX frames.. warning, Also it degrades the users UI interaction experience. So trying it with proper way as android wants it to be...
MainActivity :
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener
{
public Button StartBg;
private static final String TAG = "TASK_FIRST";
private Handler mainHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StartBg = findViewById(R.id.StartBg);
StartBg.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
ExampleRunner ExampleRunnerObj = new ExampleRunner(50000);
new Thread(ExampleRunnerObj).start();
}
});
}
}
When i keeps below class as inner class of MainActivity, It is able to access UI Components.
ExampleRunner :
public class ExampleRunner implements Runnable
{
int count;
public ExampleRunner(int count)
{
this.count = count;
}
#Override
public void run()
{
Handler threadHandler = new Handler(Looper.getMainLooper());
for(int i = 0; i < count; i++)
{
Log.d(TAG,"PERFORMING : "+i+"\n");
if(i == 25000)
{
threadHandler.post(new Runnable()
{
#Override
public void run()
{
// StartBg.setText("50k");
// OR RETURN SOMETHING..
}
});
}
}
}
}
But when i makes ExampleRunner as separate external class, it says StartBg can not be resolved...
So, How should I :
Make external java class which implements Runnable, Access Main Threads UI components...?
Or At least return something to mainActivity where i am starting it, so that from mainActivity i can access it?
Thanks in advance.
You need to pass the external class the Button as a reference:
public class ExampleRunner implements Runnable
{
int count;
Button startBg;
public ExampleRunner(int count, Button startBg) {
this.count = count;
this.startBg = startBg;
}
and create it with it:
ExampleRunner ExampleRunnerObj = new ExampleRunner(50000, StartBg);
then it will be able to use it in run().
Right now, the ExampleRunner is an anonymous class accessing the StargBg variable declared locally.
If, I right understood you, for your solution, AsyncTask it is good solution.
Check docs https://developer.android.com/reference/android/os/AsyncTask
Iam creating an android app that has many classes inside the main Java package. The MainActivity class implements Button onClick Listener and do some coding with assigning values to variable x inside the method when button is clicked, now I have class#2 use the same variable x in some other coding. I want the onClick method when it is called to send the variable x value to class#2
MainActivityCalss {
hi.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
int x = 1;
}
});
}
Class2 {
Method() {
y = x + 1;
}
}
Creating a new Java class to hold all global variables is a very good idea.
public class GlovalVariable{
public String x;
public int y;
// Generate getter/setter methods for all the variables defined here.
}
By creating this you will manage a variables very easily. If you want to rename the variable which is used through out the class, this method will make it very easy.
Define "x" in other class and set it using setter method, and where ever you get it through getter.
public class value {
public static int x;
public static void set(int value) {
x = value;
}
public static int get() {
return x;
}
}
And in onclicklistener of mainactivity.
hi.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
value.set(1);
}
});
In class2
Method() {
y = value.get()+1;
}
i'm trying to generate a set of buttons whith data from the database. But on click i'm facing the following eror
Variable 'i' is accessed from within the inner class, needs to be declared final,
Since the value of i is changes as loop goes on i cannot set it as final,
footnoteBtns[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
footnote = myDbHelper.getFootnote(chapterNumber, translationList.get(i).get("transNo"));
Popup();
}
});
You could add an additional variable that is final and set to i:
final int j = i;
And then use that one inside the overridden onClick method.
The reason why you have to do this, is that onClick is called at another point of time and not directly inside the for loop -> asynchronous. Therefore, you need to make sure that it is clear which value should be used in that later called method. That's why the variable needs to be final.
In general it very weird approach to put setOnClickListener in a loop, but in your case you can solve it with following code:
for( int i = 0; i < N; i++) {
final int p = i;
footnoteBtns[p].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
footnote = myDbHelper.getFootnote(chapterNumber, translationList.get(p).get("transNo"));
popup();
}
});
}
Try this in place of current code:
class MyOnClickListener extends View.OnClickListener {
private int myi;
public MyOnClickListener(int i) {
myi = i;
}
#Override
public void onClick(View v) {
footnote = myDbHelper.getFootnote(chapterNumber, translationList.get(myi).get("transNo"));
Popup();
}
};
footnoteBtns[i].setOnClickListener(new MyOnClickListener(i));
How can I get the value of a method parameter "myInteger" in this code.
public void myMethod(int myInteger) {
View.OnClickListener myClearHandler = new View.OnClickListener() {
public void onClick(View v) {
//***How can I get the value of "myInteger" here?***
}
};
}
Assuming you're just trying to read it, you just need to make it final:
public void myMethod(final int myInteger) {
View.OnClickListener myClearHandler = new View.OnClickListener() {
public void onClick(View v) {
int foo = myInteger;
}
};
}
In Java, only final parameters and local variables can be accessed within anonymous inner classes.
I am guessing the language should support closures and all you need to do in this case is use the variable myInteger in your onClick listener and you should be fine.. This works in fine in many languages I am not sure about Java though.
public void myMethod(final int myInteger) {
View.OnClickListener myClearHandler = new View.OnClickListener() {
public void onClick(View v) {
int myInteger = myInteger * 100;
}
};
}
AS posted by John Skeet: the final keyword is important here.
You Cannot refer to a non-final variable myInteger inside an inner class
defined in a different method
You might be getting this error, so for that you have to declare it as final like this
final int myInteger