How can I pass the context and the name string as arguments to the new thread?
Errors in compilation:
Line
label = new TextView(this);
The constructor TextView(new Runnable(){}) is undefined
Line "label.setText(name);" :
Cannot refer to a non-final variable name inside an inner class defined in a different method
Code:
public void addObjectLabel (String name) {
mLayout.post(new Runnable() {
public void run() {
TextView label;
label = new TextView(this);
label.setText(name);
label.setWidth(label.getWidth()+100);
label.setTextSize(20);
label.setGravity(Gravity.BOTTOM);
label.setBackgroundColor(Color.BLACK);
panel.addView(label);
}
});
}
You need to declare name as final, otherwise you can't use it in an inner anonymous class.
Additionally, you need to declare which this you want to use; as it stands, you're using the Runnable object's this reference. What you need is something like this:
public class YourClassName extends Activity { // The name of your class would obviously be here; and I assume it's an Activity
public void addObjectLabel(final String name) { // This is where we declare "name" to be final
mLayout.post(new Runnable() {
public void run() {
TextView label;
label = new TextView(YourClassName.this); // This is the name of your class above
label.setText(name);
label.setWidth(label.getWidth()+100);
label.setTextSize(20);
label.setGravity(Gravity.BOTTOM);
label.setBackgroundColor(Color.BLACK);
panel.addView(label);
}
});
}
}
However, I'm not sure this is the best way to update the UI (you should probably be using runOnUiThread and AsyncTask). But the above should fix the errors you've encountered.
Related
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.
For example, I try to do something in the separate thread:
public void shareQuote(final Context context, final ArrayList<Quote> quotes, final int number) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
Toast warningWindow = Toast.makeText(context, context.getString(R.string.shareWarning), Toast.LENGTH_SHORT);
} else {
new Thread(new Runnable() {
#Override
public void run() {
// Creates new intent for sharing
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType(SHARE_TYPE_TEXT);
String subject = context.getString(R.string.quotes_author);
String sharingQuote = "\"" + quotes.get(number).getText() + "\"" + "\n";
}
}).start();
}
Why do I have to send the final objects to the arguments list if I want to do something in the new thread?
Because anonymous classes only have access to final arguments (and final local variables), by design.
Java has strict scope rules. Your thread class is an anonymous class which means it will lose the outer scope after the method is finished. This means that anything that was in the methods stack frame will be removed.
When you add the key word final this prevents the field in your method from changing and allows that field to be added to the stack frame of inner class and the stack frame of your method. If the field wasn't final this couldn't be done as you could change where it points in your method and the inner class would not be aware of this change.
How to pass outer anon class ref to a method in an inner anon class in Java?
I have a method that makes async call to a server - sendCall(some_args, callback). The callback is represented by anonymous class (let's name it OuterAnon) and contains a method for failure case. Inside this method a message box is created and sendCall() is called each time OK button is pressed. So I need to pass OuterAnon to the method again.
Here is a code to demonstrate what I mean:
private void sendCall(MyData data, OuterAnon<Boolean> callback){/*...*/}
private void myCall(final MyData data) {
sendCall(data, new OuterAnon<Boolean>() {
public void onFailure(Throwable throwable) {
final OuterAnon<Boolean> callback = this; //how to avoid this?
MessageBox.show(throwable.getMessage(), new MessageListener() {
public void process(MessageBox.OnClick action) {
if (action == MessageBox.OnClick.OK) {
sendCall(new MyData("resend?"), callback);
}
}
});
}
}
});
}
As you noticed, I take a ref for callback here:
final OuterAnon<Boolean> callback = this;
and use it here:
sendCall(new MyData("resend?"), callback);
But I want to avoid ref creation and pass callback like:
sendCall(new MyData("resend?"), this); //at the moment we point to MessageListener instead of OuterAnon.
Is there any way to do it in Java?
It's hard for us to fix since you've only shown incomplete code with classes that aren't supplied, so I don't know if this example is syntactically correct. That being said, a refactoring like this may suit your needs:
private void myCall(final MyData data)
{
sendCall(data, new OuterAnon<Boolean>()
{
public void onFailure(Throwable throwable)
{
showErrorMessage(throwable);
}
});
}
private void showErrorMessage(Throwable throwable)
{
MessageBox.show(throwable.getMessage(), new MessageListener()
{
public void process(MessageBox.OnClick action)
{
if (action == MessageBox.OnClick.OK)
{
sendCall(new MyData("resend?"));
}
}
});
}
private void sendCall(MyData data)
{
sendCall(data, this);
}
In general, I think it's a usually good idea to abstract code out of anon inner classes and into their own method on the enclosing class. It's now testable, reusable, and more readable, IMO.
If you really need to specify the onFailure inside the inner class the way you showed the code, and if you need to use that specific reference for callback, and you need to code this way...
Let's answer the question: no.
In my attempts, I've achieved 3 ways to access the anon-inner-least instance inside the anon-inner-most instance, but I think that none satisfies what you expect.
In that case, the anon-inner-most doesn't have a reference to the anon-inner-least: as you said, the this now points to the anon-inner-least.
Also, I tried to search at the java specification, but couldn't find exactly the answer to the question - if someone find the answer there, please contribute.
My try:
import java.util.ArrayList;
import java.util.LinkedList;
public abstract class AnonTest {
public static void main(String[] args) {
new ArrayList<Object>() {
private static final long serialVersionUID = -5986194903357006553L;
{
// initialize inner anon class
add("1");
}
// Way 1
private Object thisReference1 = this;
// Way 2
private Object getThisReference2() {
return this;
}
#Override
public boolean equals(Object obj) {
// Way 3
final Object thisReference3 = this;
new LinkedList<Object>() {
private static final long serialVersionUID = 900418265794508265L;
{
// initialize inner inner anon class
add("2");
}
#Override
public boolean equals(Object innerObj) {
// achieving the instance
System.out.println(thisReference1);
System.out.println(getThisReference2());
System.out.println(thisReference3);
System.out.println(this);
System.out.println();
// achieving the class
System.out.println(thisReference1.getClass());
System.out.println(getThisReference2().getClass());
System.out.println(thisReference3.getClass());
System.out.println(this.getClass());
System.out.println(this.getClass().getEnclosingClass());
return super.equals(innerObj);
}
}.equals("");
return super.equals(obj);
}
}.equals("");
}
}
I have code like this:
TextBox txt = new TextBox(){
public void onLoad(){
this.addFocusHandler(new FocusHandler(){
//some codes here
//if I use "this" keyword, it refers to the handler, but how can I get a reference to the textbox?
});
}
};
Question is embedded in the position.
Edit:
In respect to the answers, the creation of a pre-defined reference works for this situation, but this apparently lost (or at least reduce) the benefits of anonymous object/function.
I hope to find a way without creating a new reference. Rather just to get the reference from that scope.
After all the answers, here is a conclusion:
Reflection does not work in GWT. (at least I did not succeed) obj.getClass() works, but others like getMethods() or getEnclosingClass() don't work.
The way to get a reference can either be declaring a reference in the right scope, or get a higher level object reference and reference downwards. I prefer the latter simply because you don't need to create a new variable.
TextBox txt = new TextBox(){
public void onLoad(){
final TextBox finalThis = this;
this.addFocusHandler(new FocusHandler(){
finalThis.doSomething();
);
}
};
The enclosing instance of a non-static inner class (anonymous or named) in Java is available as ClassName.this, i.e.
TextBox txt = new TextBox(){
public void onLoad(){
this.addFocusHandler(new FocusHandler(){
doSomethingCleverWith(TextBox.this);
});
}
};
This has worked for me in the past. It works in client side js too. Here is a reference to more detail
What is the difference between Class.this and this in Java
public class FOO {
TextBox txt = new TextBox(){
public void onLoad(){
this.addFocusHandler(new FocusHandler(){
#Override
public void onFocus(FocusEvent event) {
FOO.this.txt.setHeight("100px");
}
});
}
};
}
This may work for you:
TextBox txt = new TextBox(){
public void onLoad(){
final TextBox ref = this;
this.addFocusHandler(new FocusHandler(){
public void doSomething(){
//some codes
ref.execute();
}
});
}
};
But I prefer to migrate inner classes to named classes:
public class Test {
public void demo(){
TextBox txt = new TextBox(){
public void onLoad(){
this.addFocusHandler(new DemoFocusHandler(this));
}
};
}
}
External FocusHandler:
public class DemoFocusHandler extends FocusHandler {
private TextBox textBox;
public DemoFocusHandler(TextBox textBox){
this.textBox = textBox;
}
public void doSomething(){
//some codes
textBox.execute();
}
}
If gwt supported reflection you could do something along the lines of this:
final TextBox txt = new TextBox() {
public void onLoad() {
final Object finalThis = this;
this.addFocusHandler(new FocusHandler() {
#Override
public void onFocus(FocusEvent event) {
try {
Method method= finalThis.getClass().getMethod("getVisibleLength");
method.invoke(finalThis);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
Without reflection the existing answers are you best bet. There are two gwt reflection projects gwt reflection and gwt-preprocessor both are in beta and I have not tried them.
The dialogue and the array displays just fine, I just want to be able to set the static variable from the originating class within the onClick that is in a method that is in a different class. All of the try, catch and
<?> were things that I put in at the insistence of the compiler:
public class Setter
{
public void myList(Context context, Class<?> thisclass, int arrayid, String choice)
{
return new AlertDialog.Builder(context)
.setItems(arrayid, new OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
setChoice(thisclass, context, arrayid, which, choice);
}
})
.create();
}
public void setChoice(Class<?> thisclass, Context context, int arrayid, int which, String choice)
{
String[] array = context.getResources().getStringArray(arrayid);
try
{
Field f = thisclass.getDeclaredField(choice);
f.set(null, array[which]);
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (NoSuchFieldException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
}
public class ClassA extends Activity
{
static String stringa;
Setter setted = new Setter();
...
public void onCreate()
{
super.onCreate();
...
AlertDialog thinga = setted.myList(this, getclass(), R.array.thinga, stringa).show();
...
}
}
When I select an item from the list, I get this from debugger:
ClassCache.findFieldByName(Field[], String) line: 438
Class.getDeclaredField(String) line: 666
Setter.setChoice(Class, Context, int, int, String) line: 45 // the line with the Field
I think I'm passing it the class wrong but this is a bit out of my current depth.
I have a number of different classes each with their own static Strings. I am passing the method below the name of the String (in choice) and the context of what I had hoped was the original class that called a method that called a method that led to the code below. I was hoping I could call context.choice = something and the machine would read that as ClassA.stringa = something; how do I do that?
Briefly, I want to have a list of items that the user can choose from be the content of a dialogue, and have their selection be saved and accessible to the class that called for the creation of the dialogue. Perhaps I'm going about this all wrong but I got tired of dealing with other 'kludges' involving using spinners to do the same thing.
Because onClick can't have non-final objects declared elsewhere (at least that is my understanding) I thought maybe I could get around that by calling to another method, setChoice that would store the value of whatever was chosen. I would definitively say this is a kludge and would love to be shown the light as to how you are supposed to deal with these things.
Java does not have closures, but you can get close with anonymous inner classes.
String output;
public void onCreate() {
Setter.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
output = "selected";
}
});
}
See also this swing tutorial: http://download.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
Edit:
In spirit of your example, this should look like this:
public class Setter
{
public void setChoice(IsetString setter, String something)
{
setter.setString(something);
}
}
class ClassA extends Activity implements setString
{
static String stringa;
string polka = "dots";
Setter setted = new Setter();
...
public void onCreate()
{
super.onCreate();
...
setted.setChoice(new IsetString() {
#Override
public void setString(String s) {
stringa = s;
}
}, polka);
...
}
}
interface IsetString {
void setString(String s);
}
The short answer - use the Reflection API.
The long answer - you'll need to obtain access to the Fields of the desired Context Class. Once you gain access to the Field instances, you can set their values using the set() method; the API call is a bit tricky in that you'll need to pass in the object reference (the context object and not the context class) whose field you wish to modify.
It is necessary that your Context, choice and something parameters to the method, contain the necessary information to make this operation as simple as possible. In other words, the Context class might have to contain the actual Class object (or provides a way to get one) that contains the field.
You can use reflection for that. Suppose you context is class itself
public void setChoice(Class<?> context, String choice, String something)
{
try {
Field f = context.getDeclaredField(choice);
f.set(null, something);
} catch (....) {
}
}
Add proper exception handling
Note that first argument to set is null. That is only valid for static methods. So you may want to check that method is static using f.getModifiers().