In my Player activity I am trying to add a view from a different XML by calling addView:
view = (ViewGroup)findViewById(R.id.player_ConstraintLayout);
notifierView = LayoutInflater.from(view.getRootView().getContext())
.inflate(R.layout.notifier, null);
view.addView(notifierView);
// view.invalidate();
The Player activity comes with this associated XML file:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/player_ConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.Player">
...
</android.support.constraint.ConstraintLayout>
And the one I want to add is notifier.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000"
android:translationZ="9dp"
android:elevation="9dp">
</android.support.constraint.ConstraintLayout>
I have debugged the app and by inspecting variable view I can confirm that the notifierView has been added and is in the children list of the view. However the notifierView is not shown, even I have expected it to be of full screen size and red. When I inspected notifierView in debugger, it showed its mTop, mBottom, mLeft, and mRight variables being all 0. I assuming it means that the
android:layout_width="match_parent"
android:layout_height="match_parent"
have been ignored? Or is there some other reason why I don't see this notifierView at all?
EDIT/UPDATE:
Let me show you more of the code, as the previous above is a little simplified one.
In Player activity I have:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
notifier = new Notifier((ViewGroup)findViewById(R.id.player_ConstraintLayout));
...
}
and in Notifier.java I have
Notifier(ViewGroup view) {
handler = new android.os.Handler();
LayoutInflater inflater = LayoutInflater.from(view.getRootView().getContext());
notifierView = inflater.inflate(R.layout.notifier, view, false);
}
ConstraintLayout doesn't actually support match_parent for its children. You have to use this for "match parent" width:
android:layout_width="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
And this for "match parent" height:
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
Additionally, your LayoutInflater.inflate() call should be changed. When you pass null as the second argument, you don't give the system enough information to understand how to parse layout_ attributes on the inflated view; you should pass a real parent (in this case, view). However, if you change null to view, the return value of the inflate() call will change, so you have to add an additional param (attachToRoot) to make sure the rest of the semantics don't change.
LayoutInflater inflater = LayoutInflater.from(view.getRootView().getContext());
notifierView = inflater.inflate(R.layout.notifier, view, false);
(Posted solution on behalf of the question author).
Invalidate Caches / Restart helped. Android Studio sometime gets things wrong.
Related
I'm building an app which has 2 fragment containers and takes screenshot feature.
the problem is whenever one of the 2 fragment containers update with another fragment(replaced) and I'm taking a screenshot it shows the previous fragments and not the updated ones.
I'm using getView().getRootView() within another fragment in the activity to take the screenshot.
any advice on why it happens?
Please be more elaborate about the problem or try to add the code.
If you are not using the FrameLayout, please use this.
Code for Adding FrameLayout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/your_placeholder"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></FrameLayout>
</android.support.constraint.ConstraintLayout>
To add or replace new Fragments use the FragmentTransaction.
// Code inside the onCreate method
getSupportFragmentManager().beginTransaction()
.replace(R.id.your_placeholder, new TheOtherFragment())
.commit();
To take screen Shot :
View view = findViewById(R.id.your_placeholder);
view.setDrawingCacheEnabled(true);
view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap screenshot = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
To take screen Shot :
View view = findViewById(R.id.id_of_constraintLayout);
// Here you assign the view Variable to the id of the View hosting all your Views.
view.setDrawingCacheEnabled(true);
view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap screenshot = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
Since I began to create the inheritors from Android Views, I frequently see the error Binary XML file line XX: Error inflating class. In this special case it will be the BottomSheet.
public class MyBottomSheet extends TableLayout {
BottomSheetBehavior myBottomSheetBehavior;
ImageView testImageView;
public MyBottomSheet(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
testImageView = (ImageView) findViewById(R.id.imageView); // still OK...
myBottomSheetBehavior = BottomSheetBehavior.from(this); // crashes Here
testImageView.setOnClickListener(onClickListener); // or here if to remove the previous row
}
// ...
}
My app has multiple tabs with individual fragments. The BottomSheet requires only in one of them, however if I dont't include the BottomSheet in the MainActivity markup, it will not move. So the code is:
activity_main.xml
<?xml version="1.0"encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- ... -->
<include layout="#layout/my_bottom_sheet"/>
</android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.DrawerLayout>
my_bottom_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.bottomsheettest.widgets.MyBottomSheet
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/myBottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*"
tools:context="com.example.bottomsheettest.MainActivityFragment"
tools:showIn="#layout/activity_main">
<TableRow>
<ImageView
android:id="#+id/imageView"
android:src="#mipmap/ic_launcher"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</TableRow>
</com.example.bottomsheettest.widgets.MyBottomSheet>
MainActivityFragment.java
public class MainActivityFragment extends Fragment {
private static final int LAYOUT = R.layout.fragment_main;
private MainViewFAB fab;
private MyBottomSheet addNewItemToInboxBottomSheet;
public MainActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
MainActivity activity = (MainActivity)getActivity();
fab = activity.getMainViewFab();
addNewItemToInboxBottomSheet = (MyBottomSheet) activity.findViewById(R.id.myBottomSheet);
return inflater.inflate(LAYOUT, container, false);
}
}
I supposeI missed something important about Android Views inheritance. But this case especially strange. In the MyBottomSheet class, there are no problem when getting the ImageView from the BottomSheet. Consequently, the access to BottomSheet from the respective class is available. However, if try to manipulate somethig related with BottomSheet, the exception will be.
I share the source code (.zip) created in the Android Studio during accept the some answer. There are no tabs in shared app, however I kept the fragment structure because it exists in the original app.
When creating a View subclass that is meant to be inflated from a layout, there are several things that won't be available in the constructor at runtime.
For one, child Views of your custom View defined in the layout will not have been inflated and attached yet, so any attempt to get references to them with findViewById() will return null there. Instead, this should be done no earlier than onFinishInflate(), and moving your ImageView initialization to that method will solve that particular error.
Also, your custom View itself will not have had its own LayoutParams set yet in the constructor. This is causing the issue with the BottomSheetBehavior.from() call there, since that method simply retrieves the Behavior from the LayoutParams constructed during inflation from layout attributes on the View. In the onAttachedToWindow() method, the LayoutParams will have been set, and you can make the BottomSheetBehavior.from() call there.
Lastly, I would just mention that an InflateException is thrown whenever anything goes wrong with inflation. The root cause of the failure will be listed further on in the stack trace, and you should look there to find the more relevant details.
In AndroidStudio, I selected "New->Fragment->Fragment (List)" to create a list of items. It works fine and displays all the items; however, I would like to have a title at the top.
If I add another TextView to the "fragment_item_list.xml" file, then the title appears in all the items in the list. From other questions I figured that if I create another LinearLayout xml layout file (something like "list_header.xml") and put a TextView in it, I can use a layout inflater to inflate this layout in the fragment activity, e.g. in onCreateView,
View header = inflater.inflate(R.layout.list_header,container,false);
but then I don't know what to do with header to make it display somewhere in the fragment. I can add it to the original activity by calling container.addView(header); but how can I add it to the fragment?
I don't want to change the ActionBar title, firstly just for aesthetics, and secondly because the fragment covers it up. Ideally there would be a header like in an alert dialog.
Let me try to understand you. You want to add a title to your fragment list (like an header), not in the ActionBar. Right?
Looking the fragment_item_list.xml we have this: (I follow your steps: "New->Fragment->Fragment (List)")
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:listitem="#layout/fragment_item" />
The RecyclerView is a container used for populate a list, in other words, the RecyclerView (or ListView) contain all the list only. So, you have to add another container (like LinearLayout, RelativeLayout, etc.) as root of the layout to add more views to the layout. Example using the LinearLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World! I'm a header!"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:name="com.veroapps.myapplication.ItemFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:listitem="#layout/fragment_item" />
</LinearLayout>
With this you will have a list with a title at the top. Now, you can manage the title TextView in the ItemFragment(name of the fragment generated by Android Studio). And of course there are more ways to do that, but this is an easy one.
Hope this help you.
In your Fragment java class, there should be a method called onCreateView this is where you set the layout for your Fragment (and where the list is added). It will look something like this:
public static class ExampleFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
In this Fragment the layout is inflated by this code inflater.inflate(R.layout.example_fragment, container, false); and this means there's is an xml file defining the layout called example_fragment.xml.
Your code will be the same, when you find this XML file. You can add other views inside it.
You will want to add another TextView above the RecyclerView or ListView already in that file.
Something like this:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
...
android:text="YOUR TITLE" />
<RecyclerView
... />
</LinearLayout>
I discovered very strange behavior of CardView's shadow.
I just start to develop an app, and there is absolutely no any business-logic or something like that. But anyway...
I just add few views to my fragments layout, and discover, that after every screen rotation cardview's shadow getting darker. After 5-6 rotations it already looks like completely black. I guess, the problem can be somewhere in canvas, but no idea where and why - i even didn't start customize anything.
I hope somebody already solve similar problem with cardview, and now can share that experience.
Thank you!
Here is screenshots, code, dependencies and xml:
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:iot="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/common_gap"
iot:cardElevation="10dp"
iot:cardUseCompatPadding="true">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<DatePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="#dimen/common_gap"
android:calendarViewShown="true"
android:spinnersShown="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingLeft="#dimen/common_gap"
android:paddingRight="#dimen/common_gap"
android:text="#string/choose_date_confirm_button"
android:textColor="#color/colorPrimary" />
</LinearLayout>
</ScrollView>
</android.support.v7.widget.CardView>
</LinearLayout>
Java (Nothing else in fragment class)
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.date_choose_fragment, container, false);
return rootView;
}
dependencies
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:cardview-v7:25.0.1'
Screenshots
This is most likely not due to any bug with CardView or its shadow draw. Rather, this is likely the result of multiple Fragments with transparent backgrounds, stacked one atop another, and the darkening shadow is the additive effect of the shadows' translucency.
Active Fragment instances are automatically recreated when the Activity is, which happens by default upon an orientation change. If you're unconditionally adding a dynamic Fragment instance, for example, in the Activity's onCreate() method, that one will be added along with any instances recreated from the Activity's prior state. Effectively, each time you rotate your device, you're adding one more Fragment to the stack, and the shadow gets a little bit darker.
This can be demonstrated with just a simple TextView, to show that it's not an issue specific to CardView.
fragment.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Hello world!"
android:textSize="60sp"
android:textStyle="bold"
android:textColor="#fffbc02d"
android:shadowColor="#70707070"
android:shadowDx="10"
android:shadowDy="10"
android:shadowRadius="10" />
In the example Activity's onCreate() method, we add a dynamically created Fragment with the given layout, as described above.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction()
.add(android.R.id.content, new MainFragment()).commit();
}
public static class MainFragment extends Fragment {
public MainFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
}
}
This image shows successive captures after having rotated the device, twice for each step.
If you were to put a log print in the Fragment's onCreateView() method, you would see that an additional instance is created upon each rotation. At the end of that sequence, we have eleven Fragments in play.
One way to prevent this is to check if the Fragment is already attached to the FragmentManager before creating and adding a new one, which we can do by adding a tag in the FragmentTransaction. For example:
if (getFragmentManager().findFragmentByTag("main") == null) {
getFragmentManager().beginTransaction()
.add(android.R.id.content, new MainFragment(), "main").commit();
}
Alternatively, if you don't need to dynamically transact the Fragment after startup, you could instead statically define it in the layout, and the FragmentManager will handle the check and transaction itself.
<fragment
android:id="#+id/main_fragment"
android:name="com.mycompany.myapp.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
I'm following the android training in android's website. I am stuck at some point in fragments. The problem is that I can't get the reference to my textview in a fragment.
TextView view = (TextView) getActivity().findViewById(R.id.article_s);
I have two fragments in one activity. One is a plain simple ListFragment and the other is a plain simple TextView. Here is fragments and activity layout xml files.
ArticleFragment:
<!-- This is fragment_article.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"
android:textSize="18sp"
android:id="#+id/article_s"/>
MainActivity (My container to all fragments)
<!-- This is activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_height="match_parent"
android:name="com.example.bora.fragmentsmyway.HeadlinesFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp" />
<fragment
android:layout_height="match_parent"
android:name="com.example.bora.fragmentsmyway.ArticleFragment"
android:layout_weight="2"
android:id="#+id/article_fragment"
android:layout_width="0dp" />
</LinearLayout>
There is a public method in my ArticleFragment which takes a string to update the content of the textview of itself:
public void updateView(String article) {
TextView view = (TextView) getActivity().findViewById(R.id.article_s);
try{
view.setText(article);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
After all the views are created, I call the updateView method from my MainActivity. But I get a NullPointerException when I try to find the article TextView. I have tried cleaning, changing the TextView's resource id.
However I have found a solution for this problem. I don't know why but it seems to work when I wrap TextView with a LinearLayout in the fragment_article.xml:
<LinearLayout
xmlns:android="..."
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"
android:textSize="18sp"
android:id="#+id/article_s" />
</LinearLayout>
But I am wondering this: How can, in the tutorial samples, above code work and can't work when I wrote? I checked every code letter by letter but there is no difference between mine ant theirs. Is it because of the versions?
I'm working with the API 23 which is marshmallow.
Make sure you fragment is attached to the activity before calling updateView
this
TextView view = (TextView) getActivity().findViewById(R.id.article_s);
will give you NPE cause it does not belong to the activity
The views belong to the Fragments in which case you do
View view = inflater.inflate(R.layout.fragment_layout, container, false);
TextView textView = (TextView) view.findViewById(R.id.article_s);
Specifically, the fragment can access the Activity instance with getActivity() and easily perform tasks such as find a view in the activity layout:
Read Communicating with activity # https://developer.android.com/guide/components/fragments.html
So you use getActivity to find views that belong to activity which is not the case in your code.
Edit -
Wrapping it with a LinearLayout should not make any difference as it is just a viewgroup. If you just need a single textview no need for LinearLayout.
Read the accepted answer at : Difference between getActivity() and view in Fragment
use this code : you given wrong id to on TextView
TextView view = (TextView) getActivity().findViewById(R.id.article_s);
This is return NPE because your try to find the wrong id.
There are two different way you can resolve issue.
1) First one
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"
android:textSize="18sp"
android:id="#+id/article"/>
TextView view = (TextView) getActivity().findViewById(R.id.article);
Another way keep your XML as it is & try to do below change in java code.
2) Second One
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some Text"
android:textSize="18sp"
android:id="#+id/article_s"/>
TextView view = (TextView) getActivity().findViewById(R.id.article_s);
It doesn't work like this inside a fragment class by the way you should upload your main activity codes or your fragment class if you are using it inside the main activity, you don't need to use getActivity()
if it is inside a fragment class you need to apply it like this:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.your_fragment_layout_xml, container, false);
TextView AboutUs = (TextView) rootView.findViewById(R.id.id_of_textview);