Setting Button Color in Extended Button Class - android

This is really confusing me. I've extended the Button class as follows:
public class MyButton extends Button {
// Default colours/styles
private int myButtonDrawable = R.drawable.button_drawable;
private int myButtonTextColor = R.color.white_text;
// Constructors
public MyButton (Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// Override setEnabled to apply custom colors/styles
#Override
public void setEnabled (boolean enabled) {
super.setEnabled(enabled);
if (enabled) {
this.setBackgroundResource(myButtonDrawable);
this.setTextColor(getResources().getColor(myButtonTextColor));
}
}
Thus by using setters I can set values for myButtonDrawable and myButtonTextColor for each MyButton object, and they will be applied when I enable the button using the method setEnabled (i.e. when true).
However, the above code does not work. It crashes out with logcat
Caused by: android.view.InflateException: Binary XML file line #36: Error inflating class
The error is occuring with the line
this.setTextColor(getResources().getColor(myButtonTextColor));
which is really weird because it works fine if I use
this.setTextColor(getResources().getColor(R.color.white_text));
where the resource white_text is defined as
<color name="white_text">#FFFFFFFF</color>
I don't understand why replacing R.color.white_text with the class member myButtonTextColor causes the activity to crash. Anyone got any ideas? Thanks.

Edit: You are extending Button. Button extends TextView, which reads from its XML attributes and calls setEnabled(boolean) in its constructor. However, you have overridden setEnabled() and that method now refers to fields defined in your subclass by initializers.
The initializers for your subclass are not run before the constructor of the superclass. Therefore, at the time setEnabled() is first called by the superclass constructor, myButtonTextColor has the default value and resolving it with Resources.getColor() will throw an error.
If you persist in doing this in code, you may move your if statement to the constructor of your subclass:
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
if (isEnabled()) {
setBackgroundResource(myButtonDrawable);
//FYI, there is a setTextColor method that accepts a color ID.
setTextColor(myButtonTextColor);
}
}
since at the time the constructor of your subclass is called, your initializers are guaranteed to have run, and the superclass constructor will have already resolved the attributes from XML.
Here is an excellent explanation if you'd like to dive more deeply into exactly how Java works in this regard.
There is a better approach for what you want to do. Use a standard Android Button and in your XML (or in code), set a selector as the background and color of your button. You can define an appearance for your button's enabled state -- and many others, including pressed, focused, etc. Android will handle changing the appearance for you.
In your activity (or you can do this in XML):
Button button = new Button();
button.setTextColor(R.drawable.button_text_color);
button.setBackgroundResource(R.drawable.button_background);
drawable/button_text_color.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:color="#color/white_text" />
<item android:color="#color/grey_text" />
</selector>
drawable/button_background.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:background="#drawable/button_drawable"/>
<item android:background="#drawable/button_disabled_drawable"/>
</selector>

Related

Why in different themes different buttons are created?

I have an usual button and a theme which is applied to android:theme in AndroidManifest file:
<Button
android:id="#+id/supperButton"
android:layout_width="match_parent"
android:layout_height="120dp" />
<style name="AppTheme" parent="Theme.AppCompat">
</style>
When i inflate this button and stop the app with debugger to see what class has been created i see the following:
As you can see, instead of an usual button class, AppComapatButton has been created. When i change theme to as follows:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
</style>
MaterialButton is created, instead of an usual button class or AppComapatButton:
Question: as i can gather, themes can define what exactly type of a widget is used. So what exactly does define it? Maybe there is some attribute in a theme that does it ?
It happens if your Activity extends AppCompatActivity.
The AppCompatActivity calls the setFactory2 method using a custom implementation of LayoutInflater.
This implementation is done by the AppCompatViewInflater
which checks the names of the views in the layout and automatically "substitutes" all usages of core Android widgets inflated from layout files by the AppCompat extensions of those widgets.
You can check in the source code:
#Nullable
public final View createView(/**...*/) {
//...
switch (name) {
case "Button":
view = createButton(context, attrs);
verifyNotNull(view, name);
break;
//...
}
#NonNull
protected AppCompatButton createButton(Context context, AttributeSet attrs) {
return new AppCompatButton(context, attrs);
}
In the MaterialComponents theme is defined another implemetantion, the MaterialComponentsViewInflater.
For example you can check in the source code:
#Override
protected AppCompatButton createButton(#NonNull Context context, #NonNull AttributeSet attrs) {
return new MaterialButton(context, attrs);
}
You can use an own inflater adding in the app theme the viewInflaterClass attribute:
<style name="Theme.App" parent="Theme.MaterialComponents.*">
<item name="viewInflaterClass">com.google.android.material.theme.MaterialComponentsViewInflater</item>
</style>

How to customize Cast Dialog in the Google Cast Framework for Android

I am trying to customize the Google Cast SDK's Cast Dialog (shown when you tap the cast button and shows the list of available devices), but i haven't found a way to do it.
Currently, it just shows an AlertDialog with a list of the available devices to connect.
What i want to do instead, is open an Activity that will show the list of devices with my own UI implementation.
This is the dialog i am trying to modify:
I've found many customization aspects about this SDK, but nothing related to this dialog.
So i figured out a way to achieve this,
First i created a class that overrides MediaRouteActionProvider (which is the main class that controls that button's functionality)
public class CustomMediaRouteActionProvider extends androidx.mediarouter.app.MediaRouteActionProvider {
public CustomMediaRouteActionProvider(Context context) {
super(context);
}
#Override
public MediaRouteButton onCreateMediaRouteButton() {
return new CastButton(getContext());
}
}
Then you're gonna need to override the button's functionality with your own, in my case i open a new activity.
public class CastButton extends MediaRouteButton {
public CastButton(Context context) {
this(context, null);
}
public CastButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.mediaRouteButtonStyle);
}
public CastButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean performClick() {
Intent i = new Intent(getContext(), RemoteDevicesActivity.class);
getContext().startActivity(i);
return true;
}
}
Finally, you need to modify your xml that contains this button (i assume that you already implemented this part)
Change the field app:actionProviderClass with your custom class (in this case CustomMediaRouteActionProvider) and you're done.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="#string/connect_to"
android:id="#+id/cast"
app:actionProviderClass="CustomMediaRouteActionProvider"
app:showAsAction="ifRoom" />
</menu>
Are you have more details of final result of this? I need to do something similar but I don't get it how did you achieve it

I need help in the custom Font and TextView

I'm new in android working on an app that gives the user info about all font's of Google.
For that, I need to make an app with a TextView Something like this
On click of the TextView, the font will change With text.
I'm thinking about using onclicklistener
you can put "your_font.ttf" file in asset folder then import it with
Typeface custom_font_1 = Typeface.createFromAsset(getAssets(), "your_font.ttf");
then assign it to your showCaseTextView with this
showCaseTextView.setTypeFace(custom_font_1);
then in your onClickListener of showCaseTextView to change your specifiedTextView font do like this
specifiedTextView.setTypeFace(custom_font_1);
and repeat it for other fonts.
You can implement your own custom font with TextView, EditText, Button etc.. by using android attributes.
How to
-Here are some steps to use:
1.Create attribute file (res->values->attrs.xml)
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="TextElement">
<attr name="font" format="string"/>
<attr name="underline" format="boolean"/>
</declare-styleable>
</resources>
2.Create Custom TextView class (anywhere in java folder)
3. Use attributes inside your layout file
4. and just run your code.
Here is the full example of your question, you can go through this exmaple:
Full Demonstration
Have 2 way to archive this
1st way
public class FontCache {
private static HashMap<String, Typeface> fontCache = new HashMap<>();
public static Typeface getTypeface(String fontname, Context context) {
Typeface typeface = fontCache.get(fontname);
if (typeface == null) {
try {
typeface = Typeface.createFromAsset(context.getAssets(), fontname);
} catch (Exception e) {
return null;
}
fontCache.put(fontname, typeface);
}
return typeface;
}
}
This caches the fonts while minimizing the number of accesses to the assets. Now, since we've a method to access our custom font, let's implement a class, which extends TextView.
Extending TextView
Next, we'll create a new Java class, which extends TextView. This allows us to use that class in all XML views. It inherits all functionality and properties of a regular TextView; but adds our custom font.
Once again, we're taking a peek at the source code of our eat foody project. The code might look complex for a second, but is straight-forward:
public class EatFoodyTextView extends TextView {
public EatFoodyTextView(Context context) {
super(context);
applyCustomFont(context);
}
public EatFoodyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
applyCustomFont(context);
}
public EatFoodyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
applyCustomFont(context);
}
private void applyCustomFont(Context context) {
Typeface customFont = FontCache.getTypeface("SourceSansPro-Regular.ttf", context);
setTypeface(customFont);
}
}
The first three methods are just constructors, which we override to call a single method applyCustomFont(). That method is the important piece of the puzzle. It simply gets the (hopefully cached) font from our FontCache class. Lastly, we've to call setTypeface() with the font and we're almost done. In case you're wondering, we can call the setTypeface() directly (and not on a TextView object), since we're extending the TextView class.
Using the Class
You might wonder, if so much preparation is worth the effort. In this section you'll see that it is indeed. Because all you've left to do is use the class in an XML view and it automatically has your custom font. There is no Java code necessary!
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.futurestudio.foody.views.EatFoodyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/eat_foody_green_dark"
android:textSize="20sp"
android:text="Future Studio Blog"
android:layout_marginBottom="24dp"/>
</RelativeLayout>
As you can see, you can continue to use all niceties (e.g. textSize, textColor) of TextView. Now, just replace all elements with the class we just created, for example and you applied your custom font everywhere!
(Ref: https://futurestud.io/tutorials/custom-fonts-on-android-extending-textview)
2nd way
Follow Google guide support from API 26 (Android 8) https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml
Make change between textview to change font
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textview_normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/eat_foody_green_dark"
android:textSize="20sp"
android:text="Future Studio Blog"
android:layout_marginBottom="24dp"/>
<com.futurestudio.foody.views.EatFoodyTextView
android:id="#+id/textview_custom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/eat_foody_green_dark"
android:textSize="20sp"
android:text="Future Studio Blog"
android:visibility="gone"
android:layout_marginBottom="24dp"/>
</RelativeLayout>
attention at android:visibility="gone"
in Activity you use this code to toggle between 2 TextViews
final TextView normalTextView = findViewById(R.id.textview_normal);
final TextView customTextView = findViewById(R.id.textview_custom);
normalTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
normalTextView.setVisibility(View.GONE);
customTextView.setVisibility(View.VISIBLE);
}
});
customTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
normalTextView.setVisibility(View.VISIBLE);
customTextView.setVisibility(View.GONE);
}
});

EditView must contain data to further process

I have a EdidText and a Button in a Fragment. When I click the Button i want to checks whether the EditText is empty or not. If is empty I want to return an error message, If notEmpty means move to other fragments or something further process.
I previously search the solution for my problem, but mostly I found the solution like ,
if(textView.getText().toString().trim().equals("")) {
textView.setError("something");
} else {
//do something;
}
The above code worked correctly, but what I ask, how to do this in a layout not programmatically.
I want to know is this is achieved through the layout xml file or not. If possible means please give me an example.
Thank you in advance.
You can't set an error in with XML.
you need to set it in the onCreate of your fragment. This way you won't see the difference if it is set in xml or in code.
You could write your own EditText and add an extra style attribute "error". After that you need to use your own EditText and again set the error in the onCreate.
Create a new class and extend it from EditText like this:
public class MyEditText extends EditText {
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyEditText);
String error = a.getString(R.styleable.MyEditText_error);
a.recycle();
setError(error);
}
}
In Attr add this:
<declare-styleable name="MyEditText">
<attr name="error" format="reference|string" />
</declare-styleable>
After that you will need to always use MyEditText
Check it with this code
editText.getText().toString().isEmpty()

Set default value for TimePickerPreference

I am using this TimePickerPreference implementation: http://www.ebessette.com/d/TimePickerPreference
It works nice. But I do not know how to set a default value for it. So if the preference is called the first time, it should show the current time.
I also would like to extend it, to show the chosen time in the title.
In my preference.xml:
<com.example.preference.TimePickerPreference
android:key="quit_time"
android:dialogTitle="Quit"
android:title="Quit"/>
Maybe this code helps you TimePickerPreference.java
Firstly, I apologize for my English, I'm pulling translators . I know the thread is old, but I searched for information on this problem today and nadia had put no solution . What if it happens to anyone else here I leave . This component out there also has the bug that if you cancel the insertion of the time this is always stored . Also comment on your solution.
Let's start with the main thing, is not exactly why he never throws the setter of the default value and the quickest solution would be to manually control element builders. I personally have decided to create a new attribute called "timeByDefault" that is responsible for conducting the functionality of "defaultValue".
/**
* #param context
* #param attrs
*/
public TimePickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
handleCustomAttributes(context, attrs);
initialize();
}
/**
* #param context
* #param attrs
* #param defStyle
*/
public TimePickerPreference(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
handleCustomAttributes(context, attrs);
initialize();
}
private void handleCustomAttributes(Context context, AttributeSet attrs){
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.TimePickerPreference);
CharSequence timeByDefaultCS = arr.getString(R.styleable.TimePickerPreference_timeByDefault);
if (null != timeByDefaultCS){
setTimeByDefault(timeByDefaultCS.toString());
}
}
public void setTimeByDefault(Object defaultValue) {
setDefaultValue(defaultValue);
}
Now we create the attribute in "attrs.xml" file in the folder "/res/values​/", and add the following resource:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TimePickerPreference">
< attr name="timeByDefault" format="string" />
</declare-styleable>
</resources>
It will be very important for the new attribute can be accessed from xml will declare xmlns:custom="http://schemas.android.com/apk/res-auto" at the beginning of our xml file in the manner indicated below.
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
>
<PreferenceCategory android:title="Notifications">
<com.mypackage.TimePickerPreference
android:key="KEY_TO_SAVE_THE_TIME_IN_PREFERENCE"
android:title="My custom title"
android:summary="My detail"
android:dialogTitle="My dialog title"
custom:timeByDefault="07:30"
/>
</PreferenceCategory>
</PreferenceScreen>
Now I will explain the issue of canceling the date change:
Opening the dialog box allows us to accept or cancel the new time. This component accepts always time whatever you do. So I created a variable to temporarily store intermediate private time really introduced until accept that value.
private String tmpPersistString = "";
Replace the following function to leave as follows:
#Override
public void onTimeChanged(TimePicker view, int hour, int minute) {
tmpPersistString = hour + ":" + minute;
}
Similarly we control when we pressed one of two buttons, that when we are pressing to accept it then that we store the value of the new time:
#Override
public void onClick(DialogInterface dialog, int which) {
super.getDialog().getCurrentFocus().clearFocus();
super.onClick(dialog, which);
if (which != -2){
persistString(tmpPersistString);
tmpPersistString = "";
}
}
And although it is not entirely necessary to initialize the temporary variable ground when the dialog if the ESC key is pressed to close if you have physical keyboard, or the return button or clicks outside the window.
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
tmpPersistString = "";
}
I can only hope that you have understood enough to make this component a good choice at the time to develop and it will be of use to more people. A greeting!

Categories

Resources