Android: custom selector state never gets "true" - android

I'm struggling with this already from hours and I cannot find a solution, I hope someone can help me.
I have a ListView with a custom adapter extending BaseAdapter and each element of the list extends a LinearLayout containing only a TextView. I started basically from How to add a custom button state.
This is the list_item.xml layout file of the list element:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.mypackage"
android:id="#+id/newItem"
app:state_playing="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/item_selector"
android:orientation="horizontal" >
<TextView
android:id="#+id/textview"
style="#style/Text_View_Style_White"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="marquee"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:padding="3dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#drawable/item_text_selector" />
</LinearLayout>
Here is the relevant code of the BaseAdapter:
class AlbumListAdapter extends BaseAdapter implements Checkable {
private Context mContext;
public AlbumListAdapter(Context context) {
mContext = context;
}
#Override
public int getCount() {
return listSize; // is a variable
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
AlbumViewHolder albumHolder = null;
if (convertView == null) {
// Inflate the layout
LayoutInflater li = getActivity().getLayoutInflater();
convertView = li.inflate(R.layout.list_item, null);
albumHolder = new AlbumViewHolder(mContext, null);
albumHolder.albumTitle = (TextView) convertView
.findViewById(R.id.textview);
convertView.setTag(albumHolder);
} else {
albumHolder = (AlbumViewHolder) convertView.getTag();
}
albumHolder.albumTitle.setText("Some text in the TextView");
albumHolder.albumTitle.setSelected(true);
if (myBooleanCondition)
{
albumHolder.setPlaying(true);
albumHolder.refreshDrawableState();
for (int i = 0; i < albumHolder.getDrawableState().length; i++) {
if (albumHolder.getDrawableState()[i] != 0)
System.out.println(TAG + " State i = "
+ i + " -> " + getResources().getResourceEntryName(albumHolder.getDrawableState()[i]));
}
}
return convertView;
}
}
and here the list element class extending the LinearLayout:
static class AlbumViewHolder extends LinearLayout {
private static final int[] STATE_PLAYING_SET = { R.attr.state_playing };
private boolean mIsPlaying = false;
TextView albumTitle;
public AlbumViewHolder(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setPlaying(boolean isPlaying) {
mIsPlaying = isPlaying;
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super
.onCreateDrawableState(extraSpace + 1);
if (mIsPlaying) {
System.out.println(TAG
+ " - onCreateDrawableState() -> mIsPlaying = "
+ mIsPlaying);
mergeDrawableStates(drawableState, STATE_PLAYING_SET);
}
return drawableState;
}
}
Here is the attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="music">
<attr name="state_playing" format="boolean" />
<attr name="playing" format="boolean" />
</declare-styleable>
</resources>
and here the 2 selectors trying to use the selector state state_playing, one for the background of the LinearLayout of each element (item_selector.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/com.example.mypackage">
<item android:drawable="#color/blue_fluo3" app:state_playing="true"/>
<item android:drawable="#color/orange_fluo" android:state_pressed="true" app:state_playing="false"/>
<item android:drawable="#color/orange_fluo" android:state_activated="true" app:state_playing="false"/>
<item android:drawable="#android:color/transparent" app:state_playing="false"/>
</selector>
and one for the textColor of the TextView (item_text_selector.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/com.example.mypackage">
<item app:state_playing="true" android:color="#color/orange_fluo"/>
<item android:state_pressed="true" app:state_playing="false" android:color="#color/black"/>
<item android:state_activated="true" app:state_playing="false" android:color="#color/black"/>
<item app:state_playing="false" android:color="#android:color/white"/>
</selector>
The result is the same for both: the state_playing seems to work but only in state "false", because if I add a line at the beginning of the selector state list with <item app:state_playing="false" android:color="#color/yellow"/> for the text selector and <item android:drawable="#color/red" app:state_playing="false"/> for the background selector everything is working as expected, I can see the LinearLayout with red background and the text with yellow text!
I really don't know what to try anymore... (even if I set the property by default to the objects (app:state_playing="true") in the xml layout it doesn't work!
Any help, hint or experience sharing is extremely appreciated!

It looks okay to me,but I'd suggest setting duplicateParentState in the container layout as below. I have a vague memory of having issues along those lines myself...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.mypackage"
android:id="#+id/newItem"
android:duplicateParentState="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/item_selector"
android:orientation="horizontal" >
<TextView
android:id="#+id/textview"
style="#style/Text_View_Style_White"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="marquee"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:padding="3dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#drawable/item_text_selector" />

Related

How to make a View looks like it is being selected programmatically, if background is ?android:attr/selectableItemBackground (ListView in AlertDialog)

I pretty like the selector behavior generated in navigation drawer.
It has ripple effect.
Its ImageView and TextView has proper color, when being selected.
In my dialog, I try to achieve the same effect, by using the following layout.
label_array_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="?android:attr/selectableItemBackground"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<ImageView
android:duplicateParentState="true"
android:id="#+id/image_view"
android:paddingStart="24dp"
android:paddingLeft="24dp"
android:paddingEnd="16dp"
android:paddingRight="16dp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="?attr/labelIconSelector" />
<TextView
android:duplicateParentState="true"
android:textSize="16sp"
android:id="#+id/text_view"
android:layout_width="0dp"
android:width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="?attr/labelTextViewColorSelector" />
</LinearLayout>
labelIconSelector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<bitmap android:src="#drawable/ic_label_white_24dp"
android:tint="#color/colorPrimaryBrown" />
</item>
<item>
<bitmap android:src="#drawable/ic_label_white_24dp"
android:tint="#ff757575" />
</item>
</selector>
labelTextViewColorSelector
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="#color/colorPrimaryBrown" />
<item android:color="#color/primaryTextColorLight" />
</selector>
In my ArrayAdapter, I try to programmatically select the 1st item via view.setSelected(true)
public class LabelArrayAdapter extends ArrayAdapter<TabInfo> {
private static class ViewHolder {
public final ImageView imageView;
public final TextView textView;
public ViewHolder(View view) {
imageView = view.findViewById(R.id.image_view);
textView = view.findViewById(R.id.text_view);
Utils.setCustomTypeFace(textView, Utils.ROBOTO_REGULAR_TYPE_FACE);
}
}
public LabelArrayAdapter(#NonNull Context context, List<TabInfo> tabInfos) {
super(context, R.layout.label_array_adapter, tabInfos);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflator = LayoutInflater.from(this.getContext());
view = inflator.inflate(R.layout.label_array_adapter, null);
final ViewHolder viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
// Not sure why this is required.
view.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, Utils.dpToPixel(48)));
} else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
TabInfo tabInfo = getItem(position);
if (tabInfo == null) {
holder.imageView.setVisibility(View.INVISIBLE);
holder.textView.setText("(No label)");
} else {
holder.imageView.setVisibility(View.VISIBLE);
holder.textView.setText(tabInfo.getName());
}
if (position == 0) {
view.setSelected(true);
} else {
view.setSelected(false);
}
return view;
}
}
This is how I try to show Dialog via DialogFragment.
return new AlertDialog.Builder(getActivity())
.setTitle("Move to")
.setAdapter(new LabelArrayAdapter(this.getContext(), customTabInfos), (dialog, which) -> {
})
.create();
However, it doesn't work as you can see in the following screenshot. The 1st item doesn't look like it is being selected.
May I know, is there anything I had missed out?
How to make a View looks like it is being selected programmatically, if its background is ?android:attr/selectableItemBackground (ListView in AlertDialog)
Update
Using
alertDialog.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
alertDialog.getListView().setSelection(position);
wouldn't help either.
Here is an approach using a layer list drawable as the background for the view group that represents the item being selected.
First, for API 21+, we define a layer list drawable that displays the selected color when the item is selected. A call to View#setSelected() must be made for this to have an effect.
layer_list.xml (v21)
color/selected is #2000ffe5 for the demos.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector>
<item
android:drawable="#color/selected"
android:state_selected="true" />
</selector>
</item>
<item
android:id="#+id/selectableBackground"
android:drawable="?android:selectableItemBackground" />
</layer-list>
For API < 21, we cannot use android:drawable="?android:selectableItemBackground" in the layer list, so we will fall back to an alternate but similar visual cue.
layer_list < API 21
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector android:exitFadeDuration="#android:integer/config_shortAnimTime">
<item
android:drawable="#color/selected"
android:state_pressed="false"
android:state_selected="true" />
<item
android:drawable="#color/selectable_background"
android:state_pressed="true" />
</selector>
</item>
</layer-list>

Coordinating drawable state between parent and child

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.

Android custom drawable state not working

I am trying to implement a custom drawable state. I can see the state changing inside my custom view, however visually there is no colour change. Any help appreciated!
Here's my code:
selector.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/com.example.test.myapplication">
<item android:drawable="#drawable/defaultt" />
<item android:drawable="#drawable/error" app:my_state="1" />
<item android:drawable="#drawable/success" app:my_state="2" />
</selector>
error.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape
android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="3dip" />
<stroke android:width="3dip" android:color="#ff0000" />
<solid android:color="#ff0000"/>
</shape>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyState">
<attr name="my_state" format="integer" />
</declare-styleable>
</resources>
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.example.test.myapplication.CustomView
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector"
/>
</LinearLayout>
CustomView.java
public class CustomView extends View {
public static final int DEFAULT = 0;
public static final int ERROR = 1;
public static final int SUCCESS = 2;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private int state = DEFAULT;
private static final int[] VALIDATION_STATE = new int[] { R.attr.my_state };
#Override
protected int[] onCreateDrawableState(int extraSpace) {
if( state == DEFAULT ) {
return super.onCreateDrawableState(extraSpace);
}
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (state > 0) {
Log.d("tag", "state is "+state);
mergeDrawableStates(drawableState, VALIDATION_STATE);
}
return drawableState;
}
public void setState(int state) {
this.state = state;
refreshDrawableState();
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity {
private int mCurrentState = CustomView.DEFAULT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mCurrentState != CustomView.ERROR) {
((CustomView) findViewById(R.id.view)).setState(CustomView.ERROR);
mCurrentState = CustomView.ERROR;
} else {
((CustomView) findViewById(R.id.view)).setState(CustomView.SUCCESS);
mCurrentState = CustomView.SUCCESS;
}
}
});
}
}
Selector states should be defined as boolean attributes. You can place them directly inside a <resources> element -- you don't need to place them inside a <styleable> element.
res/attrs.xml:
<resources>
...
<attr name="state_mystate" format="boolean" />
</resources>
When you reference the attribute from a selector <item>, a value of true means the state must exist within the current state set while a value of false means the state must not exist. If you don't include a state on the <item> the it doesn't matter whether it exists within the current state set.
When matching is performed on the selector, the items are matched in the order they are defined. If the first item has no states defined, as it does in your example, it will match all possible states and always be matched first. As a result, you should order your selector items as most-specific to least-specific.
The example below is for drawable, but it applies to all selectors (color, etc.).
res/drawable/my_selector.xml:
<selector>
<!-- Matches only state sets that include app:state_mystate
and don't include android:state_pressed. -->
<item app:state_mystate="true"
android:state_pressed="false" ... />
<!-- Matches all state sets that include state_mystate. -->
<item app:state_mystate="true" ... />
<!-- Matches all possible states. -->
<item ... />
</selector>

Android Custom ListView with Checkbox and changing background color

I have a custom ListView with Checkboxes and 3 TextViews. Here is my layout for list item:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listviewitem_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<CheckBox
android:id="#+id/faxCheckBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false" />
<LinearLayout android:orientation="horizontal"
android:id="#+id/faxbox_item_first_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/faxCheckBox"
android:paddingTop="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<TextView android:id="#+id/fax_number_textView"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:lines="1"
android:ellipsize="start"
android:textSize="12sp"
android:paddingRight="5dp"/>
<TextView android:id="#+id/fax_send_date_textView"
android:layout_width="wrap_content"
android:layout_weight="0"
android:layout_height="wrap_content"
android:textSize="9sp"/>
</LinearLayout>
<TextView android:id="#+id/more_fax_info_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="9sp"
android:textColor="#color/gray"
android:layout_toRightOf="#id/faxCheckBox"
android:layout_below="#id/faxbox_item_first_row"
android:paddingLeft="8dp"/>
</RelativeLayout>
I have also a custom arrayadapter for the listview:
private class MyAdapter extends ArrayAdapter<Order> {
private ArrayList<Order> mOrders;
private Context mContext;
private int mResourceId;
public MyAdapter(Context context, int resourceId, ArrayList<Order> orders) {
super(context, resourceId, orders);
this.mContext = context;
this.mOrders = orders;
this.mResourceId = resourceId;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater vi = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(mResourceId, parent, false);
}
final Order order = mOrders.get(position);
if (order != null) {
TextView sender = (TextView)view.findViewById(R.id.number_textView);
TextView date = (TextView)view.findViewById(R.id.send_date_textView);
TextView moreInfo = (TextView)view.findViewById(R.id.more_info_textView);
CheckBox checkBox = (CheckBox)view.findViewById(R.id.checkBox);
final int itemPosition = position;
final RelativeLayout rowLayout = (RelativeLayout)view.findViewById(R.id.listviewitem_layout);
view.setTag(order.getId());
if (sender != null) {
sender.setText(order.getSender());
}
rowLayout.setBackgroundResource(R.drawable.listview_item_selector);
if (mSelectedIds.contains(order.getId())) {
rowLayout.setBackgroundColor(Color.parseColor("#9CD7EF"));
}
if (date != null) {
date.setText(Utils.getStringDateForBox(order.getSendDate(), mContext));
}
if (moreInfo != null) {
moreInfo.setText(getResources().getQuantityString(R.plurals.number_of_pages, order.getPages(), order.getPages()));
}
if (checkBox != null) {
checkBox.setTag(order.getId());
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mSelectedIds.contains(buttonView.getTag()) == true) {
if (isChecked == false) {
mSelectedIds.remove(buttonView.getTag());
}
} else {
if (isChecked) {
mSelectedIds.add((Long) buttonView.getTag());
}
}
showHideActionMode();
setActionModeTitle();
mListView.setItemChecked(position, true);
}
});
if (mSelectedIds.contains(order.getId())) {
checkBox.setChecked(true);
} else {
checkBox.setChecked(false);
}
}
}
return view;
}
}
listview_item_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
android:drawable="#color/blue" />
<item android:state_pressed="true"
android:drawable="#color/blue" />
<item android:state_selected="true"
android:state_activated="true"
android:drawable="#color/light_blue" />
<item android:state_activated="true"
android:drawable="#color/light_blue" />
<item android:state_selected="true"
android:drawable="#color/light_blue" />
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false"
android:drawable="#android:color/white" />
<item android:drawable="#android:color/transparent" />
</selector>
The problem is, if I press a row, then it changes the background color to blue, but without checkbox, checbox has all the time the orange background color. How can I change it?
Maybe should I everything differently make? I'd like at the best something like in the gmail app on the ICS.
I find only how to make a custom checkbox, but this is not the best solution.
checbox_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
android:drawable="#drawable/btn_check_off_pressed_holo_light" />
<item android:state_checked="true"
android:state_pressed="true"
android:drawable="#drawable/btn_check_on_pressed_holo_light" />
<item android:state_pressed="true"
android:drawable="#drawable/btn_check_off_pressed_holo_light" />
<item android:state_checked="true"
android:state_activated="true"
android:drawable="#drawable/btn_check_on_holo_light" />
<item android:state_activated="true"
android:drawable="#drawable/btn_check_on_holo_light" />
<item android:state_checked="true"
android:drawable="#drawable/btn_check_on_holo_light" />
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false"
android:drawable="#drawable/btn_check_off_holo_light" />
<item android:drawable="#drawable/btn_check_off_holo_light" />
and in my list item layout I added that to my checkbox:
android:button="#drawable/checkbox_selector"

How to customize list preference radio button

I have customized all the radioButtons in my application but the radioButtons in the listPreference does not get customized.
I have used this xml named btn_radio.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:state_window_focused="false"
android:drawable="#drawable/radio_selected" />
<item android:state_checked="false" android:state_window_focused="false"
android:drawable="#drawable/radio_unselected" />
<item android:state_checked="true" android:state_pressed="true"
android:drawable="#drawable/radio_selected" />
<item android:state_checked="false" android:state_pressed="true"
android:drawable="#drawable/radio_unselected" />
<item android:state_checked="true" android:state_focused="true"
android:drawable="#drawable/radio_selected" />
<item android:state_checked="false" android:state_focused="true"
android:drawable="#drawable/radio_unselected" />
<item android:state_checked="false" android:drawable="#drawable/radio_unselected" />
<item android:state_checked="true" android:drawable="#drawable/radio_selected" />
</selector>
This is the customRadioButton which extends the android custom radio button
<style name="CustomRadioButton" Parent="#android:style/Widget.CompoundButton.RadioButton">
<item name="android:button">#drawable/btn_radio</item>
</style>
in the theme of my application I have done this changes
<item name="android:radioButtonStyle">#style/CustomRadioButton</item>
<item name="android:listChoiceIndicatorSingle">#style/CustomRadioButton</item>
This changes customize all the radioButtons in my application except radioButtons in my ListPreference
Styling the ListPreference from XML is not directly possible. The problem is that ListPreference (through DialogPreference) calls AlertDialog.Builder(Context) to build its Dialog, rather than AlertDialog.Builder(Context context, int themeResourceId). While the latter allows for providing a theme, the former does not, causing it to fall back to a default Android theme.
For a project, I needed a ListPreference with a custom title-color, a custom radiobutton-style and a custom ListView-selector (basically, Holo in a different color). I solved this by extending ListPreference and overriding onPrepareDialogBuilder(Builder) and OnCreateDialogView() so I could use a custom ListView with a simple ArrayAdapter, rather than Dialog's built-in ListView (which doesn't have support for styling). I also had to override onDialogClosed() in order to set the right value to the preference.
In order to use it, all you have to do is replace the classname of the preference in your preferences.xml rom ListPreference to com.your.packagename.ThemedListPreference. Other than that, the implementation is identical to ListPreference.
public class ThemedListPreference extends ListPreference implements OnItemClickListener {
public static final String TAG = "ThemedListPreference";
private int mClickedDialogEntryIndex;
private CharSequence mDialogTitle;
public ThemedListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ThemedListPreference(Context context) {
super(context);
}
#Override
protected View onCreateDialogView() {
// inflate custom layout with custom title & listview
View view = View.inflate(getContext(), R.layout.dialog_settings_updatetime, null);
mDialogTitle = getDialogTitle();
if(mDialogTitle == null) mDialogTitle = getTitle();
((TextView) view.findViewById(R.id.dialog_title)).setText(mDialogTitle);
ListView list = (ListView) view.findViewById(android.R.id.list);
// note the layout we're providing for the ListView entries
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
getContext(), R.layout.btn_radio,
getEntries());
list.setAdapter(adapter);
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
list.setItemChecked(findIndexOfValue(getValue()), true);
list.setOnItemClickListener(this);
return view;
}
#Override
protected void onPrepareDialogBuilder(Builder builder) {
// adapted from ListPreference
if (getEntries() == null || getEntryValues() == null) {
// throws exception
super.onPrepareDialogBuilder(builder);
return;
}
mClickedDialogEntryIndex = findIndexOfValue(getValue());
// .setTitle(null) to prevent default (blue)
// title+divider from showing up
builder.setTitle(null);
builder.setPositiveButton(null, null);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
mClickedDialogEntryIndex = position;
ThemedListPreference.this.onClick(getDialog(), DialogInterface.BUTTON_POSITIVE);
getDialog().dismiss();
}
#Override
protected void onDialogClosed(boolean positiveResult) {
// adapted from ListPreference
super.onDialogClosed(positiveResult);
if (positiveResult && mClickedDialogEntryIndex >= 0
&& getEntryValues() != null) {
String value = getEntryValues()[mClickedDialogEntryIndex]
.toString();
if (callChangeListener(value)) {
setValue(value);
}
}
}
}
For my ListView items I used the layout below. Note that drawable/btn_radio_holo_light is an XML-drawable like the one in your android-sdk/platforms/android-x/data/res/drawable folder, only with references to different drawables.
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkMark="#drawable/btn_radio_holo_light"
android:gravity="center_vertical"
android:minHeight="#dimen/list_item_minheight"
android:paddingLeft="#dimen/list_item_paddingLeft"
android:paddingRight="#dimen/list_item_paddingLeft" />
For my Dialog layout (onCreateDialogView()), I used the following:
<?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/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textColor="#color/title_color"
android:textSize="22sp" />
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#color/divider_color" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:listSelector="#drawable/list_selector" />
</LinearLayout>
Please, see my answer here, maybe it helps.
It's a much easier way to solve the problem.

Categories

Resources