I want to have the following:
a textview that
.)changes its background when clicked
.)maintains that background until it is clicked again
it all comes down to the "checkable" state, but i couldnt figure out how this exactly works. here is the xml i am using for background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="#drawable/menuselected"
android:state_pressed="true" />
<!-- checked -->
<item android:drawable="#drawable/menuselected"
android:state_checked="true" />
<!-- default -->
<item android:drawable="#drawable/transpixel"/>
</selector>
Update: it partly works now. I adopted most of the code from http://kmansoft.com/2011/01/11/checkable-image-button/ for my custom Textview. I did this as actually, I need the functionality of a radio button as well.
Now I can check a Textview, but I cant uncheck it. Does anybody see why that could be the case?
You can use CheckedTextView with
checkMark null
and
background your selectable
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="#null"
android:background="#drawable/selectable"/>
your selectable can be
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/selector" />
</selector>
Make a custom TextView implementing android.widget.Checkable interface. That should be sufficient to make your selector work.
Below is the example implementation:
public class CheckableTextView extends TextView implements Checkable {
private boolean isOn=false;
public CheckableTextView(Context context) {
super(context);
}
public CheckableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked())
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
return drawableState;
}
#Override
public void setChecked(boolean checked) {
isOn=checked;
refreshDrawableState();
}
#Override
public boolean isChecked() {
return isOn;
}
#Override
public void toggle() {
isOn=!isOn;
refreshDrawableState();
}
}
Related
I want to have the following:
a textview that
.)changes its background when clicked
.)maintains that background until it is clicked again
it all comes down to the "checkable" state, but i couldnt figure out how this exactly works. here is the xml i am using for background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="#drawable/menuselected"
android:state_pressed="true" />
<!-- checked -->
<item android:drawable="#drawable/menuselected"
android:state_checked="true" />
<!-- default -->
<item android:drawable="#drawable/transpixel"/>
</selector>
Update: it partly works now. I adopted most of the code from http://kmansoft.com/2011/01/11/checkable-image-button/ for my custom Textview. I did this as actually, I need the functionality of a radio button as well.
Now I can check a Textview, but I cant uncheck it. Does anybody see why that could be the case?
You can use CheckedTextView with
checkMark null
and
background your selectable
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="#null"
android:background="#drawable/selectable"/>
your selectable can be
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/selector" />
</selector>
Make a custom TextView implementing android.widget.Checkable interface. That should be sufficient to make your selector work.
Below is the example implementation:
public class CheckableTextView extends TextView implements Checkable {
private boolean isOn=false;
public CheckableTextView(Context context) {
super(context);
}
public CheckableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked())
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
return drawableState;
}
#Override
public void setChecked(boolean checked) {
isOn=checked;
refreshDrawableState();
}
#Override
public boolean isChecked() {
return isOn;
}
#Override
public void toggle() {
isOn=!isOn;
refreshDrawableState();
}
}
I use :
public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked, };
private boolean mChecked;
public CheckableRelativeLayout(Context context) {
super(context);
}
public CheckableRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
setChecked(!mChecked);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
based on this and i use it like this :
<com.example.components.CheckableRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/CheckableRelativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector_background_navigation_item"
android:orientation="horizontal"
android:padding="10dp"
android:gravity="center_vertical" >
<ImageView
android:id="#+id/navigationDrawerItemImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/navigationDrawerItemTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/navigationDrawerItemImageView"
android:layout_margin="5dp"
android:layout_toRightOf="#+id/navigationDrawerItemImageView"
android:textColor="#drawable/selector_text_navigation_item"
android:textSize="#dimen/text_size_small" />
</com.example.components.CheckableRelativeLayout>
in a list item where selector_background_navigation_item defined like this :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="false" android:state_pressed="false" android:drawable="#color/darkblue"></item>
<item android:state_pressed="true" android:drawable="#color/sirinblue"></item>
<item android:state_checked="true" android:drawable="#color/white"></item>
</selector>
and selector_text_navigation_item defined like this :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="false" android:state_pressed="false" android:color="#color/white"></item>
<item android:state_pressed="true" android:color="#color/lightblue"></item>
<item android:state_checked="true" android:color="#color/lightblue"></item>
</selector>
Now, the problem is that the TextView Selector does not respond to the check events, while the CheckableRelativeLayout does, but both respond correctly to simple presses, whats the best way to fix this? maybe CheckableRelativeLayout missing some super call that handles its children checked state?
adding android:duplicateParentState="true" to the TextView did the trick!
I have a custom widget which is basically an EditText which has a small 'clear' button to the right, which cleans the edittext.
I built it as a subclass of LinearLayout, which adds the EditText and the Button in the constructor.
Everything works the way it should, except that I want the whole LinearLayout to have a Focused style when the EditText has focus; I want the user to see it as an EditText, not as a container with an EditText.
I attempted using addStatesFromChildren, but it does not seem to work. Code attached.
ErasableField.java
public class ErasableField extends LinearLayout {
private EditText editText;
private View button;
public ErasableField(Context context) {
super(context);
init(null);
}
public ErasableField(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ErasableField(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
this.setOrientation(HORIZONTAL);
LayoutInflater inflater = LayoutInflater.from(getContext());
editText = (EditText) inflater.
inflate(R.layout.erasable_field_edittext, this, false);
button = inflater.inflate(R.layout.erasable_field_button, this, false);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ErasableField);
boolean password = a.getBoolean(R.styleable.ErasableField_password, false);
if (password) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT);
}
}
editText.setFocusable(true);
editText.setClickable(true);
editText.setFocusableInTouchMode(true);
this.setClickable(false);
this.setFocusable(false);
this.setFocusableInTouchMode(false);
this.setGravity(Gravity.CENTER_VERTICAL);
this.setBackgroundResource(android.R.drawable.editbox_background);
addView(editText);
addView(button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
editText.getText().clear();
}
});
this.addStatesFromChildren();
}
public Editable getText() {
return editText.getText();
}
public void setText(CharSequence text) {
editText.setText(text);
}
public void setText(int resId) {
editText.setText(resId);
}
public void setText(char[] text, int start, int len) {
editText.setText(text, start, len);
}
}
erasable_field_edittext.xml
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="fill_parent"
android:background="#color/white"
android:singleLine="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textCursorDrawable="#color/grey"
>
</EditText>
erasable_field_button.xml
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/Button.Delete"
>
</ImageButton>
Nevermind the missing style, it's just a red button with a little cross, it looks like this:
When the user taps the edittext area, the style should change to a focused style.
I.e. (ignore the different button look ):
The style I'm trying to use a built in from Android:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="#drawable/editbox_background_focus_yellow" />
<item android:drawable="#drawable/editbox_background_normal" />
</selector>
Any help?
It's fixed by changing
this.addStatesFromChildren();
to
this.setAddStatesFromChildren(true);
...
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
I want to color to background of an image View when it get pressed.
I tried to do it with selector that changes the image for the different states (pressed and not pressed).
The problem now is that the image replaces the previous with the exact same size and I want it be to much larger.
I use a custom layout that implements checkable, and I tried also to overload the onCreateDrawableState function, but it never being called.
Help please..
This is how it looks:
before pressing:
after pressing:
This is the item list custom layout:
public class ListItem extends RelativeLayout implements Checkable, OnClickListener {
private View mItemChecked = null;
private ImageView imageSkinEdit = null;
/*private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_pressed
};*/
public ListItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListItem(Context context) {
super(context);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
try {
mItemChecked = this.findViewById(R.id.itemlist_checkedd);
imageSkinEdit = (ImageView)this.findViewById(R.id.skinEdit);
imageSkinEdit.setOnClickListener(this);
} catch (Exception e) {
}
}//onFinishInflate
#Override
public boolean isChecked() {
if(mItemChecked != null) {
return ((Checkable)mItemChecked).isChecked();
}
return false;
}
#Override
public void setChecked(boolean checked) {
if(mItemChecked != null) {
if (checked) {
mItemChecked.setVisibility(View.VISIBLE);
} else {
mItemChecked.setVisibility(View.GONE);
}
}
}
#Override
public void toggle() {
if(mItemChecked != null) {
((Checkable)mItemChecked).toggle();
}
}
#Override
public void onClick(View v) {
final int id = v.getId();
switch (id) {
case R.id.skinEdit:
//handleEdit();
break;
}//switch
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
Log.d("onCreateDrawableState", "onCreateDrawableState");
return super.onCreateDrawableState(extraSpace + 1);
/*final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
Log.d("ListItem", "isChecked");
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;*/
}
}
The selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#drawable/ic_go_edit_pressed" />
<item
android:state_pressed="false"
android:drawable="#drawable/ic_go_edit" />
</selector>
Image View XML:
<ImageView
android:id="#+id/skinEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:src="#drawable/edit_skin_selector" />
Try calling refreshDrawableState() at the end of setChecked. That will get onCreateDrawableState to be called
#Override
public void setChecked(boolean checked) {
if(mItemChecked != null) {
if (checked) {
mItemChecked.setVisibility(View.VISIBLE);
} else {
mItemChecked.setVisibility(View.GONE);
}
}
refreshDrawableState();
}
Finally I did it using a different method. You can see it in my next question
you should add a default state to your selector too
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#drawable/button_pressed" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="#drawable/button_focused" /> <!-- focused -->
<item android:state_hovered="true"
android:drawable="#drawable/button_focused" /> <!-- hovered -->
<item android:drawable="#drawable/button_normal" /> <!-- default -->
</selector>
I have known, that onCreateDrawableState not call i.e. ListItem has not background drawable.