Restrict an activities SetContentView to display inside an existing container - java

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

Related

ViewModel Doesn't cache data for RecyclerView and saving ListState doesn't work

I want the RecyclerView to retain it's position on device rotations.
I have 4 classes (2 Activities and 2 ViewModels) which are called MainActivity, FavouritesActivity. PLUS MainViewModel, FavouritesViewModel which extends AndroidViewModel. All things with FavouriteAvtivity and FavouriteViewModel works perfectly fine. Meanwhile, the other two are implemented the same way but when I rotate the device my List's scroll position resets, and I have literally tried everything I found regarding saving the list state in onSaveInstanceState() and onRestoreInstanceState().
Here's my application repo on GitHub: PopularMovies
I found the issue cause why it's happening for MainActivity only even though implementations are same. The issue is with your layout movie_list_item, you have to specify constant height otherwise layout gets remeasure and list scrolled up on device rotate. to prevent that use the below modified layout.
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/iv_movie_thumbnail"
android:layout_width="184dp"
android:layout_height="180dp" // don't use wrap_content, can use any height you want but make it that fix
android:contentDescription="#string/movie_thumbnail"/>

Avoid duplicating UI codes

I want to write new UI class which contain Check box and Label. At the moment there is existing UI class with same elements but their element descriptions are different. But data model for both UIs are going to be same.
So is it good practice to keep separate UI classes (by duplicating GridBagConstraints and other stuffs) for each or move common code in to abstract layer and derive description of the UI elements in the implementation level?
There are some other things that you can try, so you can avoid duplicating UI code, I'll give you 2 examples:
You can use the tag to bring the UI code inside another layout file and show it in you current layout, at the end you will be able to call it directly from your current Activity or Fragment in the same way you do with the other elements at the root of your Fragment or Activity class.
Re-using layouts
First layout file named: include_example_layout.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/textView_includeExampleLayout_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<CheckBox
android:id="#+id/checkBox_includeExampleLayout_valid"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
Second layout file named: activity_main.xml
<LinearLayout>
<include layout="#layout/toolbar_search_algolia"/>
</LinearLayout>
And from the MainActivity file you will be able to call the ids of this file include_example_layout as if they were declared directly in your activity_main file, so you will be able to reuse it.
The second one is creating a View element, this has an added advantage to the first method but is a little more complex, the thing is that you will be able to move some UI logic to the class of the new View element, for example if you want to disable the checkbox when something is happening with the information you can move thtat logic to the new view class.
Custom View
I'll no write a complete tutorial about this because it is a extense topic but I'll left some examples in other places that will help you understand the most basic concepts, there are two way in wich you can build CustomViews the first one is extending the View class that will force you to create it from scratch, but you can also extend other Views like a LinearLayout and this will help you to get started with the concept of a CustomView (is not recommended in every case, it can slow down your UI if you don't use it wisely)
Example extending LinearLayout
Example extending View

Google Maps which extends Base Activity can't inflate map fragment

I believe it is possible to setContentView() to a map fragment while extending AppCompatActivity. However I need to extend a Base Activity which extends AppCompatActivity. This means that I need to inflate my map fragment with a container.
Unfortunately it throws this exception every time:
Caused by: android.view.InflateException: Binary XML file line #7: Class is not a View com.google.android.gms.maps.SupportMapFragment
Do I have to choose or is there a way to work around this problem?
I am creating an app that needs to access a map while still keep certain functions like the navigation drawer hence I have a Base Activity.
Since you didn't provided code I am just guessing here, but from the error you posted you are probably doing this in your XML:
<com.google.android.gms.maps.SupportMapFragment
android:layout_width="match_parent"
android:layout_height="match_parent"/>
SupportMapFragment isn't a view, it is fragment, so you should place it using the fragment tag, as following:
<fragment
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
For anyone who still has this problem after migrating to SDK v28, here's something that might help: https://stackoverflow.com/a/34798398/4483494
Essentially, if you have already defined in your application manifest the key "com.google.android.geo.API_KEY", you should delete the key "com.google.android.maps.v2.API_KEY".

Improving performance of instantiating and inflating views used with a ViewFlipper

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.

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.

Categories