How to reduce size of MainActivity/outsource UI code - java

most of my apps are apps with just one screen (due to the functionality) sometimes being extended by "floating" popups that are basically RelativeLayouts (containing other UI elements) that are being added to the Main layout, keeping the UI Elements of the Main layout active and "touchable" in the background.
For a better understanding, please just look at the sketch (purely symbolic):
If you include all the stuff from the popups as well, I have about 90 UI elements, a dozen of custom View classes like buttons, sliders, ... at a time, all of them needing to have certain listeners running on them, all of them having to be resized on start up [...]
I, in fact, write quite efficient (in terms of amount of bytes used) code, and if there is a 3-line method to return a value that replaces 4 lines of additional code, I write this method just to satisfy myself. However, I quite don't understand how to outsource code from my MainActivity to other classes. Of course, if there is a calculation to be made, this is something that I put into another class rather than just creating a method in my MainActivity class. Anyway, my MainActivity is now at 1600 lines of code which is an awful amount of text to overview for debugging, adding or changing code. The variable declarations of my UI elements alone take 100 lines of code (maybe 70 lines if compressed)
That was the longest explanation I ever made for a post but now we get to my question:
How can/do you outsource code like listeners, UI stuff like findViewById() or similar things to other classes? Are there common practices to do so in an efficient way? I don't want to go for clumsy workarounds that skyrocket the CPU, so "smooth" stuff is stuff I am looking for.
It might be KIND OF off-topic but I hope it's ok to be asked here.

I'm not sure that there is particular answer on your question because I suppose it's all about experience which you can get reading source code of other projects for instance. But I have few advices which could help you:
1. Use libraries which eliminate boilerplate code like findViewById and listeners. One of them is Butterknife
7 lines of code:
View text = findViewById(R.id.text_id);
text.setOnClickListener(
new View.OnClickListener() {
#Override public void onClick(View v) {
//
}
}
);
2 lines code:
#OnClick(R.id.text_id) public void handleClickOnText() {
//
}
2. Use static helper classes:
4 lines of code:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.new_message);
builder.setTitle(R.string.create_calendar_title);
builder.show();
1 line of code:
DialogsHelper.newMessage(this);
3. Read about MVP. It's about modular app architecture. Roughly speaking it helps to divide raw View from logic.
Basic sample:
public static class SomePresenter {
private SomeView view;
public SomePresenter(SomeView view) {
this.view = view;
view.showProgressLoading();
loadData();
}
private void loadData(){
//loading data from some server
}
private void loadingDataHandler(SomeModel model){
view.showData(model);
}
}
public static class SomeView extends View{
#Inject(R.id.text_progress_title) TextView text;
public SomeView(Context context) {
super(context);
ButterKnife.inject(this);
}
public void showData(SomeModel model){
text.setText(model.dataA + ":" + model.dataB);
}
public void showProgressLoading(){
text.setText(R.string.progress);
}
}
public static class SomeModel{
public final int dataA;
public final int dataB;
}
And your onCreate method of activity could look like this:
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
new SomePresenter(someView);
}

Related

Is it mandatory to create inner classes to handle events in DataBinding?

I was learning about DataBinding, particularly the section about handling events and click events. Now, I noticed in a few YouTube tutorials that I instructors were mainly using an inner class to handle these events. However, earlier, I'd written this code that implemented the View.OnClickListener and directly allowed me to handle click events.
Here it is:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ActivityMainBinding activityMainBinding;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.enterButton.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (view == activityMainBinding.enterButton) {
String name = activityMainBinding.nameEditText.getText().toString();
String email = activityMainBinding.emailEditText.getText().toString();
String country = activityMainBinding.countryEditText.getText().toString();
User user = new User(name, email, country);
activityMainBinding.setUser(user);
}
}
}
And this works.
I was wondering, is this form of handling click events not considered a good practice? I checked through a few more tutorials and they all used inner classes, thereby causing this doubt.
Thanks for any help.
Q: Is it necessary to create inner classes?
A: No, absolutely not. It's merely a useful convention :)
Q: Is this form of handling click events ... a good practice?
A: In general, any individual "class" should do "one thing". The class's properties and its methods should match the class's "abstraction".
For example, an "Automobile" class should probably not have an "onClick()" method. Even if your "Automobile" class implementation might have a "button", with an "onClick()" method.
Or your "Automobile" might have a dozen different buttons. In that case, I'd definitely prefer to see a dozen anonymous inner classes, one for each button. It would be shorter; it would be cleaner.
In your example, however, I don't see any problem. It looks fine :)

Android: Updating textviews in multiple activities

I need some pointers on doing the following:
lets say i have 10/20 (number doesn't matter) of activities.
each of these activities has a textview that should work like a counter.
each of these activities has a button to go to the next activity.
this counter starts when the app is launched, and increment itself every second.
So what i did so far is:
have in my main activity a method that instantiate a class that extends Thread.
In that class in the run() method, i increment a variable when a second passes.
Now i'm stuck on what i should do next. Any pointers would be appreciated thanks.
Edit: i need a way to communicate from inside the run method, to whichever activity is now currently on screen, to update its textview.
Just a bit of theory here for standard Object Oriented Programming : stick to the recommended principles like Loose Coupling which makes your project code less tied to each other. You can read more on that later.
Now, using Events, you can setup a system that is synonymous with the natural Publisher/Subscriber design pattern. Like this:
The activity that needs to notify the other activities is called Publisher and the other activities that need to be notified are called Subscribers.
From here:
There are already built and tested libraries to do Events in android. Like my favorite EventBus.
Step 1 Add this line to your app-level build.gradle file:
compile 'org.greenrobot:eventbus:3.0.0'
Then create a simple Plain Old Java Object aka POJO class like this:
public class UpdateTextViewEvent{
private String textToShow;
public UpdateTextViewEvent(String text){
this.textToShow = text;
}
//add your public getters and setters here
}
Step 2 Notify others:
When you want to notify anyone of the changes, you simply called this method:
EventBus.getDefault().post(new UpdateTextViewEvent("Some new Text"));
Step 3 Receive notifications
For those who want to be notified of this event, simply do this:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
NOTE: to actually handle the event:
#Subscribe
public void onEvent(UpdateTextViewEvent event){
String text = event.getTextToShow();
//now you can show by setting accordingly on the TextView;
}
This is so much easier to do, do decouple your code by eliminating static references in your different activities
I hope this helps! Good luck!
make that Textview in second class as
public static Textview text;
and call it in main activity as
SecondActivity obj=new SecondActivity();
obj.text.settext("");
You can create one another activity e.g. BaseActivity extend with Activity class and your all 10/20 activity extends with created BaseActivity Class.
You can use your textview with protected access specifiers.
What you need to do is inside the counter class, create an a method and passed in a TextView as the parameter. Then create an int variable and set the counter as the instance:
Like this
public static class Counter extends Thread{
private static int x;
#Override
public void run(){
x = counter;
}
public void setCounter(TextView tv){
tv.setText(String.valueOf(x));
}
}
Now call this method setCounter(TextView) in all the activity's onCreate() method you'll like to display the counter, and passed in your the layout TextView as the argument. Like this
...
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState):
....
TextView cTextView = (TextView)findViewById(R.id.texT1);
Counter c = new Counter();
c.setCounter(cTextView);
}

Android message between classes

This is my first post and I did not find anything similar, so I've decided to ask.
Im developing a Poker Game for Android to practice the SDK and refresh/improve my Java. Its a simple app that control a texas hold'em poker hand.
Initally, I wrote my classes using only Java SE and it looks fine. Each class has its own purpose and testing it with console input/output, I can see it really works :)
Last week, I decided to port it to Android to see things happening through a graphic interface, so I got the resource images, make an Activity and included my poker package.
Before port to Android I can just put a println (or readLine) to see whats going on and send my inputs. Now Im stuck in how each class can communicate to the game activity to provide what must be drawn. If possible, I don't want insert Android draw code inside game classes. Im trying find a way to exchange messages between my Activity and the game classes and Id like some suggetions. Im new in developing Android apps, so I dont know all mechanics to do this.
Below are the snippet from my activity:
package my.poker.game;
//import stuff
public class ActivityHand extends Activity
{
private static Vector<Player> Players = new Vector<Player>();
public static final int MAX_PLAYERS = 8;
public static void startEmptyTable()
{
Players.removeAllElements();
Players.setSize(MAX_PLAYERS);
}
public static void LeaveTable(int pos)
{
Players.set(pos, null);
}
public static void SitTable(int pos, Player player)
{
Players.set(pos, player);
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int cash = 1000;
startEmptyTable();
SitTable(0, new Jogador("William", cash));
SitTable(2, new Jogador("Richard", cash));
SitTable(4, new Jogador("John", cash));
SitTable(6, new Jogador("Paul", cash));
SitTable(8, new Jogador("Albert", cash));
//Start a Hand.... in future this will be a loop for each Hand
Hand hand = new Hand(Players);
}
}
The object hand, will select a Dealer, deal the cards, control the small and big blinds and start the game loop.
The question is: how the hand class can tell the Activity to Draw and pass an object containing what to draw?
Thanks a lot for your help
Editing: I've decided to try implementing it using a Handler and passing simple messages. As I read from Handler at Developers a Handler object is assigned to thread's MessageQueue, so I tried to put this into my Activity Class (before Hand hand = new Hand (...) ):
Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
Bundle bundle = msg.getData();
// Do something with message contents
}
};
And inside the Hand class I put this when I want to draw something:
Handler handler = new Handler();
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("Key", "The Key's Value");
msg.setData(bundle);
handler.sendMessage(msg);
As I understood, both handlers are from same thread, so they are assigned to same MessageQueue right? I tought when I send a message inside Hand class, the Activity class can receive it in handleMessage method and processes any message I send, but handleMessage doesn't execute.
What am I missing?
Thanks again
To call methods in the activity, you want to pass the activity to this class.
for example:
public class PokerActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
Hand hand = new Hand(this);
}
public void setVisibleHand(Player player)
{
<do something in the activity>
}
}
public class Hand
{
PokerActivity pokerActivity;
public Hand(PokerActivity activity)
{
this.pokerActivity = activity;
}
public void setVisibleHand()
{
pokerActivity.setVisibleHand(player1);
}
}
Now, this might not be the best way to do it. In Android you have to be carefull to not leak the context, or you might be getting trouble with the memory. (simply passing the activity/context might be the easy way, but is also the easiest way to leak the context.)
I'd advise you to look at some simple tutorials first, to get a feeling of how android activities work.
You could use a Handler and Message system to communicate between your classes.
This tutorial by Lars Vogel should help you Android Threads, Handlers and AsyncTask - Tutorial
You will have to create another class to make the interactions with your own classes and the activity itself.
From that class you can use the activity context to control the android activity.
The approach about using Handlers and Messages is good but would require you to modify your game classes.
Maybe you should extend View Class to draw the Hand.
http://developer.android.com/reference/android/view/View.html

Android, Calling view object from code

I am kind of a newbie so excuse me if this question is too simple or too hard.
I have this code in my java file:
public void button_baby_clicked(View v)
{
//do something here
}
this gets called when someone clicks the imagebutton in my xml file, but how do I call this from the java file itself?
Because it's expecting a View object... and I'm guessing I need to recreate that? How?
Edit:
Ok, to clarify, I want to be able to call the above function via a click in my xml file as well as a function under it.
For example:
public void button_baby_clicked(View v)
{
//do something here
}
public void someFunction()
{
x = 10;
button_baby_clicked(); // This should call the above function.
}
In ur ImageButton you have to add an attribute: android:onClick="button_baby_clicked"
In the java file, you have added:
public void button_baby_clicked(View v)
{
//do something here
}
The logic behind this is:
Upon clicking ur imagebutton, this method will automatically get called, i.e "v" argument will be having ur imagebutton.
The advantage of giving like this is: You no need to initialize the imagebutton in ur activity and no need to set click listener too for this imagebutton.
Alright, if you want to have the method invoked every time the view is clicked, do what the others have said.
Alternatively, you can do something like this.
ImageView globalReference;
#Override
public void onCreate(Bundle icicle){
*** CODE ***
globalReference = (ImageView) findViewById(R.id.myImageView);
*** CODE ***
}
Then, whenever you want that to be called with that particular View, simply call
button_baby_clicked(globalReference);
You can also do this with any View object you create dynamically.
View myTv = new TextView(context);
View myLl = new LinearLayout(context);
button_baby_clicked(myTv);
button_baby_clicked(myLl);
Just get a valid View reference within the same scope as the method, and pass it in like any other method. It can even be null if the method is capable of handling it.
Can't you use it like -
mButton.setOnClickListener(new OnClickListener{
#Override
public void onClick(View v) {
button_baby_clicked(v);
}
}
);
??
EDIT :
If you need to call someFunction() from the onClick of a button,and from there,you need to call button_baby_clicked(),you have to get View v object in someFunction. This link might help you. Please refer Start a service on onClick. You can change appropriately.
I believe its best if you refactor your code and put the code in the event handler into a global method that can be called from anywhere. like this:
public void button_baby_clicked(View v)
{
taskToPerform(); // Perform a certain task
}
public void someFunction()
{
x = 10;
taskToPerform(), // Perform the same task again
}
public void taskToPerform()
{
//This is where you write the task you want to perform
}
This way you can reuse the code in the taskToPerform() method anywhere, anytime.

setOnClickListener to all extended ImageViews - Java

I have a custom class that I've written that extends ImageView (for Android Java). I want to add a ClickListener to every instance I create of the class that will do the same thing (just animate the ImageView.
I tried a few different things to no avail. The code below is what I want to accomplish but it's being applied to an instantiated object of the class.
MyCustomImageView fd = new MyCustomImageView(this);
fd.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
Animater(fd);
}
});
I tried using "implements onClickListener" on the class declaration and then a public void onClick() method in the class, and that didn't work for me.
I also tried using the code snippet above with a "this" instead of "fd" and that didn't work either.
I'm relatively new to java and this is out of the scope of my knowledge. Any assistance you can provide is greatly appreciated.
It's really easy. You have to do it in your custom class:
public class MyCustomImageView extends ImageView{
public MyCustomImageView(Context context){
super(context);
setOnClickListener(theCommonListener);
}
private OnClickListener theCommonListener = new OnClickListener(){
public void onClick(View v) {
// do what you want here
}
}
}
There are other ways to do it, but this is one is really easy to implement and understand. Every instance of MyCustomImageView will have the same event listener (unless you override it from outside).

Categories