Here is my button:
public class ChimmerButton extends Button {
public ChimmerButton(Context context) {
super(context);
}
public ChimmerButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChimmerButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/*
* This method is used to apply the external font
*/
public void setTypeface(Typeface tf, int style) {
if (!isInEditMode()) {
super.setTypeface(Typeface.createFromAsset(
getContext().getAssets(), "calibre-regular.ttf"));
}
}
}
How can i apply Theme.Light.NoTitleBar.Fullscreen on all chimmerButtons using above code?? Is there any solution for this?
Note that this should be called before any views are instantiated in
the Context (for example before calling setContentView(View) or
inflate(int, ViewGroup)).
from:
http://developer.android.com/reference/android/view/ContextThemeWrapper.html#setTheme%28int%29
Unfortunately, You must set the theme before displaying the Activity at all.
Therefore you can't have 'dynamic' themes driven by runtime code (though the comment above shows how to make a custom theme for your buttons)
ContextThemeWrapper themedContext;
public ChimmerButton(ContextThemeWrapper themedContext) {
This.themedContext = themedContext;
}
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
themedContext = new ContextThemeWrapper( Activity.this, android.R.style.Theme_Holo_Light_Dialog_NoActionBar );
}
else {
themedContext = new ContextThemeWrapper( Activity.this, android.R.style.Theme_Light_NoTitleBar );
Button's theme is context dependent by default. So instead of setting theme for buttons, set theme for the activity that contains the button. It is a much easier solution.
Apply a theme to an activity in Android?
Related
According to this article
CustomAttribute are specified with the attributeName, which needs to match the getter/setter methods of an object such that:
getter: getName (e.g. getBackgroundColor)
setter: setName (e.g. setBackgroundColor)
(so motion:attributeName need to be backgroundColor)
I've tried bellow attribute names with material button, but none of them worked.
<CustomAttribute motion:attributeName="IconTintResource" motion:customColorValue="#color/keyTextColor" />
'IconTintResource', 'iconTintResource', 'IconTint', 'iconTint', 'ColorFilter'
any suggestions?
These are the errors I'm getting
E/TransitionLayout: Custom Attribute "IconTint" not found on com.google.android.material.button.MaterialButton
E/TransitionLayout: com.google.android.material.button.MaterialButton must have a method setIconTint
E/TransitionLayout: no method setIconTinton View "f_editor_image_view_terminal"
MotionLayout's CustomAttribute use reflection to setValues on Views (roughly based on the Java beans conventions)
So if you say
<CustomAttribute motion:attributeName="foo" motion:customColorValue="#color/keyTextColor" />
It looks for a method setFoo(int value);
Unfortunately even though MaterialButton parses the xml android:iconTint="#FFF"
It does not have the method setIconTint(int color);
MotionLayout will also check for setFoo(Drawable()) and use a ColorDrawable
You can create a subclass of MaterialButton and implement the method needed
setInconTint(int color)
class MyButton extends MaterialButton {
public MyButton(#NonNull Context context) {
super(context);
}
public MyButton(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyButton(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
void setIconTint(int color) {
ColorStateList colorStateList = new ColorStateList(new int[1][0],new int[]{color});
setIconTint(colorStateList);
}
}
This would then work with MotionLayout. This will create many objects during animation but they will be short lived.
I have a custom progress bar used around the app which has a attribute for setting the indeterminate color. Since I am using the support library I am trying to have the progress bar colored on older android versions too.
In attrs I have something like:
<declare-styleable name="TestView">
<attr name="testColor" format="color"/>
</declare-styleable>
When declaring the view:
<com.TestView
....
app:testColor="#color"
/>
Then my custom view is like this:
public class TestView extends ProgressBar {
public TestView(Context context) {
super(context);
applyTint(Color.WHITE);
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TestView, 0, 0);
try {
applyTint(attributes.getColor(R.styleable.TestView_testColor, Color.WHITE));
} finally {
attributes.recycle();
}
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, android.support.design.R.style.Base_Widget_AppCompat_ProgressBar);
TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TestView, 0, 0);
try {
applyTint(attributes.getColor(R.styleable.TestView_testColor, Color.WHITE));
} finally {
attributes.recycle();
}
}
private void applyTint(int color) {
if (Build.VERSION.SDK_INT >= 21) {
setIndeterminateTintList(ColorStateList.valueOf(color));
} else {
getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
}
}
The problem I have is that it seems that the attribute of color is somehow shared between instances of TestView.
How can I have each view to keep its own attribute value?
Later edit: seems to work fine on android 6 but fails on 4.4.2
Got the answer at https://stackoverflow.com/a/37434219/379865
Basically I had to update my tint apply code with:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
DrawableCompat.setTint(DrawableCompat.wrap(getIndeterminateDrawable()), color);
else {
DrawableCompat.wrap(getIndeterminateDrawable()).mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
I have to implement Automatic Event Tracking in android
Need to automatically collect analytics data on all button clicks and page views but it has to be done in a generic way so that I don't need to write the Analytics code again for every click.
Example: I have 2 buttons on my activity each of them having a click listener. Now i want to call Analytics.track(String buttonName) so that i do not have to add this in every click listener. The data that should be passed in tracking is button Name.
A way (probably not the ultimate way) to do that could be extending Button (or View), and putting analytics code into the View#performClick() method.
As for the buttonName, it can be a a field of your custom View class, that you can set programmatically or even via an XML custom attribute.
Global implementation :
Create a custom XML attribut : create a file named attrs.xml in the ressource folder :
<resources>
<declare-styleable name="tracking">
<attr name="tracking_name" format="string" />
</declare-styleable>
</resources>
Create a custom Button (or View) class, that overwrite performClick() method and call Analytics.track() with the string gotten from your XML custom attribute or set programmatically :
public class TrackedClickButton extends Button {
private String mTrackingName;
public TrackedClickButton(Context context) {
super(context);
}
public TrackedClickButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public TrackedClickButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
#TargetApi(21)
public TrackedClickButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.tracking);
if (array.hasValue(R.styleable.tracking_name)) {
mTrackingName = array.getString(R.styleable.tracking_name);
}
}
public void setTrackingName(String trackingName) {
this.mTrackingName = trackingName;
}
#Override
public boolean performClick() {
//Make sure the view has an onClickListener that listened the click event,
//so that we don't report click on passive elements
boolean clickHasBeenPerformed = super.performClick();
if(clickHasBeenPerformed && mTrackingName != null) {
Analytics.track(mTrackingName);
}
return clickHasBeenPerformed;
}
}
Use your new class everywhere you want to track the event, for example in a layout file :
<com.heysolutions.dentsply.Activites.MainActivity.TrackedClickButton
xmlns:tracking="http://schemas.android.com/apk/res-auto"
android:id="#+id/button"
android:layout_width="50dp"
android:layout_height="50dp"
tracking:tracking_name="buttonTrackingName"/>
Once again, this is one way, may be some other easier/better/better with your implementation ways :)
Create your own clickListener in Kotlin.
In this exemple, I put a debounceTime variable to prevent double clicking :
fun View.clickAndTrack(debounceTime: Long = 500L, action: () -> Unit) {
this.setOnClickListener(object : View.OnClickListener {
private var lastClickTime: Long = 0
override fun onClick(v: View) {
if (SystemClock.elapsedRealtime() - lastClickTime < debounceTime) return
else {
// do your Analytics action here
action()
}
lastClickTime = SystemClock.elapsedRealtime()
}
})
}
You can use Activity#dispatchTouchEvent(android.view.MotionEvent) to intercept touch events.
Just for extending CheckBoxPreference or SwitchPreference on Android Lollipop, the widget (the checkbox or the switch) won't have animation anymore.
I'd like to extend SwitchPreference to force api < 21 to use SwitchCompat instead of the default one they are using (which is obviously wrong).
I am using the new AppCompatPreferenceActivity with appcompat-v7:22.1.1 but that doesn't seem to affect the switches.
The thing is that with just extending those classes, without adding any custom layout or widget resource layout, the animation is gone.
I know I can write two instances of my preference.xml (on inside values-v21) and it will work... But I'd like to know why is this happening and if somebody knows a solution without having two preference.xml.
Code example:
public class SwitchPreference extends android.preference.SwitchPreference {
public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public SwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwitchPreference(Context context) {
super(context);
}
}
This or the same for CheckBoxPreference and then using:
<com.my.package.SwitchPreference />
Will make the animation in a Lollipop device to be gone.
--
Another thing I tried for the SwitchPreference (that I can with CheckBoxPreference) is to give a layout with the default id but #android:id/switchWidgetis not public while #android:id/checkbox is. I also know I can use a <CheckBoxPreference /> and give a widget layout that is in fact a SwitchCompat, but I'd like to avoid that (confusing the names).
It seems I found a fix for your issue.
Extensive Explanation
In SwitchCompat, when toggling the the switch, it tests a few functions before playing the animation: getWindowToken() != null && ViewCompat.isLaidOut(this) && isShown().
Full method:
#Override
public void setChecked(boolean checked) {
super.setChecked(checked);
// Calling the super method may result in setChecked() getting called
// recursively with a different value, so load the REAL value...
checked = isChecked();
if (getWindowToken() != null && ViewCompat.isLaidOut(this) && isShown()) {
animateThumbToCheckedState(checked);
} else {
// Immediately move the thumb to the new position.
cancelPositionAnimator();
setThumbPosition(checked ? 1 : 0);
}
}
By using a custom view extending SwitchCompat, I found out, that isShown() always returns false, because the at third iteration of the while, parent == null.
public boolean isShown() {
View current = this;
//noinspection ConstantConditions
do {
if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
ViewParent parent = current.mParent;
if (parent == null) {
return false; // We are not attached to the view root
}
if (!(parent instanceof View)) {
return true;
}
current = (View) parent;
} while (current != null);
return false;
}
Interestingly, the third parent is the second attribute passed to getView(View convertView, ViewGroup parent) in Preference, means the PreferenceGroupAdapter didn't get a parent passed to its own getView(). Why this happens exactly and why this happens only for custom preference classes, I don't know.
For my testing purposes, I used the CheckBoxPreference with a SwitchCompat as widgetLayout, and I also didn't see animations.
Fix
Now to the fix: simply make your own view extending SwitchCompat, and override your isShown() like this:
#Override
public boolean isShown() {
return getVisibility() == VISIBLE;
}
Use this SwitchView for your widgetLayout style, and animations work again :D
Styles:
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
…
<item name="android:checkBoxPreferenceStyle">#style/Preference.SwitchView</item>
…
</style>
<style name="Preference.SwitchView">
<item name="android:widgetLayout">#layout/preference_switch_view</item>
</style>
Widget layout:
<de.Maxr1998.example.preference.SwitchView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null"
android:clickable="false"
android:focusable="false" />
Sometimes Extending from a Class is not the best solution. To avoid loosing the animations you could instead Compose it, I meant creating a Class where you have a SwitchPreference field variable and apply the new logic to it. It's like a wrapper. This worked for me.
i manage to fix it like this and animations is working before it was going to the state directly without animation:
FIX:
CustomSwitchCompat.class
public class CustomSwitchCompat extends SwitchCompat {
public CustomSwitchCompat(Context context) {
super(context);
}
public CustomSwitchCompat(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean isShown() {
return getVisibility() == VISIBLE;
}
}
In your layout do this: preference_switch_layout.xml
<com.example.CustomSwitchCompat
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#null"
android:clickable="false"
android:focusable="false"
app:switchMinWidth="55dp"/>
and in your preference.xml do this:
<CheckBoxPreference
android:defaultValue="false"
android:key=""
android:widgetLayout="#layout/preference_switch_layout"
android:summary=""
android:title="" />
I was having this issue, when I was using custom layout (app:layout) for SwitchPreference. At first, switch animation was triggered, but after a little scrolling it stopped and switch was jumping without animation. I tried every solution from stackoverflow, but nothing helped.
After debugging of SwitchCompat.setChecked method I found out that this condition is failing:
public void setChecked(boolean checked) {
...
if (getWindowToken() != null && ViewCompat.isLaidOut(this)) {
animateThumbToCheckedState(checked);
} else {
// Immediately move the thumb to the new position.
cancelPositionAnimator();
setThumbPosition(checked ? 1 : 0);
}
}
Concretely ViewCompat.isLaidOut(this) returned false. I guess this is a bug either in View or Preference (or subclasses). Anyway, I was able to fix this with little hack.
I created a subclass of SwitchCompat and did override setChecked method, where I call requestLayout() and in onNextLayout I call SwitchCompat's setChecked method. This guarantees that isLaidOut condition is true when changing checked state.
Full code of custom SwitchCompat:
class SwitchCompatFix #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = androidx.appcompat.R.attr.switchStyle,
): SwitchCompat(context, attrs, defStyleAttr) {
override fun setChecked(checked: Boolean) {
doOnNextLayout {
post { super.setChecked(checked) }
}
requestLayout()
}
}
public class SwitchPreference extends android.preference.SwitchPreference {
public SwitchPreference(Context context) {
this(context, null);
}
public SwitchPreference(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.checkBoxPreferenceStyle);
}
public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
try {
Field canRecycleLayoutField = Preference.class.getDeclaredField("mCanRecycleLayout");
canRecycleLayoutField.setAccessible(true);
canRecycleLayoutField.setBoolean(this, true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
I have developed a very huge application and now i have a requirement of having custom font for all controls in the application. so I want to know the better way to change the font in one shot. The application has more than a hundred XML layout. and i cant change all controls to a custom component with custom font. Please provide a solution to Change the font without altering all the controls in XML.
Do something like this
pacage com.prac;
class MyFontedTextView extends TextView {
public FontedTextView(Context context) {
super(context);
init();
}
public FontedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FontedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
String otfName = "MyCustomOtfFileWhichIPutInAssetsFolder.otf";
Typeface font = Typeface.createFromAsset(context.getAssets(), otfName);
this.setTypeface(font);
}
}
Now replace this all over in xml file from your TextViews
<com.prac.MyFontedTextView .... instead of <TextView
This change you have to do all over for it to apply
also for the case of button text . Button is also subclass of TextView
So the same can work for button's too
Hope this help or can lead you to the solution you are looking