jola
jola

Reputation: 1115

How do I get a custom component that extends RelativeLayout programmatically?

Being new to Android the following issue drives me crazy, and not being able to Google an answer indicates that the solution is really simple...

I try to add a custom component (ArticleView extends RelativeLayout) to a ViewGroup (LinearLayout) from code but I cannot get access to the ArticleView object, trying to cast to it just throws an

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.eo.read/com.example.eo.read.ArticleInfoActivity}: java.lang.ClassCastException: android.widget.RelativeLayout cannot be cast to com.example.eo.read.view.ArticleView
Caused by: java.lang.ClassCastException: android.widget.RelativeLayout cannot be cast to com.example.eo.read.view.ArticleView
        at com.example.eo.read.ArticleInfoActivity.onCreate(ArticleInfoActivity.java:44)

In my Activity class I do:

package com.example.eo.read;

import android.content.Context;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.example.eo.read.content.Article;
import com.example.eo.read.content.ArticleDB;
import com.example.eo.read.view.ArticleView;
...

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_article_info);

    _article = ArticleDB.getInstance().getArticle("test");

    LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    //get the linear layout into which the ArticleView is going
    LinearLayout container = (LinearLayout) findViewById(R.id.recommendation_container);

    //get the custom component
    RelativeLayout ra = (RelativeLayout)inflater.inflate(R.layout.article_view, container, false);

    //this causes the classcast exception, although this RelativeLayout really should be an ArticleView
    ((ArticleView)ra).setArticle(_article);

    //adding the ArticleView to the container works fine, and the customizations 
    //I have made in ArticleView are visible, so indeed it seems ra is an ArticleView ??
    container.addView(ra);

}

The (simplified) article_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="260dp" android:layout_height="wrap_content" android:background="@drawable/stroked_grey_plate">
<TextView
    android:id="@+id/title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="8dp"
    android:layout_marginTop="8dp"
    android:textSize="16sp"
    android:text="Sample text"
    android:textColor="#111111"
    android:scrollHorizontally="true"
    android:ellipsize="end"
    android:maxLines="1"
    />
</RelativeLayout>

The layout for the activity contains the id/recommedation_container into which the ArticleView is being inserted. Below is also the same view inserted declaratively, just for clarity:

<LinearLayout
      android:id="@+id/recommendation_container"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:layout_marginLeft="10dp">
    <com.example.eo.read.view.ArticleView
         android:layout_width="wrap_content" android:layout_height="wrap_content"
         custom:titleText="my title text"
     />
</LinearLayout>

The ArticleView class is essentially:

package com.example.eo.read.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet; 
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.eo.read.R;
import com.example.eo.read.content.Article;

public class ArticleView extends RelativeLayout {
private TextView _titleView;
private Article _article;

public ArticleView(Context context) {
    this(context,null);
}

public ArticleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ArticleView, 0, 0);

//in the case where the ArticleView is declared in XML the title is retreived from a custom attribute, this works fine.    
String titleText = a.getString(R.styleable.ArticleView_titleText);

    a.recycle();

    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.article_view, this, true);

    ViewGroup rl = (ViewGroup)getChildAt(0); //get the RelativeLayout

    _titleView = (TextView) rl.getChildAt(0);
    _titleView.setText(titleText);

} 

//in the case where the ArticleView is initiated from code the title should be set by calling this method, 
//which I never can reach since I cannot get to this ArticleView object from my activity :-(
//I realize this class is maybe not fully functional yet but first step is to actually be able to initiate it...
public void setArticle(Article a) {
    _article = a;
    _titleView.setText(_article.getTitle());
}
}

So, my question is pretty much.. why can't I do:

ArticleView ra = (ArticleView)inflater.inflate(R.layout.article_view, container, false); 

and what should I instead do to get to my ArticleView?

Upvotes: 0

Views: 1279

Answers (2)

cYrixmorten
cYrixmorten

Reputation: 7108

If I understand you correctly, you want to add the custom view programmatically and not have it defined in the XML?

If that is the case, what happens if you simply do:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_article_info);

    _article = ArticleDB.getInstance().getArticle("test");

    LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    //get the linear layout into which the ArticleView is going
    LinearLayout container = (LinearLayout) findViewById(R.id.recommendation_container);

    //get the custom component
    ArticleView av = new ArticleView(this);


    av.setArticle(_article);


    container.addView(av);

}

In case you haven't stumbled upon it yet, it seems this blog has some nice tips regarding custom views: http://trickyandroid.com/protip-inflating-layout-for-your-custom-view/

If you would like to have your Custom view inflated together with your layout, then you can do:

<LinearLayout
      android:id="@+id/recommendation_container"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:layout_marginLeft="10dp">
    <com.example.eo.read.view.ArticleView
      android:id="@+id/article" 
         android:layout_width="wrap_content" android:layout_height="wrap_content"
         custom:titleText="my title text"
     />
</LinearLayout>

Just taking the XML from your question as an example, not sure how well it fits your situation.

But now, inflating the above XML to a view called, say, root and then doing root.findViewById(R.id.article) should return a view which can be cast to ArticleView.

Thinking about it, if you have an XML file like this:

    <com.example.eo.read.view.ArticleView
         android:layout_width="wrap_content" android:layout_height="wrap_content"
         custom:titleText="my title text"
     />

You actually should be able to inflate it, as you are trying, and cast to ArticleView, since com.example.eo.read.view.ArticleView is now the root of the layout.

Upvotes: 0

Alan Deep
Alan Deep

Reputation: 2105

Replace ArticleView in your XML file with [packagename].ArticleView

For example, if your ArticleView class is contained in com.john.article, then your ArticleView should be replaced by com.john.article.ArticleView.

Upvotes: 1

Related Questions