ClassCastException in findViewById while using custom view - android

I am using custom TextView. But when I am retrieving it using find view by Id I get ClassCastException. I have using it in XML file like this:
<View class="com.android.smsapp.MsgTextView"
android:id="#+id/text"
And using it in java file like this.
MsgTextView text = (MsgTextView) row.findViewById(R.id.text);
What am I doing wrong?
#Pavandroid
I have included it in correct file
package com.android.smsapp;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;
public class MsgTextView extends TextView {
private String sender;
MsgTextView(Context c){
super(c);
}
MsgTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
MsgTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setSender(String s) {
sender = s;
}
public String getSender() {
return (sender);
}
}`
Also When I checked properly, LogCat is showing one more line
03-19 14:30:41.476: E/AndroidRuntime(24089): Caused by: java.lang.NoSuchMethodException: MsgTextView(Context,AttributeSet).. But I have defined this constructor.

instead of the above code use the below code.
<com.android.smsapp.MsgTextView
android:id="#+id/text"
additional parameters here....>
</com.android.smsapp.MsgTextView>

Related

How can I use a custom dialog instead of original dialog from Edit Text Preference

I would like to use custom dialog when clicking edit texts in preference screen.
Firstly, I have tried .setDialogLayoutResource method but it's only applied edit text itself.
Did not apply Dialog's background or Dialog Title.
Secondary, I tried
#Override
public void onDisplayPreferenceDialog(Preference preference) {
super.onDisplayPreferenceDialog(preference);
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
builder.setTitle("Test");
builder.show();
}
in SettingsFragment extends PreferenceFragmentCompat class.
however the dialog I created would appear when original dialog in the edit text was gone.
Thirdly, I changed my mind to get dialog by using findViewById. I made CustomPreferenceEditText class extends EditTextPreference. And Override public void onBindViewHolder(PreferenceViewHolder holder)
and use method
EditText editText = (EditText) holder.findViewById(android.R.id.edit);
However it returned null exception.but title or summary works great.
I assumed that there are id that can be accessed Dialog Title or Dialog Background for original dialog from edit text like edit text's #android:id/edit.
I've tried to research but could not find so far. If you know any reference or ideas please let me know.
This is custom edit text preference class
public class CustomPreferenceEditText extends EditTextPreference {
public CustomPreferenceEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public CustomPreferenceEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomPreferenceEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomPreferenceEditText(Context context) {
super(context);
}
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
AssetManager assetManager = getContext().getAssets();
final Typeface dqfont = Typeface.createFromAsset(assetManager, "fonts/dqfont.ttf");
TextView titleView = (TextView) holder.findViewById(android.R.id.title);
TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
titleView.setTextColor(getContext().getResources().getColor(R.color.white));
titleView.setTypeface(dqfont);
summaryView.setTextColor(getContext().getResources().getColor(R.color.white));
summaryView.setTypeface(dqfont);
}
}
Edit
pic1
pic2

Acquire a custom view's xml id

I have a custom view ZoneSwitchItem (extends LinearLayout) which I use in a fragment layout xml.
From inside the custom view, I need to get the id that was assigned to it in the fragment xml. So I use attrs.getIdAttribute(); but it returns null instead of the expected id zone1.
I could add a custom attribute ZoneSwitchItemId but would like to avoid that if I could use the default id attribute.
The usage in the fragment xml:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<com.android.common.ZoneSwitchItem
android:id="#+id/zone1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/cardPadding4x"
android:gravity="center_horizontal"
app:zoneItemStyle="ArmourFont_HeadlineBig" />
...
The custom view:
public class ZoneSwitchItem extends LinearLayout {
private TextView itemValue;
private TextView itemTitle;
public ZoneSwitchItem(Context context) {
super(context);
init(context, null);
}
public ZoneSwitchItem(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public ZoneSwitchItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
public void onStart() {
EventBus.getDefault().register(this);
}
public void onStop() {
EventBus.getDefault().unregister(this);
}
private void init(final Context context, AttributeSet attrs) {
inflate(getContext(), R.layout.zone_switch_item, this);
itemValue = findViewById(R.id.itemValue);
itemTitle = findViewById(R.id.itemTitle);
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ZoneSwitchItem, 0, 0);
try {
if (typedArray.getString(R.styleable.ZoneSwitchItem_zoneItemStyle) != null &&
typedArray.getString(R.styleable.ZoneSwitchType_zoneItemImage).equals("ArmourFont_HeadlineBig")) {
itemValue.setTextAppearance(context, R.style.ArmourFont_HeadlineBig);
}
} finally {
typedArray.recycle();
}
}
final String id = attrs.getIdAttribute(); // <<== returns null
itemValue.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EventBus.getDefault().post(new OnZoneSwitchItemClickedEvent(id));
}
});
}
public void setSelected(boolean selected) {
float alpha;
if (selected) {
alpha = 1.0f;
} else {
alpha = 0.5f;
}
itemValue.setAlpha(alpha);
itemTitle.setAlpha(alpha);
}
}
The documentation for the AttributeSet#getIdAttribute() method states:
Return the value of the "id" attribute or null if there is not one. Equivalent to getAttributeValue(null, "id").
The first parameter in the getAttributeValue() method is the namespace for the attribute. This is where the problem is, as null signifies no namespace, but the android:id attribute is in the namespace that the android prefix represents – http://schemas.android.com/apk/res/androi‌​d.
This means that the getIdAttribute() method will only return the value for an id attribute with no prefix. That is:
<com.android.common.ZoneSwitchItem
id="#+id/zone1"
... />
I'm not sure how useful this method is, since, with the example above, it will actually return the complete string there (#+id/zone1), you'd still have to have a separate android:id attribute to get the View properly assigned an ID, and Android Studio will certainly complain that the attribute lacks a namespace prefix.
The first solution that might come to mind is to simply call getAttributeValue() directly, and pass the correct namespace.
String id = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "id");
However, this will return the integer ID value in string form, prepended with #, as it seems that aapt directly substitutes that value in the XML during its processing.
The actual, simple resource name can be obtained from Resources, with its getResourceEntryName() method, and the View's getId() method, since the ID will be set in View's constructor during the super call. For example:
String id = getResources().getResourceEntryName(getId());

How to make a class that contains all my assets/objects to work with many fragments

I want to make a class which contains all the static objects/assets that I use in many different classes and fragments(classes).
So basically I want to be able to say in a fragment class: Style.example3 when I'm setting a font for a particular TextView.
But I keep getting errors like the path or file is not found in the Style class. I have also tried with extending the Style class with Application and use the context or getAplicationContext.getAssets, without any succses.
Right now I'm creating each asset or font everytime in every fragmentclass they are used in, which is not so good.
public class Style {
//-----------------------FONTS------------------------
public Typeface example1 = Typeface.createFromAsset(getAssets(), "fonts/FreeSerif.ttf");
public Typeface example2 = Typeface.createFromAsset(getAssets(), "fonts/Arimo-Regular.ttf");
public Typeface example3 = Typeface.createFromFile("fonts/FreeSerif.ttf");
public Typeface example4 = Typeface.createFromFile("fonts/FreeSerif.ttf");
//------------------------Color backgrounds HEX------------------------
public static String darkgray = "#373737";
public static String lightgray = "#e6e6e6";
public static String oldpaper = "#EAE1D8";
public static String lightpink = "#feaec9";
public static String darkpink = "#ff0f68";
public static String redpink = "#E849A1";
public static String yellow = "#F7E84E";
public static String orange = "#FFB732";
public static String skyblue = "#48B1E3";
public static String green = "#5dd95d";
public static String softblack = "#3d3d3d";
//------------------------------------------------------------------------
}
You shouldn't do that kind of things. Good practices in Android advise you to do as many things as possible in XML. So basically, what you could do is create a BaseActivity that extends AppCompatActivity and make your activities extend that BaseActivity.
Another thing your can do is to create your own Widget. Example with a custom textview with a custom font.
public class CustomTextView extends TextView {
public CustomTextView(Context c) {
super(c);
init(null);
}
public CustomTextView(Context c, AttributeSet attrs) {
super(c, attrs);
init(attrs);
}
public CustomTextView(Context c, AttributeSet attrs, int style) {
super(c, attrs, style);
init(attrs);
}
private void init(AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTextView);
String fontName = a.getString(R.styleable.CustomTextView_font_name);
if (fontName != null) {
Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/" + fontName);
setTypeface(myTypeface);
}
a.recycle();
}
}
and in your xml layouts :
<com.shopmium.views.widgets.CustomTextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/textview"
app:font_name="Roboto-Medium.ttf" />
Finally, use your resource files as many as possible :
use styles.xml
create a colors.xml and use it like this : R.color.mycolor
Hope it helps !
Use static import for the global class and then use the static objects locally. https://docs.oracle.com/javase/1.5.0/docs/guide/language/static-import.html

Applying style to views dynamically in java code

I have following custom button view.
public class PrayerTimeLabel extends Button {
int hours;
int minutes;
String dayHalf; //am or pm
Context parentActivity;
PrayerControl parentControl;
public PrayerTimeLabel(Context context,PrayerControl parent) {
super(context);
init(context,parent,0);
}
public PrayerTimeLabel(Context context, int defStyle, PrayerControl parent) {
//super(context, null, R.style.Button_PrayerTimeButton);
super(context, null, defStyle);
init(context,parent,defStyle);
}
private void init(final Context context, PrayerControl parent, int defStyle)
{
parentActivity = context;
parentControl = parent;
Typeface tf = Typeface.createFromAsset(context.getAssets(),"fonts/digital.ttf");
this.setTypeface(tf);
this.setText(false);
this.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
TimeDialog dialogBox = parentControl.getDialogBox();
dialogBox.setTime(hours, minutes, dayHalf);
dialogBox.show();
}
});
}
public void setTime(int hrs, int min, String half,boolean signalParent)
{
hours = hrs;
minutes = min;
dayHalf = half;
this.setText(signalParent);
}
public void setText(boolean signalParent)
{
super.setText(String.format("%02d", hours)+":"+String.format("%02d", minutes)+" "+dayHalf);
if(signalParent){
parentControl.setPrayerTime(hours, minutes, dayHalf);
}
}
}
and I have the following style defined in my style.xml
<style name="Button.PrayerTimeButton" parent="#android:style/TextAppearance.Widget.Button">
<item name="android:background">#000</item>
<item name="android:textSize">18dp</item>
<item name="android:textColor">#FFFF00</item>
</style>
The extended button is not getting this style. Can some on point our what I am doing wrong? I searched for the solution and found this. Can some one suggest some thing
Note: I cannot use XML to apply styles. It has to be constructor.
Edit:
Following is the class where this custom button is created and used. I have deleted many irrelevant lines of code
public class PrayerControl extends LinearLayout {
protected PrayerTimeLabel prayerTimeButton;
protected String prayerName;
protected static int counter=0;
public PrayerControl(Context context) {
super(context);
init(context);
}
public PrayerControl(Context context, AttributeSet attrs) {
super(context, attrs);
getXMLAttributes(context, attrs);
init(context);
}
protected void getXMLAttributes(Context context, AttributeSet attrs)
{
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.PrayerControl);
prayerName = a.getString(R.styleable.PrayerControl_name);
dayHalf = a.getString(R.styleable.PrayerControl_dayHalf);
hours = a.getInteger(R.styleable.PrayerControl_hours, 4);
minutes = a.getInteger(R.styleable.PrayerControl_minutes, 30);
ltrProgress = a.getInteger(R.styleable.PrayerControl_postNamazInterval, 0);
rtlProgress = a.getInteger(R.styleable.PrayerControl_preNamazInterval, 0);
intervalMax = a.getInteger(R.styleable.PrayerControl_intervalMax, 30);
a.recycle();
}
protected void init(Context context)
{
counter++;
parentActivity = context;
this.setOrientation(LinearLayout.HORIZONTAL);
this.setId(counter);
prayerTimeButtonStyle = R.style.Button_PrayerTimeButton;
initializePrayerTimeButton();
}
protected void initializePrayerTimeButton()
{
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
40);
params.gravity = Gravity.CENTER;
//params.weight = 1.0f;
prayerTimeButton = new PrayerTimeLabel(parentActivity,prayerTimeButtonStyle,this);
prayerTimeButton.setTime(hours, minutes, dayHalf,false);
prayerTimeButton.setLayoutParams(params);
this.addView(prayerTimeButton);
}
}
This is an old answer: I got a -1 a couple of minutes ago and here is the
NEW SOLUTION:
The basic idea is to pass an attribute to the constructor.
Check this link for the complete solution: Applying style to views dynamically in java code
OLD SOLUTION (NOT WORKING):
Add these constructors to your class:
public StyledButton(Context context) {
super(context, null, R.style.Button_PrayerTimeButton);
//... whatever
}
public StyledButton(Context context, AttributeSet attrs) {
super(context, attrs, R.style.Button_PrayerTimeButton);
//... whatever
}
public StyledButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, R.style.Button_PrayerTimeButton);
//... whatever
}
Why not use setTextAppearance(Context context, int resid);. It lets you set the text color, size, style, hint color, and highlight color.
In PrayerTimeLabel class,
private void init(final Context context, PrayerControl parent, int defStyle)
{
setTextAppearance(context, R.style.Button_PrayerTimeButton);
...
}
For more info see this post : setTextAppearance through code referencing custom attribute
Here is an old answer (and simpler) for this topic:
https://stackoverflow.com/a/21455192/4360308
Summarizing:
ContextThemeWrapper newContext = new ContextThemeWrapper(baseContext, R.style.MyStyle);
button = new Button(newContext);
You should make in your constructor a call to the super(context,attrs,defStyle) constructor, which will apply the defStyle to your View.
However you can not than change the style dinamically.
The bug report you link to states:
In addition, when dynamically creating elements at run-time this means that you can't simply apply a style in code. You need to create a separate XML layout for the view you're building, and inflate it [...]
With this in mind, a solution could look like this:
Create an xml layout for your button - let's call it prayertimebutton.xml:
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFF00"
//...etc
/>
Then, in the initializePrayerTimeButton method in your PrayerControl class, inflate and add the PrayerTimeLabel button to the PrayerControl layout:
//Retrieve a LayoutInflater instance that is hooked up to the current context
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//The LayoutInflater instantiates the layout file into a PrayerTimeLabel object
PrayerTimeLabel button = (PrayerTimeLabel)inflater.inflate(R.layout.prayertimebutton, this, false);
//add the PrayerTimeLabel to the PrayerControl
this.addView(button);
Note that the second parameter of LayoutInflater.inflate() is a reference to the parent View(Group).
The below answer by adamp, an Android framework engineer at Google, discusses this approach in more detail.
Why so complex to set style from code in Android

what is wrong with this assignment?

I have defined a class XX like this:
public class XX extends RelativeLayout {
protected static final boolean DEBUG = true;
public XX(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public XX(Context context, AttributeSet attrs) {
super(context, attrs);
if (DEBUG)
Log.i(this.getClass().getSimpleName(), " ->2"
+ Thread.currentThread().getStackTrace()[2].getMethodName());
//getAttributes(context, attrs);
}
public XX(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (DEBUG)
Log.i(this.getClass().getSimpleName(), " ->3"
+ Thread.currentThread().getStackTrace()[2].getMethodName());
//getAttributes(context, attrs);
}
}
and in my code I write:
RelativeLayout v = (RelativeLayout) this.findViewById(R.id.switch_TCMyTB);
XX x = (XX) v; //<----- crashes here
but it crashes with the assignment. I assume the because XX extends View I can just assign the view (RelativeLayout) to an XX object.
But it crashes. What is wrong with the assignment?
EDIT:
I changed extends View to extends RelativeLayout. Also changed View v to RelativeLayout v.
But I still get a classCastException..???? Why?
While
RelativeLayout r = (RelativeLayout) v;
certainly works fine.
Since I'm not completely familiar with custom components, I just tried to make an example.
I can't answer your question as to why it wont work. If my example won't run for you, you will need to provide a logcat.
I create a custom class:
public class TestLayout extends RelativeLayout {
public TestLayout(Context context) {
super(context);
}
public TestLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TestLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
This class is in the package com.test.cc
In an XML layout I use
<com.test.cc.TestLayout
android:layout_width="10dip"
android:layout_height="10dip"
android:id="#+id/testLayout"
/>
In this example layout.. TestLayout is a child of a LinearLayout.
Add xmlns:android="http://schemas.android.com/apk/res/android" if its the highest level component in an xml layout.
Then in the activity:
//Called in onCreate mthod of an activity.
setContentView(R.layout.test); //make sure you call this first
TestLayout l = (TestLayout)findViewById(R.id.testLayout);
This runs fine for me.
This was tested on a 2.2 and 3.2 device.
So make sure you call setContentView(...) first and then create an object of the layout.
Also make sure in the xml deffinition the package is right. Although if this was wrong you would get an class not foudn exception
EDIT||
I tried running this:
RelativeLayout l = (RelativeLayout)findViewById(R.id.testLayout);
TestLayout tl = (TestLayout)l;
And it also runs fine without any exceptions. So I assume the problem lies elsewhere. Maybe package name errors or something.
The following code runs without ClassCastExceptions
So my guess is that the error may lie in the XML.
public class Test {
public static void main(String[] args) {
B b = new B();
Object o = b;
A a = (A) o;
B b2 = (B) a;
System.out.println("done");
}
public static class A /* extends Object */ {
}
public static class B extends A {
}
}

Categories

Resources