Please correct me if my OOP application is incorrect. I have a custom Android class at /src/myworkspace/MyObject.java that does not extend anything.
public class MyObject {
...
public void methodOne() {
...
}
}
Within the main activity, MyObject.methodOne() is called.
How would I manipulate view objects in methodOne?
For example somthing similar to
mButton = (Button) findViewById(R.id.button_one);
mButton.setOnClickListener(mButtonListener);
The methods of MyObject are reused in many activities. If this approach is incorrect, where should the repeatable code be stored?
I would suggest MyObject receive a view as a parameter:
public class MyObject {
...
public void methodOne(Button button) {
...
}
}
The just call it this way from your activity:
... activity code ....
MyObject obj = new MyObject();
Button button = (Button) findViewById(R.id.button_one);
obj.methodOne(button);
.... more activity code.
You can use other approaches too :
Bind MyObject to activity in the constructor :
MyObject(Activity owner) {
.. assign activity to member
}
Use Object as a nested inner class.
Activity
class MyObject {
Each has it's merits or gotchyas, I personally would stick with passing a view by parameter, it is the most loosly coupled, and IMO the simplest and least error prone.
Related
I seem to be stuck with a problem with an object communicating with my activity class. The object is a view object with an onClick method that when called I would like it to notify my activity class so that it can perform said action. Below is some example code of my situation (assume all conventional setup operations have already been made):
public class MainActivity extends AppCompatActivity{
//...other global methods and objects
//Does not have access to instantiated Entry object(s)
public void entryObjectWasClicked(){
//perform said action
}
}
public class Entry extends View implements View.OnClickListener{
//...other global methods and objects
//Does not have access to the MainActivity object
#Override
public void onClick(View v){
//send a message to the MainActivity to
//somehow call the entryObjectWasClicked() method
}
}
The only way (off the top of my head) that I could think about dealing with this problem is by creating a static method in MainActivity and then calling it from an anonymous MainActivity object in the onClick method of Entry. The problem with the static method approach is that any subsequent method/object/primitive usages in the static method force those methods/objects/primitives to be static. This defeats the purpose of then being able to have two different instances of the MainActivity object.
After some looking I came across using Broadcast messages, specifically using the LocalBroadcastManager to send an intent to the activity. This code example works for my model, but I want to know: is this the best way for me to go about sending messages to my MainActivity from my Entry object?
If there is a more effective way of doing all this, what would it be?
You're overcomplicating things. Don't override onClick for this. Instead, have your activity call setOnClickHandler on your view, which sets a callback that's called when the view is clicked. Then use the default implementation.
Since you extend view, i guess you want to use it inside a layout. That means you may want to create a Listener for that. Example:
public class Entry extends View implements View.OnClickListener{
private OnClickListener listener;
public void setListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(){
if (this.listener != null) this.listener.onClick(this);
}
}
How you can inflate your layout in your Activity and access your custom view.
public class MainActivity extends AppCompatActivity{
public void onCreate( ...) {
Entry entry = findViewById(R.id.entry);
entry.setListener(new OnClickListener(...));
}
}
can someone tell me, how do get a Stringvalue from a thread to the mainActivity?
i have a thread like this:
public class XMLHandler extends DefaultHandler {
XMLDataCollected data = new XMLDataCollected();
......
......
public String getInformation() {
String information = "";
if (data.getData().equals("residential")) {
information = "Stadt";
}
return information;
}
}
in the mainActivity i tried to set the value into a textview like this:
textView.setText(xmlHandler.getInformation());
i does not work after all. what i am doing wrong? any solutions and advices? thanks in advance
If you have a SeparateThread class then you need to create one Interface say
public interface FetchValueListener{
public void sendValue(String value_to_send);
}
And your acctivity will be implementing this interface and thus sendValue(value_to_send) method will be added to your activity.
Next step would be when you create the object of the THread class then you need to pass the object of that interface in the paramater as follows:
public class myThreadClass{
FetchValueListener mllistener;
myThreadClass(FetchValueListener listenerObj){
mllistener=listenerObj;
}
}
Now when you want to send some value to the activity from thread you can just simply call
mllistener.sendValue(value_you_wan_to_send);
And inside your actiivty you will get the value in the sendValue() method..
In that method you need to post the data to runnable using the handler so that you can make changes to the UI like setText etc.....
If you directly try to set the value of text view in that method you will get an exception.
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) {
}
});
}
}
Is there any way to access a layout's view from a non-Activity-derived class? I'm creating an Accordion class and need to access some of the activity's UI elements. I'm passing in the activity's context to my accordion class's constructor, but the findViewById API is only available from the Activity class. I also don't want to pass in an instance of my activity since that seems to be frowned upon due to potential memory leaks.
I'm pretty sure you can just pass an activity as a parameter, e.g.
public void initSouthViews(Activity activity) {
for (int i = 0; i < southScores_.length; ++i) {
southScores_[i] = (EditText) activity.findViewById(10);
}
}
Here is something that might be helpful.
public interface IViewRequest {
public View requestViewByID(int id);
}
public class MyActivity extends Activity {
private IViewRequest viewRequest = new IViewRequest(){
public View requestViewByID(int id){
return findViewById(id);
});
}
public class Accordion(){
private IViewRequest viewRequest;
public Accordion(IViewRequest viewRequest){
this.viewRequest = viewRequest;
}
private View findViewById(int id){
return viewRequest.requestViewByID(id);
}
}
I have never tried something like this. I also don't know if it won't cuase any memory leaks. But it does what you asked :) "Calling findViewById() from outside an activity"
Activity's context is in fact the Activity class itself. Assuming that this object will live inside only one Activity, it should be safe to pass object of type Activity to it. Otherwise, think about reengineering your Accordion class.
I passed in an instance of one of the Views into the class's constructor.
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.