I have a custom view LockButton which is just a FrameLayout that holds two specific child views. It holds an ImageButton as well as a type of custom view ProgressIndicator. This question centers around the behavior of LockButton and ImageButton. In particular I have added a custom state to the LockButton. The ImageButton has a StateListDrawable for it's source and a ColorStateList for its background.
The problem is when I change the state in LockButton, that change is not displayed in the ImageButton
What am I doing wrong?
LockButton Class:
public class LockButton extends FrameLayout {
private static final int[] STATE_LOCKED = {R.attr.state_locked};
private boolean locked = true;
View button;
public LockButton(#NonNull Context context) {
this(context, null);
}
public LockButton(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.lock_button, this);
button = findViewById(R.id.lock_button_actual);
}
#Override
public void setOnClickListener(#Nullable OnClickListener l) {
button.setOnClickListener(l);
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState;
if(locked){
drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, STATE_LOCKED);
} else {
drawableState = super.onCreateDrawableState(extraSpace);
}
return drawableState;
}
public void lock(boolean locked) {
if(this.locked != locked) {
this.locked = locked;
}
refreshDrawableState();
button.refreshDrawableState();
}
public static class ProgressCircle extends View {...}
}
LockButton Layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:state_locked="true"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<view class="com.example.LockButton$ProgressCircle"
android:layout_width="68dp"
android:layout_height="68dp"
android:layout_gravity="center"
/>
<ImageButton
android:id="#+id/lock_button_actual"
android:layout_width="#dimen/fab_size"
android:layout_height="#dimen/fab_size"
android:background="#drawable/button_lock_background"
android:src="#drawable/icon_lock_status"
android:tint="#color/white"
android:elevation="6dp"
android:layout_gravity="center"
android:duplicateParentState="true"
/>
</FrameLayout>
Attributes XML:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="lockState">
<attr name="state_locked" format="boolean" />
</declare-styleable>
</resources>
Drawable icon_lock_status.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:state_locked="false" android:drawable="#drawable/icon_lock_open" />
<item app:state_locked="true" android:drawable="#drawable/icon_lock_closed"/>
</selector>
Drawable button_lock_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight"
>
<item>
<shape android:shape="oval">
<solid android:color="#color/button_lock_color" />
</shape>
</item>
</ripple>
Color button_lock_color.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:state_locked="false" android:color="#color/grey_dark" />
<item app:state_locked="true" android:color="#color/vibrant_red" />
</selector>
When I need to change the state, I simply call
lockButton.lock(true); // or false
on the LockButton
I have also tested to see if using default states instead of my custom one would work any better. This did not help. The ImageButton still does not respond to changes in the default states.
Setting a StateListDrawable as a background to the LockButton (FrameLayout) does work. So the state change is working properly on the parent, it is just not reflected in the child.
I'm trying to make my disabled EditText's style like in guidelines:
https://www.google.com/design/spec/components/text-fields.html#text-fields-labels
<EditText
android:id="#+id/account_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/test_account"
android:enabled="false" />
I've already implemented material design styles, so all my widgets look material.
The question is: Is there any way to get this dotted underline using styles, without putting additional views?
My styles.xml:
<style name="Theme.Main" parent="#style/Theme.AppCompat.Light.NoActionBar">
<item name="android:editTextStyle">#style/android:Widget.Material.EditText</item>
</style>
Finally I managed to do as you've shown, It looks like
Create dotted.xml inside drawable folder and paste these
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="1dp"
android:left="-2dp"
android:right="-2dp"
android:top="-2dp">
<shape android:shape="rectangle">
<stroke
android:width="0.5dp"
android:color="#android:color/black" />
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#030310"
android:dashGap="5dp"
android:dashWidth="5dp" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
</item>
</layer-list>
Then simply set the android:background attribute to dotted.xml we just created. Your EditText looks like this.
<EditText
android:id="#+id/account_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test account"
android:background="#drawable/dotted"
style="#style/Theme.Main" <!--Its your custom style-->
android:enabled="false" />
AFAIK, there is no "stock" way. Take the material design guidelines as it is, a guideline ;)
Finally I ended up with a custom view. It might be not the most elegant solution but it solves my task.
Hope it will be helpful for someone.
DisabledTextField.java:
package com.kabunov.example.ui.common;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.kabunov.example.R;
public final class DisabledTextField extends FrameLayout {
private TextView tvCaption;
private TextView tvText;
private View divider;
public DisabledTextField(final Context context) {
this(context, null);
}
public DisabledTextField(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public DisabledTextField(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
View.inflate(getContext(), R.layout.component_disabled_text_field, this);
tvCaption = (TextView) findViewById(R.id.caption);
tvText = (TextView) findViewById(R.id.value);
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DisabledTextField);
if (null != typedArray) {
final String captionText = typedArray.getString(R.styleable.DisabledTextField_caption);
if (TextUtils.isEmpty(captionText)) {
tvCaption.setVisibility(GONE);
} else {
tvCaption.setText(captionText);
tvCaption.setVisibility(VISIBLE);
}
final String text = typedArray.getString(R.styleable.DisabledTextField_disabledText);
tvText.setText(text);
typedArray.recycle();
}
}
public final void setCaption(final CharSequence caption) {
this.tvCaption.setText(caption);
this.tvCaption.setVisibility(VISIBLE);
}
public final void setDisabledText(final CharSequence value) {
this.tvText.setText(value);
}
}
/layout/component_disabled_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/DisabledTextFieldCaption"/>
<TextView
android:id="#+id/value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/DisabledTextFieldValue"/>
<View
android:id="#+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#drawable/disabled_text_field_background"
android:layerType="software"/>
</LinearLayout>
/values/attributes_disabled_text_field.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DisabledTextField">
<attr name="caption" format="string"/>
<attr name="disabledText" format="string"/>
</declare-styleable>
</resources>
/drawable/disabled_text_field_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="-1dp"
android:left="-1dp"
android:right="-1dp"
android:top="0dp">
<shape android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#color/text_color_disabled_text_view"
android:dashGap="2dp"
android:dashWidth="1dp" />
<solid android:color="#android:color/transparent" />
</shape>
</item>
</layer-list>
/values/styles.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="text_color_disabled_text_view">#61000000</color>
<style name="DisabledTextFieldCaption">
<item name="android:textSize">12sp</item>
<item name="android:textColor">#color/text_color_disabled_text_view</item>
</style>
<style name="DisabledTextFieldValue">
<item name="android:layout_marginTop">3dp</item>
<item name="android:layout_marginBottom">8dp</item>
<item name="android:textSize">16sp</item>
<item name="android:textColor">#color/text_color_disabled_text_view</item>
</style>
</resources>
Example of usage:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.kabunov.example.ui.common.DisabledTextField
android:id="#+id/example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:caption="Some caption"
custom:disabledText="Disabled text"/>
</LinearLayout>
And the result:
Use Selector as view's background, like following codes :
my_selector.xml:
<?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/cell_bg_e" />
<item android:state_checked="true" android:drawable="#drawable/cell_bg_e" />
<item android:state_selected="true" android:drawable="#drawable/cell_bg_e" />
<item android:drawable="#drawable/cell_bg_n_trans" />
</selector>
MyView.java
public class MyView extends LinearLayout
{
public MyView(Context context, CharSequence text, Drawable drawable) {
super(context);
setBackgroundResource(R.drawable.my_selector);
}
}
it works on all the devices except for some certain 800x480 resolution device(lick htc g12)
why ?
You should ensure the view you have set this selector drawable as a background of is clickable.
You can do this in code:
public MyView(Context context, CharSequence text, Drawable drawable) {
super(context);
setClickable(true);
setBackgroundResource(R.drawable.my_selector);
}
Alternatively in XML:
<MyView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/my_selector"
android:clickable="true"
android:orientation="horizontal"/>
I have textview which I want to change the color when I focus or cliclked it like a link text in web I have try to follow this but it still doesn't work
please help, thanks
this is my java code
public class TextColorActivity extends Activity {
/** Called when the activity is first created. */
ColorStateList cl = null;
private TextView title;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
title = (TextView)findViewById(R.id.hello);
try {
Log.d("test","try");
XmlResourceParser xpp = getResources().getXml(R.drawable.selector_txt);
cl = ColorStateList.createFromXml(getResources(), xpp);
} catch (Exception e) {}
title.setTextColor(cl);
title.setFocusable(true);
title.setClickable(true);
title.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d("test","click");
}
});
}
this is my selector_txt.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:color="#color/Darkgoldenrod"/>
<item android:state_pressed="true" android:state_enabled="false"
android:color="#color/Darkgreen" />
<item android:state_enabled="false" android:color="#color/Red" />
<item android:color="#color/blue"/>
and this is my main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/hello"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
android:textSize="30dp"
android:textStyle="bold"
android:duplicateParentState="true"/>
You can also set your color in the xml if you want. Just create a color folder in your res folder and move the xml file there then you can set it via android:textColor="#color/selector_txt"
Regards the problem you're having. Android will always use the first match in a selector. If a TextView is pressed it is also focused. So add android:pressed="false" to your first item or move the line after the pressed status line.
That's the full xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#color/Red" />
<item android:state_pressed="true" android:color="#color/Darkgreen" />
<item android:state_focused="true" android:color="#color/Darkgoldenrod"/>
<item android:color="#color/blue"/>
</selector>
Is there an easy way to use a custom image for a checkbox? I'm looking to duplicate the "starred" behavior of gmail. So I want to have a checkbox that, when checked, is a filled in star. And when unchecked is an empty star. Do I have to use an imageview and do my own logic myself?
Create a drawable checkbox selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/checkbox"
android:state_checked="false"/>
<item android:drawable="#drawable/checkboxselected"
android:state_checked="true"/>
<item android:drawable="#drawable/checkbox"/>
</selector>
Make sure your checkbox is like this android:button="#drawable/checkbox_selector"
<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:button="#drawable/checkbox_selector"
android:text="CheckBox"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#color/Black" />
Checkboxes being children of Button you can just give your checkbox a background image with several states as described here, under "Button style":
...and exemplified here:
Copy the btn_check.xml from android-sdk/platforms/android-#/data/res/drawable to your project's drawable folder and change the 'on' and 'off' image states to your custom images.
Then your xml will just need android:button="#drawable/btn_check"
<CheckBox
android:button="#drawable/btn_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true" />
If you want to use different default android icons, you can use android:button="#android:drawable/..."
res/drawable/day_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/dayselectionunselected"
android:state_checked="false"/>
<item android:drawable="#drawable/daysselectionselected"
android:state_checked="true"/>
<item android:drawable="#drawable/dayselectionunselected"/>
</selector>
res/layout/my_layout.xml
<CheckBox
android:id="#+id/check"
android:layout_width="39dp"
android:layout_height="39dp"
android:background="#drawable/day_selector"
android:button="#null"
android:gravity="center"
android:text="S"
android:textColor="#color/black"
android:textSize="12sp" />
If you have Android open source code, you can find the styles definition under:
src/frameworks/base/core/res/res/values
<style name="Widget.CompoundButton.CheckBox">
<item name="android:background">
#android:drawable/btn_check_label_background
</item>
<item name="android:button">
?android:attr/listChoiceIndicatorMultiple
</item>
</style>
Based on the Enselic and Rahul answers.
It works for me (before and after API 21):
<CheckBox
android:id="#+id/checkbox"
android:layout_width="40dp"
android:layout_height="40dp"
android:text=""
android:gravity="center"
android:background="#drawable/checkbox_selector"
android:button="#null"
app:buttonCompat="#null" />
Try it -
package com;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
public class CheckBoxImageView extends ImageView implements View.OnClickListener {
boolean checked;
int defImageRes;
int checkedImageRes;
OnCheckedChangeListener onCheckedChangeListener;
public CheckBoxImageView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
init(attr, defStyle);
}
public CheckBoxImageView(Context context, AttributeSet attr) {
super(context, attr);
init(attr, -1);
}
public CheckBoxImageView(Context context) {
super(context);
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
setImageResource(checked ? checkedImageRes : defImageRes);
}
private void init(AttributeSet attributeSet, int defStyle) {
TypedArray a = null;
if (defStyle != -1)
a = getContext().obtainStyledAttributes(attributeSet, R.styleable.CheckBoxImageView, defStyle, 0);
else
a = getContext().obtainStyledAttributes(attributeSet, R.styleable.CheckBoxImageView);
defImageRes = a.getResourceId(0, 0);
checkedImageRes = a.getResourceId(1, 0);
checked = a.getBoolean(2, false);
a.recycle();
setImageResource(checked ? checkedImageRes : defImageRes);
setOnClickListener(this);
}
#Override
public void onClick(View v) {
checked = !checked;
setImageResource(checked ? checkedImageRes : defImageRes);
onCheckedChangeListener.onCheckedChanged(this, checked);
}
public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
this.onCheckedChangeListener = onCheckedChangeListener;
}
public static interface OnCheckedChangeListener {
void onCheckedChanged(View buttonView, boolean isChecked);
}
}
Add this attrib -
<declare-styleable name="CheckBoxImageView">
<attr name="default_img" format="integer"/>
<attr name="checked_img" format="integer"/>
<attr name="checked" format="boolean"/>
</declare-styleable>
Use like -
<com.adonta.ziva.consumer.wrapper.CheckBoxImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/checkBox"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:clickable="true"
android:padding="5dp"
app:checked_img="#drawable/check_box_checked"
app:default_img="#drawable/check_box" />
It will fix all your porblems.
Another option is to use a ToggleButton with null background and a custom button.
Bellow an example that includes a selector to the text color as well.
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#drawable/toggle_selector"
android:background="#null"
android:paddingLeft="10dp"
android:layout_centerHorizontal="true"
android:gravity="center"
android:textColor="#drawable/toggle_text"
android:textOn="My on state"
android:textOff="My off state" />
toggle_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_checked="true"
android:drawable="#drawable/state_on" />
<item
android:drawable="#drawable/state_off" />
</selector>
toggle_text.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_checked="true"
android:color="#color/app_color" />
<item
android:color="#android:color/darker_gray" />
</selector>
If you are using custom adapters than android:focusable="false" and android:focusableInTouchMode="false" are nessesury to make list items clickable while using checkbox.
<CheckBox
android:id="#+id/checkbox_fav"
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#drawable/checkbox_layout"/>
In drawable>checkbox_layout.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/uncked_checkbox"
android:state_checked="false"/>
<item android:drawable="#drawable/selected_checkbox"
android:state_checked="true"/>
<item android:drawable="#drawable/uncked_checkbox"/>
</selector>
If you use androidx.appcompat:appcompat and want a custom drawable (of type selector with android:state_checked) to work on old platform versions in addition to new platform versions, you need to use
<CheckBox
app:buttonCompat="#drawable/..."
instead of
<CheckBox
android:button="#drawable/..."
Adding custom drawable in the android:button did not work for me in Material Checkbox version-1.3.0 . I had to set android:drawable="#drawable/checkbox_selector" instead and also set android:button="#null" . You can also add android:drawablePadding to make it look good. However, this makes the entire checkbox clickable (along with the text).