I am having a few problems with how I've structured my App. I have a click handler in my Core class
that I decided I want to be forwarded to another class to make my code smaller and more modular, the problem is
inside ButtonClass, findViewById always returns NULL, I believe due to being out of scope.
In my XML manifest file I do have: android:name="com.prj.MyAppName"
In my core class things work fine, but once I create a new class I cannot use findViewById() inside of it.
Here is a stripped down version of my code:
public class Class1 extends Core
{
Button buttonHint1 = (Button)findViewById(R.id.buttonHint1);
}
public class Core extends Activity
{
public void myClickHandler(View target)
{
//THIS WORKS
//TextView userText2 = (TextView) findViewById(R.id.textViewHint1);
//userText2.setText( "OKOKOKO" );
ButtonClass myButtonClass = new ButtonClass();
myButtonClass.myClickHandler(target);
}
}
public class ButtonClass extends Core
{
public void myClickHandler(View target)
{
switch( target.getId() )
{
case R.id.buttonHint1:
//CRASHES ON findViewById ()!!!
TextView userText1 = (TextView) findViewById(R.id.textViewHint1);
userText1.setText( "OKOKOKO" );
break;
}
}
}
Does anyone know if there are any modifications I can make to allow ButtonClass to be able to use findViewById and perhaps
fix my scoping issues so my App does not crash?
I am still learning Android and would appreciate any advice.
Why not change the constructor of ButtonClass to allow the parameter (TextView)findViewById(R.id.textViewHint1)?
public ButtonClass(TextView tv){
//constructor stuff
}
If this is called in an Activity it should work.
Edit: My mistake, not TextView, Button!
Related
This is in the MainActivity:
imgModeContrastLeft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImagePair imp = new ImagePair();
String objId = "sMfSYvBxmX";
imp.downloadImagePairInBackground(this, objId);
}
});
But the this is not the MainActivity. How do I reference the main activity in the OnClickListener?
you can do MainActivity.this to get the MainActivity context
v.getContext() here is the "best" way
public void onClick(View v) {
ImagePair imp = new ImagePair();
String objId = "sMfSYvBxmX";
imp.downloadImagePairInBackground(v.getContext(), objId);
}
It will make it more "portable" so if you move, decide to reuse the code in another class, or change the class name, you don't need to change it.
The way you have it, this is referring to the inner-class and not the Activity Context
First of all, Remember that a whole InnerClass is tied to every single object of the outside class, subject to the condition that InnerClass ain't static and you have created an object of InnerClass too.
Now, whenever you need access to the reference of the object that invoked the InnerClass the Syntax is:
OuterClassName.this;
That's the right code:
imgModeContrastLeft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImagePair imp = new ImagePair();
String objId = "sMfSYvBxmX";
imp.downloadImagePairInBackground(MainActivity.this, objId);
}
});
As has been noted in one of the other answers, you can qualify this with a class name to reference a specific outer class from within an inner class. However, this syntax is somewhat obscure, and so I would recommend the more explicit (if also more verbose) approach of simply assigning to a local:
final MainActivity mainActivity = this;
imgModeContrastLeft.setOnClickListener(...); // reference "mainActivity"
So I have a View:
public class RadialMenuWidget extends View
from where I want to start a thread like I do from an activity
String urlInput = "http://myserver.com/"+mynewfile;
DownloaderThread downloaderThread = new DownloaderThread(UpdateActivity, urlInput);
downloaderThread.start();
but I get the message "Expression expected" on this line "DownloaderThread(UpdateActivity,... " more specifically on the activity name (UpdateActivity) even though I imported the activity inside the Widget.
What can I do to avoid this while still being able to call the thread.
The idea is, I use the RadialMenuWidget, and inside the RadialMenuWidget class I test to see which menu button was pressed and based on that, I decide what to do next. Calling other intents works just fine, but now I want to start to download a file using a separate thread (that I can call from a regular activity's onButtonClick)
EDIT
So my radialMenu has this structure:
public class RadialMenuWidget extends View {
...
public interface RadialMenuEntry {
...
}
public RadialMenuWidget(Context context) {
...
}
#Override
public boolean onTouchEvent(MotionEvent e) {
...
if (state == MotionEvent.ACTION_UP) {
...
if (menuEntries.get(i).getName() == "Update now") {
String urlInput = "http://myhost.com/"+mynewfile;
DownloaderThread downloaderThread = new DownloaderThread(this.UpdateActivity, urlInput);
downloaderThread.start();
}
}
...
And the DownloadThread class looks like this:
public class DownloaderThread extends Thread {
public DownloaderThread(UpdateActivity inParentActivity, String inUrl)
{
downloadUrl = "";
if(inUrl != null)
{
downloadUrl = inUrl;
}
parentActivity = inParentActivity;
}
#Override
public void run()
{
// does the download
}
...
}
Please help.
Thank you
My solution in the end was to mimic a 2 button radial menu using images. I used the images as backgrounds for a button, placed them in a linear layout, used 9-patch images and got to call the thread onClick like before. So I avoided having to deal with radialMenuWidget in this view.
So question is over now.
I was hoping for a java solution to this problem since I am a newbie and eager to learn anything Android, but this is it, I will manage with my (ugly) solution.
I need to determine in runtime from code if the application is run under TestInstrumentation.
I could initialize the test environment with some env/system variable, but Eclipse ADK launch configuration would not allow me to do that.
Default Android system properties and environment do not to have any data about it. Moreover, they are identically same, whether the application is started regularly or under test.
This one could be a solution: Is it possible to find out if an Android application runs as part of an instrumentation test but since I do not test activities, all proposed methods there won't work. The ActivityManager.isRunningInTestHarness() method uses this under the hood:
SystemProperties.getBoolean("ro.test_harness")
which always returns false in my case. (To work with the hidden android.os.SystemProperties class I use reflection).
What else can I do to try to determine from inside the application if it's under test?
I have found one hacky solution: out of the application one can try to load a class from the testing package. The appication classloader surprisingly can load classes by name from the testing project if it was run under test. In other case the class is not found.
private static boolean isTestMode() {
boolean result;
try {
application.getClassLoader().loadClass("foo.bar.test.SomeTest");
// alternatively (see the comment below):
// Class.forName("foo.bar.test.SomeTest");
result = true;
} catch (final Exception e) {
result = false;
}
return result;
}
I admit this is not elegant but it works. Will be grateful for the proper solution.
The isTestMode() solution did not work for me on Android Studio 1.2.1.1. Almighty Krzysztof from our company tweaked your method by using:
Class.forName("foo.bar.test.SomeTest");
instead of getClassLoader(). Thanks for Krzysztof!
We created a solution to pass parameters to the MainActivity and use it inside the onCreate method, enabling you to define how the Activity will be created.
In MainActivity class, we created some constants, which could also be an enum. We created a static attribute too.
public class MainActivity {
public static final int APPLICATION_MODE = 5;
public static final int UNIT_TEST_MODE = 10;
public static final int OTHER_MODE = 15;
public static int activityMode = APPLICATION_MODE;
(...)
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
switch (activityMode) {
case OTHER_MODE:
(...)
break;
case UNIT_TEST_MODE:
Log.d(TAG, "Is in Test Mode!");
break;
case APPLICATION_MODE:
(...)
break;
}
(...)
}
(...)
}
We made MainActivityTest class abstract, created a setApplicationMode and called this method inside the setUp() method, before calling the super.setUp() method.
public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
protected void setUp() throws Exception {
setApplicationMode(); // <=====
super.setUp();
getActivity();
(...)
}
(...)
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
}
}
All other test classes inherit from MainActivityTest, if we want it to have another behaviour, we can simply override the setApplicationMode method.
public class OtherMainActivityTest extends MainActivityTest {
(...)
#Override
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.OTHER_MODE;
}
}
The user nathan-almeida is the friend that is co-author of this solution.
Is it possible to make a secondary class to hold the OnClick Listener? Meaning not being created in the Activity class?
I just find that putting OnClick listeners in the main activity class is just messy and I would rather have them in separate classes. Thanks
Sure, that's possible. Just create a class that implements View.OnClickListener and set that as listener to the View. For example:
public class ExternalOnClickListener implements View.OnClickListener {
public ExternalOnClickListener(...) {
// keep references for your onClick logic
}
#Override public void onClick(View v) {
// TODO: add code here
}
}
And then set an instance of above class as listener:
view.setOnClickListener(new ExternalOnClickListener(...));
The parameterized constructor is optional, but it's very likely you'll need to pass something through to actually make your onClick(...) logic work on.
Implementing a class anonymously is generally easier to work with though. Just a thought.
Instead of putting the onCLicklistener in a separate class, why dont you try to define onClickListener outside onCreate()??
For e.g: like this
onCreate()
yourViewName.setOnClicklistener(listener):
Outside onCreate()
private OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
};
Yes you can. However, making the listener an inner class has one advantage - it can access the fields and variables of your activity class directly. If you make it a separate class, and your listener actually need to access 5 views, your listener constructor might look like this:
MyListener listener = new MyListener(context, button, textView1, textView2, ratingBar, imageView);
Which is kinda bulky too. If your listener is simple, go ahead and make it a separate class. Otherwise, its up to you for readability.
Let me share how I code it using MVP. It's the best way to make clean code. Remember each class must have an interface to control it. I will show you the simplest one.
Suppose you want to Toast a text onClick and control it from another class. Here's how it works. Creating interfaces is for nothing but to connect with each other and you can review the code easily.
Create an interface for that MainActivity class.
public interface MainActivityView {
void showToast();
}
Create another interface for the Presenter class.
public interface IMainPresenter<V extends MainActivityView> {
/*Generic Type is to make sure it comes from MainActivity class only and to avoid other class to access it.*/
void onAttach(V mainView);
void onButtonClick();
}
Remember interfaces are nothing but to override method for each class.
Create a Presenter class
public class MainPresenter<V extends MainActivityView> implements IMainPresenter<V> {
private V mainActivityView;
#Override
public void onAttach(V mainActivityView) {
this.mainActivityView=mainActivityView;
}
public V getView() {
return mainActivityView;
}
#Override
public void onButtonClick() {
getView().showToast(); //This is the method from MainActivity controlling with this class
}
}
I'll skip, activity_main.xml layout because there's just a button with id="#+id/buttonId." In MainActivityClass,
public class MainActivity extends AppCompactActivity implements MainActivityView {
Button btn;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainPresenter mainPresenter = new MainPresenter();
mainPresenter.onAttach(this);
btn = findViewById(R.id.buttonId);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mainPresenter.onButtonClick(); //Here, check No.3 again!
}
});
}
#Override
public void showToast() {
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
}
}
All I want to tell you is that. If you create objects in a class, it cannot make unit testing. That's why you're not seeing any new objects calling in android. So, you can use a singleton pattern (Here is Lazy Type) in Presenter class. I'll remove its interface and Generic to see it clearly.
public class MainPresenter {
private static final MainPresenter mainPresenter = new MainPresenter();
MainPresenter() {}
public static MainPresenter getInstance() {
return mainPresenter;
}
//Some methods here can be get it once you create an object with getInstance();
}
And so you can get its methods from MainActivity like this.
Instead of creating objects like this...
MainPresenter mainPresenter = new MainPresenter();
You can get it like this...
MainPresenter mainPresenter = mainPresenter.getInstance();
More example for singleton pattern can be found here,
https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples
Finally, using static is not a very good choice because it uses memory space whether you use it or not. And so, you can create objects within Application Layer get it with a Typecasting. I'm sure you don't need to unit test that Application layer.
public class AppLayer extends Application {
private MainPresenter mainPresenter;
#Override
public void onCreate() {
super.onCreate();
mainPresenter = new MainPresenter();
}
public MainPresenter getMainPresenter() {
return mainPresenter;
}
And you need to give a class name within Application in manifest.xml
<application
android:name=".AppLayer"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
</application>
And you can get it with a Typecast in MainActivity like this!
MainPresenter mainPresenter = ((AppLayer)getApplication()).getMainPresenter();
For further studies, I suggest you learn ButterKnife, Dagger 2 and SOLID Principles. It will help you to create clean coding. Have fun!
You can do it. But just think that you will not have a reference to the activity, neither to it's attributes, including all the views. (unless you make them public or accessible with getters methods).
Also, be extra carefull with storing references to the activity or any members on the listener, since they might avoid the garbage collector from getting the listener memory back.
public class CommonClick {
public static void commonClick(final AppCompatActivity context){
context.findViewById(R.id.appbar).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
I have a custom gallery view in which I am overriding some methods. I would like to be able to call a function in my main activity from this class. How do I make a reference back to my main class?
I thought I'd just push the class reference into CustomGallery by creating a setter function ---> g.setBaseClass(this);
CustomGallery g = (CustomGallery) findViewById(R.id.playSelectionGallery);
g.setSpacing(10);
g.setCallbackDuringFling(false);
g.setAdapter(new ImageAdapter(this));
g.setSelection(1);
registerForContextMenu(g);
g.setBaseClass(this);
Problem is this is of type Context and someFunctionToCall() will result in a not a member of this class error. In my custom class I have:
public void setBaseClass(Context baseClass)
{
_baseClass = baseClass;
}
private void callSomeFuntionOnMyMainActivityClass()
{
_baseClass.someFunctionToCall();
}
All I want to do is call back to my main class, called ViewFlipperDemo. This would be easy in As3. Any thoughts? Hopefully I'm missing something really simple.
That's actually not a good idea... but you can do it this way:
private void callSomeFuntionOnMyMainActivityClass()
{
((ViewFlipperDemo)_baseClass).someFunctionToCall();
}
What you should do instead is implementing a simple observer which allows you to notify the Activity that something happened. That's one of the main OO principles, your custom class shouldn't know anything about your activity class.
Observer pattern example
The Observer interface:
// TheObserver.java
public interface TheObserver{
void callback();
}
Your custom view:
public class CustomGallery{
private TheObserver mObserver;
// the rest of your class
// this is to set the observer
public void setObserver(TheObserver observer){
mObserver = observer;
}
// here be the magic
private void callSomeFuntionOnMyMainActivityClass(){
if( mObserver != null ){
mObserver.callback();
}
}
// actually, callSomeFuntionOnMyMainActivityClass
// is not a good name... but it will work for the example
}
This is the activity that will benefit of the observer (notice that now you can use your custom view on different activities not just one, that's one of the key reasons to implement it this way):
public class YourActivity extends Activity{
// your normal stuff bla blah
public void someMethod(){
CustomGallery g=(CustomGallery)findViewById(R.id.playSelectionGallery);
g.setObserver(new TheObserver(){
public void callback(){
// here you call something inside your activity, for instance
methodOnYourActivity();
}
});
}
}
You will notice that this design pattern (observer) is widely used in Java and Android... almost any kind of UI event is implemented using observers (OnClickListener, OnKeyListener, etc.). By the way, I didn't test the code, but it should work.