Improving performance of instantiating and inflating views used with a ViewFlipper - java

I am using a ViewFlipper to display all the measurements the app has made (1 measurement = 1 page element). The problem is, the performance of initializing/reloading of the content of the ViewFlipper gets very bad if there are more then 50 elements. i.e., the app gets unresponsive for about 15 seconds after it has launched. I did some logging and found out, that more than 70% of the loading time is spent on View.inflate(ctx, R.layout.view_measurement, null);
From my understanding, inflate(...) does parse the layout xml file to a View so it can be used in Java. Since the layout xml file is everytime the same, it looks to me like there is the same heavy job being done for each element over and over again. I tried many things to change this and let this happen only one time, unfortunately without success. I also tried to use and tags in the xml also but always got errors.
I know that there are better ways rather than the ViewFlipper to do this job, but as I am in a hurry everything else works fine, I would like to keep this and find a fast solution.
MainActivity.java
...
ViewFlipper viewFlipper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewFlipper = (ViewFlipper)super.findViewById(R.id.measurementsViewFlipper);
int measurementsAmount = 100; //example
for(i=0; i<measurementsAmount; i++){
View measurementView = View.inflate(ctx, R.layout.view_measurement, null);
... fill the view ...
viewFlipper.add(measurementView);
}
...
}
view_measurement.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
...
</LinearLayout>
</RelativeLayout>

The only way to reduce the xml inflation time is to reduce the number of views used in the xml layout. For example, just by looking at he layout you posted, you could(and should) remove the root RelativeLayout if it's just wrapping the LinearLayout. Also, using ConstraintLayout might allow you further remove ViewGroups and reduce the nesting level and the view count.
But this will still not be ok because the real problem is you are inflating a lot of views upfront. In your example if the page layout contains just 5 views you'll create 500 views in total(100 x 5). That's a lot and this is why you need to have a view recycle mechanism so you don't need to create all views upfront, just a few for the current pages.
If you want ViewFlipper's behavior, the Android SDK has a component called AdapterViewFlipper which you should use instead. It just needs an additional adapter class, to which you'll pass the measurement data and in which you'll inflate the measurement layout.

Related

Restrict an activities SetContentView to display inside an existing container

I have a complex imported package that creates an activity and then uses SetContentView on an R.layout file. This layout essentially inflates and covers the screen, but I would like it to only "inflate" inside an existing view that is attached to MainActivity.
The activity class looks something like this
public class ExampleActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
setTheme(R.style.Theme_AppCompat_NoActionBar);
super.onCreate(savedInstanceState);
setContentView(R.layout.example_layout);
}
}
And I would call it from something like a clickListener with a function like this (where activity is MainActivity)
public static void startNewActivity(Activity activity) {
Intent navigationActivity = new Intent(activity, ExampleActivity.class);
activity.startActivity(navigationActivity);
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#AFAFB1"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="viewToFill">
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
So the question I'm asking is would it be possible to set an activity up so that when SetContentView is called a predefined view (attached to MainActivity) is filled instead of the entire screen. In simple terms, have SetContentView put R.layout.example_layout into the linearlayout viewToFill in activity_main.xml.
Any help would be appreciated, I have ideas of working around it, it's just that this solution is by far the least involved if it's possible, but I'm very aware it may not be
edit: made it clearer that the target layout is attached to a different activity than the new layout created by SetContentView
As per the official documentation in Android application components the Activity covers the entire screen / window which is the correct and desired behaviour.
If you want to start a view that only covers the partial screen then you should use a Fragment. A Fragment represents a reusable portion of your app's UI and provides the modularity that you want.
Just define Theme.AppCompat.NoActionBar for that Activity in AndroidManifest.xml.
The essence of this question was "can I have 2 activities running on the same screen" and the answer to that question is no, it is not currently possible (for the most part).
I say for the most part because I actually stumbled across exactly what I was looking for at https://developer.android.com/guide/topics/large-screens/activity-embedding, activity embedding on a phone screen is what I was looking for which is the ability to run 2 activities with ui elements on the same screen. However as the docs say this is an experimental API that is only for newer large screen devices, so it is still very early days for this technology and there's a good chance that it never comes to small screen devices.
I came across fragments a lot both here and researching this question, but a fragments intention is to give a single activity reusable portions of the UI and to provide lifecycles to those portions, among a host of other features.
So with the complex package I have, I am going to find what the activity lifecycle provides to it, and find another way of mimicking it without it having to start a new activity and then I'll replace the setcontentview with a regular inflation pointed somewhere in activity_main.xml

Minimize number of layouts in custom view Android

I'm trying to create a pretty simple custom view. However, I need to duplicate this view in my layout a few hundred times which makes the initial drawing very slow (a few seconds). I'm not sure what the best way to approach this is, but I read that having extraneous layouts can slow down the drawing significantly.
When I first designed the custom view, had it extend a LinearLayout and then inflated my view's xml into it. But actually, I feel like the LinearLayout is a waste. Is there a way to cut it out?
Alternatively, is there a more efficient way to create the same view many times?
EDIT
To be clear, the custom views are generated at runtime and the exact number is determined then. Also, these views are embedded in a more complicated layout
Below is my Java class:
public class MyView extends LinearLayout {
public TOCNumBox(Context context) {
super(context);
inflate(context, R.layout.myview, this);
}
}
And here's the XML:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="234">
</TextView>
Are all 100 views being shown at the same time? If not, such as if they are in a scrollable listview, try using a recyclerview to improve performance. Alternatively, you could put all 100 TextViews in a single layout if they do need to be shown at the same time.

Xml layout files not working with Textview

I am a beginner when it comes to Android developing and I am trying to write a search application that takes in a string and outputs the text messages that contain that string. My problem is with trying to format the text output on the second screen. I have the text displaying correctly using the code:
TextView textView = new TextView(this);
textView.setTextSize(20);
// Set the text view as the activity layout
setContentView(textView);
..... (find string matches)
textView.append(msg);
However, when I go and edit the .xml file for that file nothing changes (I have tried adding bold, adding a starting text etc.). I copied my TextView xml block to my activity_main.xml and it displayed a bold "hello world" on the first screen and not the second even when the TextView section was an exact copy. What is it that I am missing in the second that I am doing in the first? Is my problem in my TextView declaration? My end goal is to display many text message matches and force them to fit on the screen horizontally and allow the user to scroll vertically, is changing the second xml file the wrong way to do it?
Thanks
It appears you're mixing up the two different ways to 'create' views:
declaring them in xml and inflating them
instantiating them programmatically
The first approach is where you use an xml file to (statically) declare the layout. I.e. your layout file may be named activity_layout.xml and include the following entry that defines a TextView with id textview1.
<TextView android:id="#+id/textview1" ... />
In order to use a view in such a layout definition, you need to 'inflate' it. A common place for this is in an Activity. However, you first need to tell Android what layout to inflate from:
// inflate from 'activity_layout.xml'
setContentView(R.layout.activity_layout);
// inflate the TextView with id 'textview1'
TextView textview = (TextView) findViewById(R.id.textview1);
Now, the second approach is to (dynamically) instantiate views in code. This does not require a layout file. Typically you'll be able to differentiate this from inflating because the presence of the 'new' keyword. I.e.
// not xml definition required; all code
TextView textview = new TextView(getActivity());
I hope you're seeing where this is going? Your code snippet suggest you're using the second approach, while your explanation mentions you're modifying a layout file in the hope to see changes. Basically you're changing a layout file you're not currently using, hence you don't see anything happen.
Either change your code to use the views in the layout, or get rid of the layout and do everything in code. Usually the first approach is a more flexible one and easier to use; e.g. you'll be able to benefit from previews in Eclipse and it'll be much easier to manage if the layout gets more complex.
Your problem is this:
// Set the text view as the activity layout
setContentView(textView);
If you set setContentView like this , no matter how you change your xml file you still get the same thing. A text with size 20.
Try to create a new layout and add the Textview on it.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/TextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
Change your code as follow:
setContentView(R.layout.new_layout);
TextView textView = (TextView)findViewById(R.id.TextView1);
..... (find string matches)
textView.append(msg);

Can multiple android views be stored in the same .xml file without belonging to the same parent?

I have an android project that has several small views which I need to instantiate at runtime. I haven't been able to figure out a way to store all of these related views in a single xml file and I now there are going to be many of these xml files. I was just wondering if there is any way to have them all in a single file, but not belonging to some parent ViewGroup.
The layout folder in android kinda sucks since there's no way to make subfolders, everything is just piled into the same place, ugh.
I hope someone can tell me of a better way of organizing these things.
If I understand you correctly you want several views meged onto one screen or merged into one xml file. You can include other xml's into one.
The articles showed you how to use the tag in XML layouts,
to reuse and share your layout code. This article explains the tag and how it complements the tag.
http://developer.android.com/resources/articles/layout-tricks-merge.html
Also, this video might help (about 19 minutes in). Shows you how to extract a current layout and be able to include it in others.
a couple things:
Yes, the layout folder is a pain. I use strict naming conventions to make it bearable, and in eclipse use the shortcut ctrl + shift + r to quickly find the layout I am looking for. Try naming your layouts after your activity: activity1_menu_overlay and activity1_main. With the above shortcut, just type out Activity1 and it will only show you the relevant layouts.
And if that doesn't work, you can try wrapping all your views in LinearLayouts and using view.setVisibility(View.Gone); or view.setVisibility(View.Visible); to show/hide the appropriate views.
Here is an example of that second one, because it's tough to explain.
one XML file:
<LinearLayout>
<LinearLayout ... android:visibility="visible">
<copy/paste of view 1>
</Linearlayout>
<Linearlayout ... android:visibility="gone">
<copy/paste of view 2>
</Linearlayout>
<Linearlayout ... android:visibility="gone">
<copy/paste of view 3>
</Linearlayout>
<Linearlayout ... android:visibility="gone">
<copy/paste of view etc.>
</Linearlayout>
</Linearlayout>
keep in mind this approach will require you to define a reference to each "child" LinearLayout view in your activity, so you can call setVisiblity appropriately.
This approach works well for animations, and I would only use it for 2 or 3 possible views in one xml file.

Load partial XML layout into ready layout for android

I am attempting to create a program with a set of dynamically loaded layout "pages". I have the base layout created with a minimal default skeleton of the Views common to each page. For each page I was going to hard code all the views to be swapped (:facepalm). My next thought was to create a text file and put the necessary data in a well formatted design. Only then I realized that's exactly what the XML files are. So what I would like to do is create an XML file of the pages with the data exactly as it would appear in the original layout file. Then as each page is loaded (possibly unload another page), pull the XML data for that page and insert it into the current base layout structure.
My page data
<?xml version="1.0" encoding="utf-8"?>
<pageList>
<page:1>
<Button .../>
<EditText .../>
</page>
<page:2>
...
</pagelist>
The base XML layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:weightSum="1">
<LinearLayout android:id="#+id/linearLayout1"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="1.00" android:weightSum="1">
<EditText android:id="#+id/editText1" android:inputType="textMultiLine"
android:layout_width="0dp" android:layout_weight="0.30"
android:layout_height="match_parent"></EditText>
<RelativeLayout android:layout_width="0dp"
android:id="#+id/orientationLayout" android:layout_weight="0.70"
android:layout_height="match_parent">
<dynamically insert my PAGE here>
I am new to Android programming and have only a little experience in generating interfaces from XML. In programming C# and XML for a previous job, I would have to pull the data directly from an embedded XML file and use that to create the Button or TextBox myself. Do I need to do similar in this case or is there a way to automatically load it?
I have looked this up for a while and most answers I found on this site and other places are from months ago or longer. Those answers tend to range from IMPOSSIBLE to do it yourself. I'm hoping maybe in the past few months there might have been a change to the system I have yet to find.
You are probably looking for a ViewStub.
A ViewStub is an invisible, zero-sized View that can be used to lazily
inflate layout resources at runtime. When a ViewStub is made visible,
or when inflate() is invoked, the layout resource is inflated. The
ViewStub then replaces itself in its parent with the inflated View or
Views. [...]
You can use this like a normal view in your layout and use findViewById() to reference it in code. After that use ViewStub.setLayoutResource() to set layout that you want to show and call ViewStub.inflate() to show it. This way you can write a normal XML
layout file for every (sub-)page you need.
Also see this article.
Edit: Or probably not, I have to mention that the stub gets removed from the view hierachy after inflating. So, depends on your actual use case if this is helpful.

Categories