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.
Related
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
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.
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);
}
I'm implementing AdapterView<ListAdapter> to produce an AbsListView-like class I can use with a CursorAdapter in a layout. I'm implementing this because I want to use the handy automatic data update behaviour CursorAdapter gives you; additionally, I can reuse the same adapter in a more conventional ListView elsewhere in my app.
I'm basing my class heavily on the Android source for AbsListView.
I'm having trouble with this though: in my own class, also extending AdapterView<ListAdapter>, I put this code:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
#Override
public void onChanged() {
super.onChanged();
//my update code here
}
#Override
public void onInvalidated() {
super.onInvalidated();
//my shutdown code here
}
}
Eclipse says "AdapterView.AdapterDataSetObserver cannot be resolved to a type".
I can't see that this is controlled by an import, and clearly since ListView can override this class, I would expect to be able to as well. Why isn't it visible?
The AdapterView.AdapterDataSetObserver is package private according to the javadoc. See the link here: http://www.androidjavadoc.com/1.0_r1_src/android/widget/AdapterView.html .
Thus it will not be visible outside of the package.
Could someone explain the best way to achieve the following, I think it's a fairly simple question but I'd like to know the best way, as it can be achieved several different ways.
Let's say I wanted a class which updated a TextView's text to "Test". Of course this isn't what my real problem is, but I'm trying to make this question as simple as possible. Let's also say this will accept any text view.
The class it calls does not inherit Activity.
The ways I see to do this are as follows, please explain why and why not these methods should or shouldn't be used.
Pass the TextView as a reference and update the text.
public class Test
public void updateText(TextView tv)
{
tv.setText("Test");
}
The other option is to pass the Activity, and call findViewById, but the problem here is if the ID does not match that that the Test class expects, the view will return null and won't update the TextView.
public class Test
public void updateText(Activity act)
{
TextView tv = (TextView) act.findViewById(R.id.i_must_exist);
tv.setText("Test");
}
Another choice would be to use getters/setters.
public class Test
private TextView mTvToUpdate;
public void setTextView(TextView tv)
{
mTvToUpdate = tv;
}
public void updateText(Activity act)
{
mTvToUpdate.setText("Test");
}
I guess the real question is it wrong to pass an objects reference as a parameter, is Activity the preferred way? Why? It is more likely to experience memory leaks, are both solutions OK? Is it down to preference?
Please don't reply with "Why would you want to do this?" as in this example I obviously wouldn't want to do what I am asking, but the question behind it still applies.
Thanks in advance.
I wouldn't go with the last way without a good reason because when you hold a reference to an UI element, you may leak memory more easily. When you hold a reference to a Widget, you hold a reference to the Activity either, so you have to make sure that you don't hold the reference to the Widget if the Activity is destroyed (possibly with a WeakReference). This is described in the article Avoiding Memory Leaks.
Besides that there's no real difference.
The object which "owns" the textfield should be the one who actually calls the setText method. So in most cases, that would be the activity. It seems like in this case, your Test class should only provide a method which will return the text you want to display as a string.
So something like
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
Test t = new Test(...);
String testText = t.getTestString();
TextView tv = (TextView)findViewById(R.id.TestTextView);
tv.setText(testText)
}
}
public class Test
{
public String getTestString()
{
return "Test"; // Probably would be more dynamic in your case.
}
}
This will abstract the data knowledge out to the Test class. All your activity needs to know is that someone else is providing a data string, and it can set the text itself.