Android View type at runtime - java

I'm creating an app which used a GridView to display a set of items. Each of these items is defined as follows:
<?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">
<com.example.myapp.customviews.Tile
android:layout_width="wrap_content"
android:layout_weight="1"
android:scaleType="fitCenter"
android:layout_height="match_parent"
android:id="#+id/tile" />
</LinearLayout>
In my OnItemClickHandler I want to check if the type of the View with id R.id.tile (My custom View class mentioned above) is com.example.myapp.customviews.EmptyTile. This class is a child of the Tile class.
Currently I'm checking if the class is an EmptyTile as follows:
LinearLayout tileLayout = (LinearLayout) gridView.getChildAt(position);
if (EmptyTile.class.isInstance(tileLayout.findViewById(R.id.tile))) {
Log.d("myappDebug", "Correct instance!");
}
I have tried checking using instanceof and tileLayout.findViewById(position).getClass() == EmptyTile.class but these also don't work. At runtime, my view is just a Tile View and not an EmptyTile View.
How can I check if my View is actually an instance of the EmptyTile class?

instanceof must work. But, in your XML, you define the view to be of type com.example.myapp.customviews.Tile, so why do you expect it to be an EmptyTile?
If you use EmptyTile (with the correct package of course) in the XML, the instanceof check and the other options will work.

Related

Get child of ScrollView programmatically

I have made a custom ScrollView which has a single child inside it (a layout). Finding the child by ID using findViewByid in the CustomScrollView.java causes the app to crash, so is it possible to find it in a simple manner similar to this (which detects the custom scrollview) or getParent() (which detects the parent layout that both the scrollview and the child is inside of)?
I'm trying to apply requestDisallowInterceptTouchEvent(true); to the child of the custom ScrollView.
xml:
<?xml version="1.0" encoding="utf-8"?>
<com.example.test4.CustomScrollView 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/dynamic_status_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
tools:context=".MainActivity">
<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/dynamic_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity">
</android.support.constraint.ConstraintLayout>
</com.example.test4.CustomScrollView>
Code inside CustomScrollView.java where I want to add requestDisallowInterceptTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Add here
return super.onTouchEvent(ev);
}
Problem:
In short, I want to find the child of the custom ScrollView, the layout with the id dynamic_status, without using findViewById.
You should be able to do it like this:
#Override
public boolean onTouchEvent(MotionEvent ev) {
((ConstraintLayout)getChildAt(0)).requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(ev);
}
You can always access child Views (Views nested deeper than your current View) with getChildAt. Since requestDisallowInterceptTouchEvent is only available to ViewGroups you have to cast it either directly to your ConstraintLayout or - for better reuseability - to a ViewGroup.
Please note that I had not included any checks if the child is available.
This code is also the same for Kotlin and Java. It does not make any difference (syntactically yes, but the concept is the same).

Custom wrapping view in Android using xml layouts

I want to create a wrapper element that can be reusable throughout the whole app, because it's the base of each screen.
This wrapper should be possible to add to layout editor in Android Studio and needs to be able to hold any inner elements inserted in editor.
But these elements have to be inserted inside the layout under header with image and only inside a specified area defined in wrapper xml layout (innerContainer).
This is the layout of wrapper:
<ScrollView
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">
<android.support.constraint.ConstraintLayout
...
android:background="#drawable/frame_container_layout"
tools:context=...>
<include
android:id="#+id/cv_header"
layout="#layout/cv_header"
...>
</include>
<android.support.constraint.ConstraintLayout
android:id="#+id/innerContainer"
.../>
</android.support.constraint.ConstraintLayout>
What I need is to use this layout in custom view in a way, that when it's used somewhere, the elements will be only inserted into innerContainer.
Child XML:
<...custom_views.WrapperFragmentView
...
app:exampleColor="#33b5e5"
app:exampleDimension="24sp"
app:exampleDrawable="#android:drawable/ic_menu_add"
app:exampleString="Hello, WrapperFragmentView">
<TextView
android:id="#+id/textView1"
...
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</...custom_views.WrapperFragmentView>
The child is now inserted over the wrapper layout, which is what I don't want. It's not inside innerContainer.
In kotlin file I defined
class WrapperFragmentView : android.support.constraint.ConstraintLayout {
...
private fun init(attrs: AttributeSet?, defStyle: Int) {
var inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
inflater.inflate(R.layout.cv_wrapper_fragment, this, true)
findViewById<android.support.constraint.ConstraintLayout>(R.id.innerContainer)
The innerContainer is the view I want to have as a root element for the children that are going to use it. Not the whole layout cv_wrapper_fragment.
How to set this in the WrapperFragmentView?
Or, if the whole concept is wrong and if there is an easier approach how to work with reusable wrapping elements I am all for that.

View added from XML file is not shown in its parent view

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.

How to add a title to a fragment in Android

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>

How do I set a background image for each item of ListView in Android?

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dip"
android:src="#drawable/icon" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="fill_parent">
<TextView
android:id="#+id/toptext"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:gravity="center_vertical"
android:textStyle="bold"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:id="#+id/bottomtext"
android:singleLine="false"
/>
</LinearLayout>
</LinearLayout>
This is my current row. If I created a .JPEG, and I want that to be for each item...how would I change this .xml file? Where would I put the image? In Assets?
If you want to have individual backgrounds for each list item, you must declare your own custom Adapter.
Derive it from BaseAdapter and the most important part to implement is the getView(int, View, ViewGroup) method.
You must understand how android re-uses already existing list item view elements when you scroll through a list. That means: at any moment in time, there will be only as many views generated as there can be seen on screen simultaneously.
This optimal strategy of not generating too many views altogether leads to the problem that you will have to set the background for each list item according to their position currenty needed when getView is called. If you would try to set the background statically only when generating the view, it will re-appear again possibly attached to the wrong element.
The getView method either brings a "convertView" as its second parameter or not (null). If your method is called with a convertView set to something, that means: "re-use this view for the item required right now".
The technique used here is nicely described within the API demos (section Lists) and there's also a video blog for that.
Here's how it might be done:
public class MyAdapter extends BaseAdapter {
public View getView(int position, View convertView, ViewGroup parent) {
// position is the element's id to use
// convertView is either null -> create a new view for this element!
// or not null -> re-use this given view for element!
// parent is the listview all the elements are in
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
// here you must do whatever is needed to populate the elements of your
// list element layout
...
} else {
// re-use the given convert view
// here you must set all the elements to the required values
}
// your drawable here for this element
convertView.setBackground( ... );
// maybe here's more to do with the view
return convertView;
}
}
That's basically it. If there's only a few background drawings I would possibly cache them as well so you don't have to read the resources over and over again!
Have fun!
Did you want a list view where each row had its own background, like the one below?
In your code, you most likely would set a ListAdapter on the view. A number of the adapters that you can construct take in an layout or view argument. You should just have to set the android:background property for the view that you point this to.
For example, I might have an ArrayAdapter created like this:
new ArrayAdapter<String>(context, R.layout.item, R.id.itemName);
Of course, I use ListView's setAdapter with this adapter. In my item.xml file, I can define the text view itemName like this:
<TextView android:id="#+id/itemName" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Test view" android:background="#drawable/add"/>
However, I don't think all types of ListAdapter has a constructor with that feature. Check the documentation for the type of ListAdapter that you are using. If you are using one without this constructor, I think you may have to subclass the adapter and override the adapter's getView to inflate your view from your XML file.
I have the image add.png in each of res/drawable folders.

Categories

Resources