Android custom view background covering view content - java

I'm trying to figure out why an Android custom view which works when no background is set suddenly stops working when the background is set. It seems the background covers the items added to the view when it is set. I've simplified the view code to the bare minimum which reproduces the problem and to be able to post the code here. The custom view inherits from RelativeLayout and the code is as follow:
public class TestView extends RelativeLayout {
private LayoutInflater mInflater;
private ViewTreeObserver mViewTreeObserber;
private boolean mInitialized = false;
public TestView(Context ctx) {
super(ctx, null);
initialize(ctx, null, 0);
}
public TestView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
initialize(ctx, attrs, 0);
}
public TestView(Context ctx, AttributeSet attrs, int defStyle) {
super(ctx, attrs, defStyle);
initialize(ctx, attrs, defStyle);
}
private void initialize(Context ctx, AttributeSet attrs, int defStyle) {
mInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mViewTreeObserber = getViewTreeObserver();
mViewTreeObserber.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!mInitialized) {
mInitialized = true;
drawItem();
}
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawItem();
}
private void drawItem() {
if (!mInitialized) return;
removeAllViews();
View item = mInflater.inflate(R.layout.testview_item, null);
TextView txt = (TextView)item.findViewById(R.id.test_view_item);
txt.setText("Test View Text");
txt.setTextColor(Color.BLACK);
txt.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
addView(item);
}
}
The item layout is simple:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:id="#+id/test_view_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:ellipsize="end"
android:maxLines="1" />
</LinearLayout>
And the sample app simply declares two instances of the custom view in XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin">
<TextView android:text="With Background set"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.machado.felipe.TestView
android:background="#android:color/holo_blue_light"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.machado.felipe.TestView>
<TextView android:text="WithOUT Background set"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.machado.felipe.TestView
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.machado.felipe.TestView>
</LinearLayout>
The result looks like the picture bellow:
This is driving me crazy! I'm not used to write custom views in android and this is someone else's code which I'm trying to fix! I don't even know if this is the way it should be done, since I'm inflating views and adding them to the RelativeLayout I don't think I should be adding them in the onDraw, but since the complete code is doing more complex stuff, as laying out the items in multiple rows with wrapping, it is possibly a valid approach... But, anyway, I can't figure out how to fix this!

Related

Can I link a layout/view class to an XML item

What I am trying to achieve (which I do not know if it is even doable, since I'm new to android development) is to have:
A MainActivity class utilize/reference and XML layout (this i know is doable)
Within the MainActivity class also call a custom Layout programmatically (I am uncertain how to do this)
To elaborate on my question
I have the following XML layout which is compromised of two items:
A dropdown menu
A RelativeLayout view (*** this is where I would like to link to my custom class to)
Here is the XML code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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="wrap_content"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/weekDropdown"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:hint="#string/select_week"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="#+id/weekSelected">
<AutoCompleteTextView
android:id="#+id/weekSelected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:layout_weight="1"
android:padding="14dp"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<RelativeLayout <-- Here is where I would like to link my custom class
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</RelativeLayout>
</LinearLayout>
In my custom class, I build my layout/view programmatically:
public class MainActivityLayout extends RelativeLayout {
Context context;
TableLayout tableA;
TableLayout tableB;
TableLayout tableC;
TableLayout tableD;
public MainActivityLayout(Context context) {
super(context);
this.initComponents();
this.setComponentsId();
etc...
}
}
Which I call it from my MainActivity class like so:
setContentView(new MainActivityLayout(this));
So is it possible to link the RelativeLayout in the XML to my custom class? If so can you let me know how to do so?
If you want to use your custom view, the easiest way is to include it in the xml. So this needs to be changed:
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</RelativeLayout>
into this
<myapp.mypackage.MainActivityLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</myapp.mypackage.MainActivityLayout>
Change myapp.mypackage based on the real path of MainActitvityLayout.
Then just use it like any other usual layouts.
setContentView(R.layout.main_activity);
Also you must override these constructors:
public MainActivityLayout(Context context)
{
super(context);
}
public MainActivityLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public MainActivityLayout(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
public MainActivityLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
However, if you insist on using it like this:
setContentView(new MainActivityLayout(this));
You can not use the main xml layout at all, but you can still inflate xml files onto your custom view. I won't write them here, you can easily search on this site on how implementing them.

Responsive UI GridView layout

I have surveyed in other sites and got different responses about how I can solve my problem. In fact the problem is that I am trying to make a responsive gridView layout to display 25 textviews with numbers. I had hard time in constructing it the way I want but here is what I got:
<LinearLayout 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:orientation="vertical"
android:weightSum="10"
android:layout_gravity="center"
android:gravity="center"
tools:context="com.example.hristodraganov.bingo.MainActivity"
tools:showIn="#layout/activity_main">
<RelativeLayout
android:id="#+id/relLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4">
</RelativeLayout>
<GridView
android:paddingTop="8dp"
android:id="#+id/gridview"
android:background="#000"
android:layout_width="match_parent"
android:layout_weight="6"
android:layout_height="0dp"
android:gravity="center"
android:columnWidth="70dp"
android:numColumns="5"
android:verticalSpacing="10dp"
android:horizontalSpacing="1dp"
android:stretchMode="spacingWidth"
/>
This is the layout that is used on the MainActivity. I inflate it with textview item with this code:
<?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="match_parent"
android:background="#d3d3d3"
android:gravity="center">
<com.example.****.****.SquareView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/cell"
android:textColor="#D0583B"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="30sp">
</com.example.****.****.SquareView>
There is the extension of the BaseAdapter class that is used to inflate the grid with the textviews:
public class GridAdapter extends BaseAdapter {
private final Context mContext;
private String[] numbers;
public GridAdapter(Context context, String[] numbers) {
this.mContext = context;
this.numbers = numbers;
}
#Override
public int getCount() {
return numbers.length;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(mContext);
gridView = inflater.inflate(R.layout.textview_layout, null);
TextView textView = (TextView) gridView.findViewById(R.id.cell);
textView.setText(numbers[position]);
} else {
gridView = (View) convertView;
}
return gridView;
}
}
Also, I am forcing the textView to be squared in this SquareView class:
public class SquareView extends android.support.v7.widget.AppCompatTextView {
public SquareView(Context context) {
super(context);
}
public SquareView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}}
The layout is doing fine by now, here is an image of the size I used to construct it Nexus 5X. It looks good but when I change to 4" -Nexus S the whole last row is somewhere below the screen. Also this happens to 5"-Nexus 5 where the last row is slightly visible. Above 5.2" to 6.0" the layout fits perfectly. So my question is what should I do in this scenario to make the layout fit for small-sized screens without making a duplicate layout for them. (Note: I was told that the BaseAdapter implementation would fix the responsiveness of the layout). Any ideas what could I do? (Sorry for the long texts.)
Because there are a lot of different Android devices out there your best choice would be to encapsulate your layout in a ScrollView.
Another option is to get rid of the weights and let the top RelativeLayout grow/shrink after the GridView has taken all the space it needs. This still might not work on smaller screens or in landscape.
```
<RelativeLayout
android:id="#+id/relLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</RelativeLayout>
<GridView
android:paddingTop="8dp"
android:id="#+id/gridview"
android:background="#000"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:columnWidth="70dp"
android:numColumns="5"
android:verticalSpacing="10dp"
android:horizontalSpacing="1dp"
android:stretchMode="spacingWidth"/>
```

How to android inflate XML Layout in ConstraintLayout class?

i create a ConstrantLayout class
public class AboutView extends ConstraintLayout {
TextView about_txt;
TextView dr_txt;
public AboutView(Context context) {
super( context );
init();
}
public AboutView(Context context, AttributeSet attrs, int defStyleAttr) {
super( context, attrs, defStyleAttr );
init();
}
private void init() {
LayoutInflater inflater = LayoutInflater.from( getContext() );
inflater.inflate( R.layout.about_layout,this );
about_txt = (TextView) findViewById(R.id.about_txt);
dr_txt = (TextView) findViewById(R.id.dr_txt);
}
}
Layout about_layout.XML file to inflate into class
Create layout how you want your custom view to look like. There is nothing complicated about it.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayoutxmlns: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"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="#+id/about_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintStart_toStartOf="parent"
tools:text="About" />
<TextView
android:id="#+id/dr_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
tools:text="Text" />
</android.support.constraint.ConstraintLayout>
This way you can set constraints and edit the layout with editor.
AFTER you are done with editting I would recommend changing the root view ConstraintLayout to merge.
By merging you won't have extra layout inside the custom view. Be careful - merge attributes are ignored.

Create custom layout in XML to initialize in code or in another layout xml

I have encountered an issue i am trying to resolve (or understand better the way it should be done) in creation of custom Layout in Android.
I want to create a custom RelativeLayout class for my use, which is defined in a layout XML file.
my_relative_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<com.mypackage.MyRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null"
android:scaleType="fitStart"
android:src="#drawable/my_drawable"/>
<TextView
android:id="#+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/title_gray"
android:layout_below="#id/image_view"
android:gravity="center"
android:text="#string/placeholder" />
</com.mypackage.MyRelativeLayout>
Usage
public class MyRelativeLayout extends RelativeLayout {
private AttributeSet attrs;
private ImageView imageView;
private TextView textView;
public MyRelativeLayout(Context context) {
super(context);
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.attrs = attrs;
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.attrs = attrs;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
if (attrs != null) {
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.MyRelativeLayout, 0, 0);
drawableResource = a.getResourceId(R.styleable.MyRelativeLayout.image_view, 0);
a.recycle();
}
imageView = (ImageView) findViewById(R.id.image_view);
textView = (TextView) findViewById(R.id.text_view);
if (drawableResource != 0 && imageView != null) {
imageView.setImageResource(drawableResource);
}
}
}
My issue is that i want to initialise this layout both in another XML and in code.
But as I wrote my class and XML, i can only use it in code by doing:
myLayout = (MyRelativeLayout) LayoutInflater.from(this.getActivity()).inflate(R.layout.my_relative_layout, container, false);
When writing the following in another XML causes the onFinishInflate to fail on getViewById (returns null) and the childrenCount is 0
<com.mypackage.MyRelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#id/another_layout"
app:image_view="#drawable/my_image" />
and doing the following, won't let me configure the custom image_view attribute.
<include layout="#layout/my_relative_layout"/>
To fix that, i can change the custom layout XML root element to be of type RelativeLayout and add the following to the beginning of onFinishInflate method:
LayoutInflater.from(getContext()).inflate(R.layout.my_relative_layout, this, true);
But the XML won't reference my class.
My questions are,
1. Am i missing something in the definition of the custom layout?
2. What is the correct definition for custom layout?
Thank you in advance!
First you should use the styled attributes in the contractor as shown in this example.
How can you expect from MyRelativeLayout that defined as fallow:
<com.mypackage.MyRelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#id/another_layout"
app:image_view="#drawable/my_image" />
To be aware of views that are defined in my_relative_layout.xml?
To make it work you should create a costume layout and add it to com.mypackage.MyRelativeLayout manually.
Some thing like that:
costume_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null"
android:scaleType="fitStart"
android:src="#drawable/my_drawable"/>
<TextView
android:id="#+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/title_gray"
android:layout_below="#id/image_view"
android:gravity="center"
android:text="#string/placeholder" />
</RelativeLayout>
Your costume view
public class MyRelativeLayout extends RelativeLayout {
private AttributeSet attrs;
private ImageView imageView;
private TextView textView;
public MyRelativeLayout(Context context) {
super(context);
init();
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(Context context){
LayoutInflater.from(context).inflate(R.layout.costume_layout, this);
}
}

Android: Custom View in XML-Layout

I'm trying to add a ListView and a Custom View in my layout, but I always get this error:
"...Error inflating class com.example..."
Without the Custom View it works and only the ListView is shown.
Activity:
package com.example.training;
public class PracticeActivity extends ActionBarActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.practice_layout);
initListView(); //private function
}
//Other stuff
}
Custom View:
package com.example.training;
public class LinearChart extends View{
LinearChart(Context context, AttributeSet attrs){
super(context,attrs);
}
LinearChart(Context context){
super(context);
}
LinearChart(Context context, AttributeSet attrs, int defStyle){
super(context,attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
canvas.drawLine(10, 10, 30, 30, paint);
}
}
XML-Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/practice_listview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerHorizontal="true" >
</ListView>
<view
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="#+id/practice_listview"
class="com.example.training.LinearChart" />
</RelativeLayout>
There are no public constructors on your class. Make sure you override the constructors. It should look like this instead:
public LinearChart(Context context, AttributeSet attrs){
super(context,attrs);
}
public LinearChart(Context context){
super(context);
}
public LinearChart(Context context, AttributeSet attrs, int defStyle){
super(context,attrs, defStyle);
}
Notice the addiction of public modifier.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/practice_listview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerHorizontal="true" >
</ListView>
<com.example.training.LinearChart
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="#+id/practice_listview"
/>
</RelativeLayout>
Instead of <view in your xml file, you need the fully qualified name of the class (or use a new xml namespace). So, in this example, your xml might look like:
<com.example.training.LinearChart
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
(and therefore remove the class: attribute in your current XML). This should then inflate correctly.

Categories

Resources