Casting an EditText as a TextView - android

This is a question on performance, not on capability.
When I get a View from findViewById(...), and it's an EditText, but I don't need anything specific to EditText, should I cast the View to TextView or to EditText? TextView is a closer subclass to View, but EditText is what the View actually is.

IMHO, yes you can cast the edittext as textview. But I dont think there is any must-to-follow rule. You can perform operations either by casting to an edittext or a textview.
As you dont need the functionalities of edittext you can surely cast to textview

I can see no problem doing that. If you dont have any EditText specific task then go ahead and cast it to TextView.
P.S.:- I personally do that always.. :D

You should cast it to EditText because otherwise it will through exception and your application will be crashed. And BTW if you are doing such things in your code I don't think you are following good practices. So don't do that.

Here is what I think, for me clarity is more important than any kind of optimization you can think of making in your application. If you have an EditText then leave it as such, otherwise you are only adding confusion to the code.

At the end the object already exists, and it is an EditText, so you will be accessing an EditText, no matter what are you casting it. You are only casting it in order to use it's methods in code, but the object instance doesn't change. So regarding to the inheritance, for example, if the EditText has overriden a method of the TextView, if you cast the object to a TextView and use it, you will still be using the EditText method.
Anyway, the virtual machine should cache the results of method resolution, so it wil be no impact in further use.

Don't worry about premature optimizations. Is this actually a bottleneck in your code? It shouldn't be. Something like this is totally negligible. It shouldn't have any noticeable effect on performance because it's still the same underlying object, just the type is being handled differently.
edit: Also, you may not need the EditText features now, but in the future you may. Just leave it as an EditText to save yourself a maintenance headache in the future.

I've found that there IS benefit in casting an EditText to a TextView in java code.
Doing so can reduce the amount of imports in your code, which can (drastically) reduce the size of your compiled APK.
EditTexts and Buttons can more often than not be referenced as TextViews in code (Buttons could also just be a View). Almost any kind of ViewGroup can be referenced as just ViewGroup. AbsListView instead of ListView (API 11+). AbsSpinner instead of Spinner. Just to name a few easy ones. Doing so has reduced some of my apk sizes by 20-30%.

you can miss the cast. findViewById returns a View object. If you don't need anything specific, work with it as View object
EDIT:
since we are talking about performance, here is a quick test. The results looks a bit random to me, but it looks like there is no significant difference between EditText and TextView. Only the Reflection method is slower:
try {
String text = "";
String result = "";
//Reflection
Long time = SystemClock.elapsedRealtimeNanos();
View v = ctx.findViewById(R.id.hello_text);
Field f = v.getClass().getSuperclass().getDeclaredField("mText");
f.setAccessible(true);
text = f.get(v).toString();
time = SystemClock.elapsedRealtimeNanos() - time;
result = "via Reflection ("+time.toString()+" ms)";
//TextView
time = SystemClock.elapsedRealtimeNanos();
TextView t = (TextView)ctx.findViewById(R.id.hello_text);
text = t.getText().toString();
time = SystemClock.elapsedRealtimeNanos() - time;
result = result + "\nvia TextView ("+time.toString()+" ms)";
//EditText
time = SystemClock.elapsedRealtimeNanos();
EditText e = (EditText)ctx.findViewById(R.id.hello_text);
text = e.getText().toString();
time = SystemClock.elapsedRealtimeNanos() - time;
result = result + "\nvia EditText ("+time.toString()+" ms)";
Toast.makeText(ctx, result, Toast.LENGTH_SHORT).show();
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (NoSuchFieldException e) {
}
}

Related

findViewById doesn't seem to return correct view type?

Since we don't have to cast anymore, I expected findViewById to return the correct type, but it doesn't seem to do that. I'm obviously making a very simple mistake here, can you point it out?
I have a TextView's ID (since I created it dynamically) and want to change the text size of that item, this snippet works fine:
TextView tmpView = findViewById(chain.getIngredientNameId());
tmpView.setTextSize(8);
But this one doesn't:
findViewById(chain.getIngredientNameId()).setTextSize(8);
So I assume I have to case it to TextView but none of my attempts seems to work (using () or <>), what obvious thing am I missing?
You should cast it if do not save to variable.
TextView tmpView = findViewById(chain.getIngredientNameId()); //TextVeiw
((TextView) findViewById(chain.getIngredientNameId())).setTextSize(8); // TextVeiw
findViewById(chain.getIngredientNameId()); // View

Should View objects in Android have a variable assigned to them if that variable is never used?

Should View objects (TextViews, EditTexts, etc) have a variable assigned to them in onCreateView even if the variable will never be used? Is it acceptable to just assign the object's id to the Fragment using findViewById and access the object by using its id instead?
More detail:
I have several EditText values that are being initialized like this:
EditText role1 = (EditText) root.findViewById(R.id.role1_edit_text);
EditText role2 = (EditText) root.findViewById(R.id.role2_edit_text);
// Six other similar entries truncated
However, I'm not using the actual variables for role1 so Android Studio suggests removing the variable and writing it like this:
root.findViewById(R.id.role1_edit_text);
root.findViewById(R.id.role2_edit_text);
// Six other similar entries truncated
I have never seen Views assigned this way and just happened upon it because I'm referencing the EditText by their ids in my methods like this:
// Accessing View objects using view ids
editTextIds = new int[]{R.id.add_location_role1_edit_text, R.id.add_location_role2_edit_text,
R.id.add_location_role3_edit_text, R.id.add_location_role4_edit_text,
R.id.add_location_role5_edit_text, R.id.add_location_role6_edit_text,
R.id.add_location_role7_edit_text, R.id.add_location_title_edit_text};
private boolean allFieldsHaveInput() {
for (int id : editTextIds) {
EditText editText = (EditText) getView().findViewById(id);
if (isTextEmpty(editText)) {
return false;
}
}
return true;
}
I have several such methods that use loops to go through all the EditText ids so switching to variables would definitely make the code more verbose. I feel like EditText editText = (EditText) getView().findViewById(id); might be slightly more expensive computationally, but with roughly eight Views it seems like a small price to pay so I don't have to write the method like this:
// Accessing View objects using a variable
private EditText role1, role2, role3, role4, role5, role6, role7, role8;
private boolean allFieldsHaveInput() {
return !isTextEmpty(role1) && !isTextEmpty(role2) && !isTextEmpty(role3)
!isTextEmpty(role4) && !isTextEmpty(role5) &&
!isTextEmpty(role6) && !isTextEmpty(role7) &&
!isTextEmpty(role8);
}
I have never seen View objects assigned without a variable in open source code so I'm a little concerned if removing the variable is a bad idea (especially since I'm planning to put the code up on GitHub).
From a readability standpoint would it be better to just assign variables like role1 even if the code becomes slightly more verbose?
You have already captured some of the nuances in the approaches. Personally, I'd choose code readability over performance in most cases. In your case however, I strongly believe an array approach would fare much better.
private EditeText[] roleEditTexts = new EditText[8];
// assign
This would also avoid the costly calls to findViewById() (if you are calling that code snippet multiple times).

Xamarin: Setting text for a TextView programmatically

This is probably a mistake or lack of comprehension on my part, but I am quite confused right now. I'm trying to set a TextView in my Xamarin Android application programmatically. Here's my code:
TextView currentCharacterName =
FindViewById(Resource.Id.characterName);
currentCharacterName.SetText("test");
Unfortunately, this does not work, as I get the error "Argument 1: cannot convert from 'string' to 'int'". After reading in the available methods for SetText, I noticed the method I'm trying to call demands a ResId. I don't really understand why I would need a ResId to modify the text of a TextView.
I tried searching on Google for answers, and I came across this answer from 2014 that had the exact same problem as I do. The solution was to use the Text() method instead to set the TextView. Unfortunately, when I try this solution, I get the error "Non-invocable member 'TextView.Text' cannot be used like a method". When I try to check the Text method description, I see "string TextView {get/set} To be added."
Does this mean there's no implementation yet to set the text of a TextView? I am really reluctant to believe this, as it baffles me that such a big framework like Xamarin wouldn't even have get/set functions for something as simple as setting the text of TextView. I feel like there's a very simple solution for my problem, but I can't seem to find it.
TextView.SetText(X) allows you to set the text from a Resource id:
currentCharacterName.SetText(Resources.Id.MyString);
You are looking for the Text property:
currentCharacterName.Text = "test";
Xamarin: TextView class
Android.Widget.TextView.Text Property
Syntax:
public String Text { get; set; }
Test this code:
TextView currentCharacterName = FindViewById<TextView>(Resource.Id.characterName);
currentCharacterName.Text = "Your Text";

Simple TextView.setText causes 40% CPU Usage

Running my application causes ~40% CPU usage on my Phone:
final String position = String.format("%02d:%02d:%02d", time.getHours(), time.getMinutes(),
time.getSeconds());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
c.mTxtPosition.setText(position);
...
By commenting out the setText method the CPU Usage drops to the expected level of ~4%. The method is invoked every second and does refresh ImageViews, CustomViews ... without causing the same load excess.
Besides the CPU Usage dalvik constantly reports garbage collecting of about 10-1000 objects just by calling setText().
Creating a tracefile like this:
Debug.startMethodTracing("setText");
c.mTxtPosition.setText(position);
Debug.stopMethodTracing();
traceview lists the following methods as Top 5 by their respective exclusive CPU%:
ViewParent.invalidateChildInParent(16%)
View.requestLayout(11%)
ViewGroup.invalidateChild(9%)
TextView.setText(7%)
toplevel(6%)
Has anybody an explanation for this?
I noticed this myself a while ago, I think the problem is that every time you call setText, the size of the textbox can change, thus requiring the entire screen to go through relayout (expensive).
I haven't tried this myself yet, but if your textbox is simple and can be made to be a relatively fixed size, maybe try to subclass TextView and create a view that does not resize itself on setText, but rather just draws whatever it can into the existing area? That would save a lot of time.
Perhaps theres already a flag to setText that can make it do this, but I'm not aware of it, though I haven't searched closely.
In my case, I update a TextView from touch event, which cause a lot of updating The solution was to change the TextView layout_width & layout_height to fixed sized.
some possible improvements :
try using a handler which updates the textview every 0.5 seconds instead of a thread that does it.
make the runnable a final constant object instead of craeting a new one every second.
consider checking that the time has changed (newTimeInMs-LastPublishedTimeInMs>=1000) before telling the textview to update itself.
instead of String.format , try using StringBuilder . however , you won't enjoy the locale solution that the String.format gives (for example , for arabic digits) .
In my case it was this property of TextView:
android:ellipsize="marquee"
Removing it speeded up setting text.
If you look at the source code of setText method you can see that it does a lot of heavy lifting - there is measuring, drawing and object allocations, all of which run on the main thread.
You can use the new PrecomputedText API in order to do all of this on the background thread and make setText faster.
You can use the following working example using kotlin & coroutines
private fun TextView.setTextAsync(text: String) {
val textView = this
lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(text, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
For more details you can read an article about it on my blog
https://androidexplained.github.io/android/ui/2020/10/21/improving-textview-settext-performance.html

How to override the Android ListView fast scroller appearance

I'm trying to implement a ListView with a FastScroll mechanism which uses time rather than A-Z
Unfortunately I can't seem to find a way into the layout used by the FastScroller index - it seems determined to show a small black square with very large white text
I've looked at the source:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.0_r1/android/widget/FastScroller.java/
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/android/widget/AbsListView.java#AbsListView
Both of these seem to show the key fast scroll member variables are private, and that the resource id used is fixed:
mOverlayDrawable = res.getDrawable(com.android.internal.R.drawable.menu_submenu_background);
Is there any way to override this? Ideally I'm targeting 2.2. and above.
You can try with reflection with something like this:
try {
Field scrollerField = AbsListView.class.getDeclaredField("mFastScroller"); //java.lang.reflect.Field
scrollerField.setAccessible(true);
FastScroller instance = scrollerField.get(listViewInstance);
Field overlayField = instance.getClass().getDeclaredField("mOverlayDrawable");
overlayField.setAccessible(true);
overlayField.set(instance, yourValueHere);
} catch (Exception e) {
Log.d("Error", "Could not get fast scroller");
}
I just typed it out so it might or might not compile straight off the bat, but that's the idea. I didn't check if the fields were called the same in all of the versions, you might have to adjust.

Categories

Resources