Butterknife View injection - android

I stumbled across a very interesting Dependency Injection library called ButterKnife. Using ButterKnife it's easily possible to inject Views into activities or fragments.
class ExampleActivity extends Activity {
#InjectView(R.id.title) TextView title;
#InjectView(R.id.subtitle) TextView subtitle;
#InjectView(R.id.footer) TextView footer;
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.inject(this);
// TODO Use "injected" views...
}
}
However if using Dependency Injection those Views must be public so that Butterknife can inject it (using private fields results in an exception fields must not be private or static).
In my past project I always made all the member fields (including the views) private as I thought this is best practice (information hiding etc.) Now I am wondering if there is a reason why one should not make all the views public? In this case I cannot use ButterKnife but I want to use it because it simplifies the code a lot.

First off, Butter Knife is not a dependency injection library. You can think of it as a boilerplate reduction library since all it does is replace findViewById and various setXxxListener calls.
The reason that Butter Knife requires views not be private is that is actually generates code which sets the fields. The code that it generates lives in the same package as your class which is why the field must be package-private, protected, or public. If the field was private the generated code would fail to compile since it cannot access the private field.
The generated code looks something like this:
public static void inject(ExampleActivity target, ExampleActivity source) {
target.title = (TextView) source.findViewById(R.id.title);
target.subtitle = (TextView) source.findViewById(R.id.subtitle);
target.footer = (TextView) source.findViewById(R.id.footer);
}
When you call ButterKnife.inject(this) it looks up this generate class and calls the inject method with your instance of ExampleActivity as both the destination for the fields and the source for findViewById calls.

Related

Is there any harm if we use private access modifier for Android Views?

For e.g., is the following code is right?
private LinearLayout layout1;
private LinearLayout layout2;
private LinearLayout layout3;
No, there is no harm in it. However, if you are using some kind of view injection library like ButterKnife, it won't let you define your views as private. The reason is the following:
The reason that Butter Knife requires views not be private is that it actually generates code which sets the fields. The code that it generates lives in the same package as your class which is why the field must be package-private, protected, or public. If the field was private the generated code would fail to compile since it cannot access the private field.
Source: Butterknife View injection

Using Android Annotation in an existing project

I am trying to you use Android Annotations (https://github.com/excilys/androidannotations) in an existing project. I cannot convert the whole project to use Annotations. Can I have some activities that utilize annotations and some acitivites that doesn't use Annotations.
But When I did that, some functionalities stopped working. Like If I used only -
#ViewById(R.id.find)
public Button FIND;
...
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
if (FIND != null) {
FIND.setOnClickListener(this);
}
...
}
OnClick on the button doesn't work. Is it mandatory to use #Click annotation.
Can't I just use annotations only where I wish to have. And other parts of the code be the old code without annotations. Please guide me.
Thanks
Please read the doc more carefully. The injected views are first available in the #AfterViews annotated methods:
#AfterViews
void afterViews() {
// you can use injected views here
}
https://github.com/excilys/androidannotations/wiki/Injecting-Views#afterviews
First of all please respect coding convention, you can find ax explanation here
Then, for your question, you can use code of both types: native android and android annotations one.
If you inflate a view using #ViewById annotation, you must keep in mind that the injection of the view is done at a certain point of the execution, so before of that your variable will be null.
As WonderCsabo reccommended to you, use injected views inside of #AfterViews annotated method.
Otherwise, if you want to mantain android native syntax, you MUST instantiate your view manually with findViewById method. Obviously after that you have setted activity's layout

Android MVP pattern package structure

I saw various great tutorials on MVP pattern in android, but the authors all seem to have different practice on packaging.
The first tutorial I saw did the packaging by functionalities. Such as, "Login", "Join", "UI" package.
The UI package has only activities, the "Login" package has the interfaces for the presenter and the concrete presenter, and this package contains a sub package "Model" that contains everything about the login model(communications with the server). The "Join" package has the same composition as the "Login" package.
But the other one I saw did the packaging by scene, such as "Join", "Login".
"Join" package contains an activity, and three sub packages named "Model", "View", "Presenter".
What is the best practice? Are there any articles that handles this issue?
App should have package according to features not by the common
functionality.
I find people make activity, fragments, Adapters,etc.
common purpose package in one group but this is bad practice!
Mostly developers group them like this because they do this to keep the same package structure for all the applications they work on. But that is very wrong decision cause it is always hard to find classes when they are grouped only because they share same parent classes!
We should group the classes according to parent classes but only if
we are making some API but if we are making a custom product for our
client then it is very bad practice.!
Like all activities most developers put in activity package because all activity classes extends the Activity class.That makes sense that this is only activity related package but it is hard to go through those packages.
Suppose we have One OrderListActivity class and we fetch the order list from server and then display it in one OrderListFragment class and obviously for that we need OrderListAdapter to show the order listing. so when customer ask for some modification or any feature he wants on that Order List screen we have to go to many packages to satisfy client need. Like we have to go to activity package and modify some thing in OrderListActivity and then go to OrderListFragment and then OrderListAdapter and then OrderListViewHolder,etc.!So This becomes too hard and we may create issues in process of modifying!
so we should group together the classes which are getting
changed/modify together.
That's the best practice and so we should group all those classes who are responsible for OrderListing feature in one package and we call it orderdlist package.
Please check my this medium post i have explained the package structure too in that:--
https://medium.com/#kailash09dabhi/mvp-with-better-naming-of-implementation-classes-dry-principle-e8b6130bbd02
I just repost my answer here
I often put business logic code in Model Layer (don't make confusion with model in database). I often rename as XManager for avoiding confusion (such as ProductManager, MediaManager ...) so presenter class just uses for keeping workflow.
The rule of thumb is no or at least limit import android package in presenter class. This best practice supports you easier in testing presenter class because presenter now is just a plain java class, so we don't need android framework for testing those things.
For example here is my mvp workflow.
View class: This is a place you store all your view such as button, textview ... and you set all listeners for those view components on this layer. Also on this View, you define a Listener class for presenter implements later. Your view components will call methods on this listener class.
class ViewImpl implements View {
Button playButton;
ViewListener listener;
public ViewImpl(ViewListener listener) {
// find all view
this.listener = listener;
playButton.setOnClickListener(new View.OnClickListener() {
listener.playSong();
});
}
public interface ViewListener {
playSong();
}
}
Presenter class: This is where you store view and model inside for calling later. Also presenter class will implement ViewListener interface has defined above. Main point of presenter is control logic workflow.
class PresenterImpl extends Presenter implements ViewListener {
private View view;
private MediaManager mediaManager;
public PresenterImpl(View, MediaManager manager) {
this.view = view;
this.manager = manager;
}
#Override
public void playSong() {
mediaManager.playMedia();
}
}
Manager class: Here is the core business logic code. Maybe one presenter will have many managers (depend on how complicate the view is). Often we get Context class through some injection framework such as Dagger.
Class MediaManagerImpl extends MediaManager {
// using Dagger for injection context if you want
#Inject
private Context context;
private MediaPlayer mediaPlayer;
// dagger solution
public MediaPlayerManagerImpl() {
this.mediaPlayer = new MediaPlayer(context);
}
// no dagger solution
public MediaPlayerManagerImpl(Context context) {
this.context = context;
this.mediaPlayer = new MediaPlayer(context);
}
public void playMedia() {
mediaPlayer.play();
}
public void stopMedia() {
mediaPlayer.stop();
}
}
Finally: Put those thing together in Activities, Fragments ... Here is the place you initialize view, manager and assign all to presenter.
public class MyActivity extends Activity {
Presenter presenter;
#Override
public void onCreate() {
super.onCreate();
IView view = new ViewImpl();
MediaManager manager = new MediaManagerImpl(this.getApplicationContext());
// or this. if you use Dagger
MediaManager manager = new MediaManagerImpl();
presenter = new PresenterImpl(view, manager);
}
#Override
public void onStop() {
super.onStop();
presenter.onStop();
}
}
You see that each presenter, model, view is wrapped by one interface. Those components will called through interface. This design will make your code more robust and easier for modifying later.
The good practice is to separate stuffs by feature (sometimes considered as module) and layer, not by their role. Reason: class/interface name already told that, e.g LoginView, LoginPresenter, LoginFragment, LoginActivity etc.

Is using a generic FindViewById a bad practice?

Situation
I have a BaseActivity from which I extend other activities. In the BaseActivity I have a findCastedViewById which basicaly casts the view and then returns it.
I do this because I, personaly, find it ugly casting the view all the time.
Question
I was wondering if there is any problem or cons that I could get from using this approach that anybody else using this method had.
Here is BaseActivity:
BaseActivity.java
public class BaseActivity extends Activity{
//Other stuff
private <E extends View> E findCastedViewById(int id){
return (E) findViewById(id);
}
//Other stuff
}
Consider using ButterKnife, it solves the problem of having to continuously cast your views and it saves you a lot of time
Once you go butterknife, there's no way back
Basically you annotate the View variables with
#FindView annotation and it will find the correct view for you when ButterKnife.bind(this) is called
Here's a small snippet of code where ButterKnife is used from the GitHub Page
class ExampleActivity extends Activity {
#FindView(R.id.user) EditText username;
#FindView(R.id.pass) EditText password;
#OnClick(R.id.submit) void submit() {
// TODO call server...
}
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
You can read more about ButterKnife from here
As lukaspp already noted, in SDK 26+ there is no need for vew casting.
They have implemented the same code as in the question, except it is now the default findViewById(). So yes OP, your code is good!
As explained in this answer, if you compileSdk is at least API 26, you don't need to cast the view anymore.
It's not only ugly but also an expensive operation if you repeat it constantly.
If you are sure you will always need that specific derived class, you can freely use it. However, I sometimes find I only need methods from the base class View, e.g. setVisibility(), in such occasions casting would be a waste.

How to redefine library functionality in project?

I have library project that implements most of application functionality, it's like a template of application. Every project that uses this library can redefine some resources, themes and so on. Main case is colors and urls to get information, that this applicatoin would show. But to redefine some code is more problematic. For example there is view that displays information from xml, but xml is different and I need to parse it differently. My current realization is like this.
public class MyView extends LinearLayout {
public setData(XmlData xml) {
//call to helpers static method to get parsed data from xml
ArrayList<Item> items = ParseHelper.getItems(xml);
}
}
So what I need is only change some logic inside ParseHelper. Now I see only one way, to redefine layout.xml to change MyView to ProjectMyView in which I'll change method setData to use another ParseHelper. But it's not good.
Maybe there is some patterns or another ways to solve this?
I think another way to use different classes from library or project is to use reflaction. For example packages in project is differs only by name (com.library.helpers and com.project.helpers) and check for class in project, if exists use it, if no use from library. But I think it will use many resources.
Can anyone share their experience?
You can make MyView as abstract, and let setData as an unimplemented method and forcing all subclasses to implement this method like this:
public abstract class MyAbstractView extends LinearLayout {
public abstract setData(XmlData xml);
}
Them, you library has an class that extends MyAbstractView with the most usual implementation like this:
public class MyView extends MyAbstractView {
public setData(XmlData xml) {
//call to helpers static method to get parsed data from xml
ArrayList<Item> items = ParseHelper.getItems(xml);
}
}
For those which want a different implementation, they just need to also extend MyAbstractView.
Finally, the caller or these objects just need to do something like this:
public void init(MyAbstractView arg, XmlData xml) {
arg.setData(xml);
}

Categories

Resources