I have a ToggleButton, when you click it, I don't want the state to change. I will handle state changes myself when after I receive feedback from whatever the button toggled. How might I prevent the state change on click?
You can implement your own ToggleButton with overriden toggle() method with empty body.
You could simply use the CheckedTextView instead.
Of course, you need to set a background image and a text based on the state, but other than those (which you might have used already), it's a nice alternative solution.
here's a sample code in case you miss the textOn and textOff attributes:
CheckableTextView.java :
public class CheckableTextView extends CheckedTextView {
private CharSequence mTextOn, mTextOff;
public CheckableTextView (final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckableTextView, defStyle, 0);
mTextOn = a.getString(R.styleable.CheckableTextView_textOn);
mTextOff = a.getString(R.styleable.CheckableTextView_textOff);
a.recycle();
}
public CheckableTextView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckableTextView(final Context context) {
this(context, null, 0);
}
#Override
public void setChecked(final boolean checked) {
super.setChecked(checked);
if (mTextOn == null && mTextOff == null)
return;
if (checked)
super.setText(mTextOn);
else
super.setText(mTextOff);
}
public void setTextOff(final CharSequence textOff) {
this.mTextOff = textOff;
}
public void setTextOn(final CharSequence textOn) {
this.mTextOn = textOn;
}
public CharSequence getTextOff() {
return this.mTextOff;
}
public CharSequence getTextOn() {
return this.mTextOn;
}
}
in res/values/attr.xml :
<declare-styleable name="SyncMeCheckableTextView">
<attr name="textOn" format="reference|string" />
<attr name="textOff" format="reference|string" />
</declare-styleable>
another possible solution would be to use setClickable(false) on the ToggleButton, and handle onTouchListener when the motion action is ACTION_UP .
While I think you can just mark it as disabled, I don't think it is a good idea, as users are used to a certain semantic of such a button.
If you only want to show some state, why don't you use an ImageView and show different images depending on state?
Related
I was recently working with ImageButtons and I came across this new type of ImageButton 'VisibilityAwareImageButton'. It would be really helpful if someone could tell me the usage of this ImageButton and how is it different from the regular ImageButton? Thanks in advance :)
Here's the full source for VisibilityAwareImageButton.
class VisibilityAwareImageButton extends ImageButton {
private int mUserSetVisibility;
public VisibilityAwareImageButton(Context context) {
this(context, null);
}
public VisibilityAwareImageButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VisibilityAwareImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mUserSetVisibility = getVisibility();
}
#Override
public void setVisibility(int visibility) {
internalSetVisibility(visibility, true);
}
final void internalSetVisibility(int visibility, boolean fromUser) {
super.setVisibility(visibility);
if (fromUser) {
mUserSetVisibility = visibility;
}
}
final int getUserSetVisibility() {
return mUserSetVisibility;
}
}
It appears to be almost exactly the same as a regular ImageButton, only it keeps track of the last visibility actually set by the user. The only usage I can find is in the FloatingActionButton source. It is used to keep track of what the user wants the visibility of the view to be while it does it's own internal changes and animations. i.e.
if (child.getUserSetVisibility() != VISIBLE) {
// The view isn't set to be visible so skip changing it's visibility
return false;
}
It's in the design support library and has package visibility, so it seems like Google intends on using it internally (and seemingly only for the FAB implementation at this time).
In this example the contentScrim attribute is set with a color, but I can't figure out how to control when it starts. I woud like to start the color transition sooner.
Can you give me a hint? Thanks in advance.
You'd have to create a class that extends CollapsingToolbarLayout. Something like this (you might need to adjust that so it exactly fits your needs):
public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout {
public static interface Listener {
public void onContentScrimAnimationStarted(boolean showing);
}
private Listener mListener;
public CustomCollapsingToolbarLayout(Context context) {
super(context);
}
public CustomCollapsingToolbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomCollapsingToolbarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setScrimsShown(boolean shown, boolean animate) {
super.setScrimsShown(shown, animate);
if (animate && mListener != null) {
mListener.onContentScrimAnimationStarted(shown);
}
}
public void setListener(Listener listener) {
mListener = listener;
}
}
And just call setListener on your CustomCollapsingToolbarLayout instance.
CustomCollapsingToolbarLayout mToolbarLayout =
(CustomCollapsingToolbarLayout) findViewById(R.id.toolbar_layout);
mToolbarLayout.setListener(new Listener() {
#Override
public void onContentScrimAnimationStarted(boolean showing) {
//do what you want
}
});
EDIT (actually answering the question):
Modify the scrimVisibleHeightTrigger value (with the setScrimVisibleHeightTrigger method of the CollapsingToolbarLayout) to change the starting point of the animation.
I've got a problem with my CustomTextView. I'm trying to get a custom value from my layout-xml file and use this in my setText() method. Unfortunately the setText() method gets called before the constructor and because of this I can't use the custom value in this method.
Here's my code (broken down to the relevant parts):
CustomTextView.class
public class CustomTextView extends TextView {
private float mHeight;
private final String TAG = "CustomTextView";
private static final Spannable.Factory spannableFactory = Spannable.Factory.getInstance();
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "in CustomTextView constructor");
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
this.mHeight = values.getDimension(R.styleable.CustomTextView_cHeight, 20);
}
#Override
public void setText(CharSequence text, BufferType type) {
Log.d(TAG, "in setText function");
Spannable s = getCustomSpannableString(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
private static Spannable getCustomSpannableString(Context context, CharSequence text) {
Spannable spannable = spannableFactory.newSpannable(text);
doSomeFancyStuff(context, spannable);
return spannable;
}
private static void doSomeFancyStuff(Context context, Spannable spannable) {
/*Here I'm trying to access the mHeight attribute.
Unfortunately it's 0 though I set it to 24 in my layout
and it's correctly set in the constructor*/
}
}
styles.xml
<declare-styleable name="CustomTextView">
<attr name="cHeight" format="dimension"/>
</declare-styleable>
layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ctvi="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.mypackage.views.CustomTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/my_fancy_string"
android:textSize="16sp"
ctvi:cHeight="24dp" />
</LinearLayout>
And just as a proof - here's the LogCat output:
30912-30912/com.mypackage.views D/CustomTextView﹕ in setText function
30912-30912/com.mypackage.views D/CustomTextView﹕ in CustomTextView constructor
So as you can see the setText() method is called before the constructor. That's kinda weird and I don't know what I need to change in order to use my custom attribute (cHeight) in the setText-method.
Thanks in advance for any help!
It's the TextView super() constructor that calls your setText() based on the attribute values.
If you really need to access your custom attribute when setting a text value, use a custom attribute for the text as well.
I don't think any of this solutions are good, IMHO. What if you just use a custom method, like setCustomText() instead of overriding the custom TextView.setText(). I think it could be much better in terms of scalability, and hacking / overriding the implementation of the TextView could lead you into future problems.
Cheers!
First of all, remember to always recycle the TypedArray after using it.
TextView calls #setText(CharSequence text, BufferType type) during its construction therefore define a delayed call to setText as so:
private Runnable mDelayedSetter;
private boolean mConstructorCallDone;
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "in CustomTextView constructor");
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
this.mHeight = values.getDimension(R.styleable.CustomTextView_cHeight, 20);
mConstructorCallDone = true;
}
Then inside your setText-override:
public void setText(final CharSequence text, final TextView.BufferType type) {
if (!mConstructorCallDone) {
// The original call needs to be made at this point otherwise an exception will be thrown in BoringLayout if text contains \n or some other characters.
super.setText(text, type);
// Postponing setting text via XML until the constructor has finished calling
mDelayedSetter = new Runnable() {
#Override
public void run() {
CustomTextView.this.setText(text, type);
}
};
post(mDelayedSetter);
} else {
removeCallbacks(mDelayedSetter);
Spannable s = getCustomSpannableString(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
}
Unfortunately it is a limitation on Java, that requires to call super(..) in constructor before anything else. So, your only workaround is to call setText(..) again after you initialize the custom attributes.
Just remember, as setText called also before you initialize your custom attributes, they may have null value and you can get NullPointerException
Check my example of customTextView which capitalize first letter and adds double dots at the and (I use it in all my activities)
package com.example.myapp_android_box_detector;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
public class CapsTextView extends AppCompatTextView {
public Boolean doubleDot;
private Boolean inCustomText = false;
public CapsTextView(Context context){
super(context);
doubleDot = false;
setText(getText());
}
public CapsTextView(Context context, AttributeSet attrs){
super(context, attrs);
initAttrs(context, attrs);
setText(getText());
}
public CapsTextView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initAttrs(context, attrs);
setText(getText());
}
public void initAttrs(Context context, AttributeSet attrs){
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CapsTextView, 0, 0);
doubleDot = a.getBoolean(R.styleable.CapsTextView_doubleDot, false);
a.recycle();
}
#Override
public void setText(CharSequence text, BufferType type) {
if (text.length() > 0){
text = String.valueOf(text.charAt(0)).toUpperCase() + text.subSequence(1, text.length());
// Adds double dot (:) to the end of the string
if (doubleDot != null && doubleDot){
text = text + ":";
}
}
super.setText(text, type);
}
}
I need to create a complex toggle button (rtl checkbox).
I know that LinearLayout can be clickable, and I saw the following example
Can I do it through XML only?
Checkboxes and other views should be directly swapped when using rtl locales.
If you want to make sure you are compliant with RTL languages use the new "start" & "end" params included in JellyBean (marginStart & marginEnd instead of marginLeft/Right & marginRight/Left). Although if you need that behavior for previous versions you might need other tricks.
Note that TextView's will already move completely to the other side of the box: make sure you don't use a lot of WRAP_CONTENT in that case, it may result in misalignments.
LinearLayout can be clickable but not not checkable.
To get RTL toggle view I did the following:
I created a nine-path images to pressed, checked and default state.
Created a selector:
<item android:drawable="#drawable/button1_on" android:state_pressed="true"/>
<item android:drawable="#drawable/button1_selected" android:state_checked="true"/>
<item android:drawable="#drawable/button1_off"/>
3.Created a class that extends Button and Implements Checkable:
public class MyCheckbox extends Button implements Checkable {
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
private boolean mIsChecked = false;
public MyCheckbox(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyCheckbox(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyCheckbox(Context context) {
super(context);
}
#Override
public boolean isChecked() {
return mIsChecked;
}
#Override
public void setChecked(boolean isChecked) {
mIsChecked = isChecked;
}
#Override
public void toggle() {
setChecked(!mIsChecked);
}
#Override
public boolean performClick() {
toggle();
return super.performClick();
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
In the same way it can be implemented on a clickable LinearLayout
In my application, initially I'm setting opacity of TextView to 60.
After that, when the user presses a button, I want to decrease or increase the opacity of the TextView as per pressing in a button that increases it or a button that decreases it.
I have tried this, but every time when I get the opacity of text view its -3 or -1 which is actually not.
public void decreaseOpacity(View v){
int op=txtView.getBackground().getOpacity();// its alwz -ve value
txtView.getBackground().setAlpha(op-1);
}
try this code
public class AlphaTextView extends TextView {
public AlphaTextView(Context context) {
super(context);
}
public AlphaTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlphaTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onSetAlpha(int alpha) {
setTextColor(getTextColors().withAlpha(alpha));
setHintTextColor(getHintTextColors().withAlpha(alpha));
setLinkTextColor(getLinkTextColors().withAlpha(alpha));
return true;
}
}
Drawable.getOpacity() dos not take into account changes made by setAlpha(). See the docs:
Note that the returned value does not take into account a custom alpha
or color filter that has been applied by the client through the
setAlpha(int) or setColorFilter(ColorFilter) methods.
You may need to store the alpha value as a variable and not use getOpacity(), on example:
private int mTextViewAlpha = 255;
public void decreaseOpacity(View v){
if ( mTextViewAlpha-- <= 0 ) mTextViewAlpha = 0;
txtView.getBackground().setAlpha(mTextViewAlpha);
txtView.getBackground().invalidateSelf();
}