Cannot access a global final variable from within an inner class - android

I am using an animation to slide a view to the top of the screen. The code for the animation is contained within a method called LoopAnimation() which is called from main.
public class MainActivity extends AppCompatActivity {
final View view = findViewById(R.id.view);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoopAnimation(view); \\ The animation loop method
}
This LoopAnimation() method uses a nested setOnClickListener to create an animation loop
public void LoopAnimation(View view){
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// starts the animation
view.animate().translationY(-100);
view.animate().setDuration(1500);
// reverses the animation
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do some job here
view.animate().translationY(100);
view.animate().setDuration(1500);
LoopAnimation(view); // Method calls itself
// to create loop effect
}
});
}
});
}
The problem is that I am getting a trivial error that I can't understand. Although I have declared view as global and final, I get this error in LoopAnimation()
Variable 'view' is accessed from within inner class, needs to be declared final.

You are using the variable view that is defined within your method's scope. Notice that your method's parameter is also called view so you are not actually using the global variable that you think you are using.
public void LoopAnimation(View view){
Edit: I've looked more into the way that you are trying to do this, and the approach isn't what I would do. Here is something more reasonable:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View view = findViewById(R.id.view);
view.setOnClickListener(new View.OnClickListener() {
private boolean _forwards = true;
#Override
public void onClick(View v) {
if (_forwards) {
// starts the animation
v.animate().translationY(-100);
v.animate().setDuration(1500);
_forwards = false;
} else {
// reverses the animation
v.animate().translationY(100);
v.animate().setDuration(1500);
_forwards = true;
}
}
}
}
}

Related

Custom Listener for Compound component

I wrote a compound component and was adding a custom listener to react.
Inside the class for the compound component which uses an xml file.
public class VerticalCounterBlock extends LinearLayout {
public interface VerticalCounterBlockListener {
public void onCountChanged(int newCount);
}
private VerticalCounterBlockListener mVerticalCounterBlockListener = null;
public void setVerticalCounterBlockListener(VerticalCounterBlockListener listener){
mVerticalCounterBlockListener = listener;
}
// ... Other functions
}
I got my interface, I got the listener and I got the setter and I engage the listener like this in the button I have in the compound component. I can see that toast that is showing there when I test
addBtn = (Button)findViewById(R.id.btn_addcount);
addBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
count++;
counttv.setText(String.format("%1$d", count));
Toast.makeText(getContext(), "VCB", Toast.LENGTH_SHORT).show();
if(mVerticalCounterBlockListener != null) {
mVerticalCounterBlockListener.onCountChanged(count);
}
}
});
In my main activity
m20_vcb = (VerticalCounterBlock) findViewById(R.id.vcb_m20);
m20_vcb.setVerticalCounterBlockListener(new VerticalCounterBlock.VerticalCounterBlockListener() {
#Override
public void onCountChanged(int newCount) {
increasePreachCountTotal();
Toast.makeText(CounterActivity.this, String.format("%1$d", newCount), Toast.LENGTH_SHORT).show();
}
});
I do not see that toast nor does it engage the function call. What am I missing?
I can suggest you several improvement scope here mainly restructuring the current format.
Lets not keep the interface as a inner class. So here's your VerticalCounterBlockListener.java
public interface VerticalCounterBlockListener {
public void onCountChanged(int newCount);
}
Now implement this interface in your MainActivity
public class MainActivity implements VerticalCounterBlockListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m20_vcb = (VerticalCounterBlock) findViewById(R.id.vcb_m20);
m20_vcb.setVerticalCounterBlockListener(this);
}
// ... Other methods
// Override the onCountChanged function.
#Override
public void onCountChanged(int newCount) {
increasePreachCountTotal();
Toast.makeText(CounterActivity.this, String.format("%1$d", newCount), Toast.LENGTH_SHORT).show();
}
}
You might consider removing the Toast from the addBtn click listener which might create exception.
addBtn = (Button)findViewById(R.id.btn_addcount);
addBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
count++;
counttv.setText(String.format("%1$d", count));
if(mVerticalCounterBlockListener != null) {
mVerticalCounterBlockListener.onCountChanged(count);
}
}
});
This was good there was something wrong with my system. i uninstaklled app and restarted computer and it worked as expected.

A custom function structure for my customView.onclick

I have searched but could not find answer of my question.
This is what I have:
private class BoxView extends View {
private String caption;
private OnClickListener bvClickListener = null
public BoxView(Context context) {
super(context);
this.bvClickListener = new this.OnClickListener(){
public void onClick (View v){
/*v.setCaption("X"); view don't have this method */
}}
}
public void setCaption(String s){
this.caption=s;
invalidate();
}
}
This is what I want to have:
private class BoxView extends View {
private String caption;
private OnClickListener bvClickListener = null
public BoxView(Context context) {
super(context);
this.bvClickListener = new this.OnClickListener(){
public void onClick (BoxView bv){
bv.setCaption("X");
}}
}
public void setCaption(String s){
this.caption=s;
invalidate();
}
}
I may need custom methods for my custom views. And I want to be able to pass my custom view instead of view version of it when onclick is triggered so I can access to it directly.
Updated
And I want to have access to real object not a converted one. So I want to avoid this:
public void onClick (View v){
((BoxView)v).setCaption("X");
}
Call setCaption method as in onClick :
public void onClick (View v){
((BoxView)v).setCaption("X");
}
Try this
class Main extents Activity
{
BoxView boxView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if view is used using layout then
boxView = (BoxView)findViewByID(id);
//else if directly used
boxView = new BoxView(this);
box.setOnClickListener(new onClickListener()
{
#Override
public void onClick(View view) {
boxView.setCaption("X");
boxView.invalidate();
}
});
}
}

android inheritance activity(Base - Main activity)

I am having a problem about implemeting android apps.
I made baseActivity which is that base of other applications and other applications.
These are my code.
First, BaseActivity.java
public class BaseActivity extends Activity {
protected void onCreate(Bundle savedInstanceState,int layoutId) {
super.onCreate(savedInstanceState);
setContentView(layoutId);
Button menuBtn = (Button)findViewById(R.id.menuBtn);
menuBtn.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.d("bss","menu");
}
});
}
}
And the other one is MainActivity.java
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState,R.layout.activity_main);
setContentView(R.layout.activity_main);
}
}
Now I have a question! When I was click the menu button, click listener did not act. It did not print log message and any action. So, I have a problem to make my application. Is it related with life-cycle or How can I solve the problem.
I do not like the way you are forcing thing, still if you want to make it works this way you have remove setContentView(R.layout.activity_main); from MainActivity, that's because one you call again setContentView(R.layout.activity_main); in the sublcass, the view hierarchy for that Activity will be recreated invalidating what you have done in the super class
The code is setting the Content View twice. Following is the sequence of your code.
setContentView()
Add button listener
setContentView()
Statement # 2, adds the button listener and it is all good till now. But as soon as you reset the content view on statement # 3, the previous settings get void. And the button gets reinitialized and the onClickListener is no more attached to the button.
Remove the contentview from BaseActivity and put
Button menuBtn = (Button)findViewById(R.id.menuBtn);
menuBtn.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.d("bss","menu");
}
});
into MainActivity. The Base activity should be SuperClass and MainActivity subclass all the actions for view shold be initialize into the subclass.
Try this :
public class BaseActivity extends Activity {
protected void onCreate(Bundle savedInstanceState,int layoutId) {
super.onCreate(savedInstanceState);
}
public int getLayoutXML() {
return -1;
}
public abstract int getMenuId();
}
After this use this BaseActivity class like this :
public class service_detail extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Use button like this:
Button menuBtn = (Button)findViewById(R.id.menuBtn);
menuBtn.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.d("bss","menu");
}
});
}
#Override
public int getMenuId() {
// TODO Auto-generated method stub
return 1;
}
#Override
public int getLayoutXML() {
// TODO Auto-generated method stub
return R.layout.service_detail;
}
}

Array of subclasses and onClick()

I want to create by code an array of objects that are subclasses of Button.
public class MyButton extends Button {
private Context ctx;
private int status;
public MyButton(Context context) {
super(context);
ctx = context;
status = 0;
}
private click() {
status = 1;
// OTHER CODE THAT NEEDS TO STAY HERE
}
}
In the main activity I do this:
public class myActivity extends Activity {
private MyButton[] myButtons = new MyButton[100];
#Override
public onCreate(Bundle si) {
super.onCreate(si);
createButtons();
}
private void createButtons() {
for (int w=0; w<100; w++) {
myButtons[w] = new MyButton(myActivity.this);
myButtons[w].setOnClickListener(new View.onClickListener() {
public void onClick(View v) {
// ... (A)
}
});
}
}
}
Now I want the click() method inside MyButton to be run each time the button is clicked.
Seems obvious but it is not at my eyes.
If I make the click() method public and run it directly from (A), I get an error because myButtons[w].click() is not static and cannot be run from there.
In the meantime, I an not able to understand where to put the code in the MyButton class to intercept a click and run click() from there. Should I override onClick? Or should I override onClickListener? Or what else should I do?
How can I run click() whenever one of myButtons[] object is clicked?
Thanks for the help.
You can cast View v you got in listener to MyButton and call click on it:
private void createButtons() {
View.OnClickListener listener = new View.onClickListener() {
public void onClick(View v) {
((MyButton) v).click();
}
};
for (int w=0; w<100; w++) {
myButtons[w] = new MyButton(myActivity.this);
myButtons[w].setOnClickListener(listener);
}
}
you can add:
View.onClickListener onclick = new View.onClickListener() {
public void onClick(View v) {
((MyButton)v).click();
//since v should be instance of MyButton
}
};
to your Activity
then use:
myButtons[w].setOnClickListener(onclick);
//one instance of onclick is enough, there is no need to create it for every button
in createButtons()
but ... why, oh why array of buttons we have ListView in android ...

Passing a reference of an Activity

Hi guys I can't figure out why I cant get it to work, to check the CheckBox of an activity from within another class.
In the onCreate method of the activity I'm passing a reference of itself to another class
public MainActivity()
...
dbi = new DBPrefsInterface(this);
...
}
public DBPrefsInterface(Context ctx)
{
MainActivity pma = (MainActivity)ctx;
this.ma = pma;
}
Now I try to Check a checkbox which is placed on the activity
this.ma.cbx.setChecked(true);
but it isn't working.
It seems I didn't pass a reference, only a Copy of MainActivity.
Thanks in adcance
Try passing a handler and a reference to the checkbox in the constructor of the other class
and make the handler send a message to the checkbox's original context
hanlder.post(new Runnable() {
#override
void run {
checkbox.setChecked(true);
}
});
Why don't you pass in the CheckBox itself? So your constructor becomes public DBPrefsInterface(CheckBox cbx) and you manipulate that reference.
Here is an example of a simple activity to demonstrate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.checkbox);
CheckBox cbx = (CheckBox) findViewById(R.id.box);
Button button = (Button) findViewById(R.id.button);
final DBPrefsInterface iface = new DBPrefsInterface(cbx);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
iface.toggle();
}
});
}
private class DBPrefsInterface {
CheckBox cbx = null;
public DBPrefsInterface(CheckBox cbx) {
this.cbx = cbx;
}
public void toggle() {
cbx.setChecked(cbx.isChecked());
}
}
I don't see why you would want to do this, but I think using the following constructor would do the work:
private MainActivity ma;
public DBPrefsInterface(MainActivity a){
ma = a;
}
Then to set the checkbox (but checkbox needs to be public in order to work):
ma.cbx.setChecked(true);

Categories

Resources