Because checkbox isnt an option for my project I want the checkable to have a background when checked.Supporting from 2.3 i havent manage to solve this problem yet.
Selection is correct but what i see at screen isnt.Random color at random row..
Fist i have this
<?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/abs__list_selector_holo_light" />
<item android:state_checked="true" android:drawable="#drawable/abs__list_selector_holo_light" />
<item android:state_selected="true" android:drawable="#drawable/abs__list_selector_holo_light" />
<item android:drawable="#drawable/abs__list_selector_holo_light" />
</selector>
-
public class CheckableRelativeLayout extends RelativeLayout implements
Checkable {
private boolean isChecked;
private List<Checkable> checkableViews;
private static final int[] CheckedStateSet = {
R.attr.state_checked
};
public CheckableRelativeLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initialise(attrs);
}
public CheckableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initialise(attrs);
}
public CheckableRelativeLayout(Context context, int checkableId) {
super(context);
initialise(null);
}
/*
* #see android.widget.Checkable#isChecked()
*/
public boolean isChecked() {
return isChecked;
}
/*
* #see android.widget.Checkable#setChecked(boolean)
*/
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
for (Checkable c : checkableViews) {
c.setChecked(isChecked);
}
}
/*
* #see android.widget.Checkable#toggle()
*/
public void toggle() {
this.isChecked = !this.isChecked;
for (Checkable c : checkableViews) {
c.toggle();
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
final int childCount = this.getChildCount();
for (int i = 0; i < childCount; ++i) {
findCheckableChildren(this.getChildAt(i));
}
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CheckedStateSet);
}
return drawableState;
}
#Override
public boolean performClick() {
toggle();
return super.performClick();
}
/**
* Read the custom XML attributes
*/
private void initialise(AttributeSet attrs) {
this.isChecked = false;
this.checkableViews = new ArrayList<Checkable>(5);
}
/**
* Add to our checkable list all the children of the view that implement the
* interface Checkable
*/
private void findCheckableChildren(View v) {
if (v instanceof Checkable) {
this.checkableViews.add((Checkable) v);
}
if (v instanceof ViewGroup) {
final ViewGroup vg = (ViewGroup) v;
final int childCount = vg.getChildCount();
for (int i = 0; i < childCount; ++i) {
findCheckableChildren(vg.getChildAt(i));
}
}
}
}
What i get is this
Here's a clean example on making a Checkable View:
import android.R;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Checkable;
import android.widget.LinearLayout;
public class ActivatedLinearLayout extends LinearLayout implements Checkable{
public static final int[] CHECKED_STATE = {R.attr.state_checked};
private boolean mChecked;
public ActivatedLinearLayout(Context context) {
super(context);
}
public ActivatedLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ActivatedLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setChecked(boolean b) {
mChecked = b;
refreshDrawableState();
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
mChecked = !mChecked;
refreshDrawableState();
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
int[] states = super.onCreateDrawableState(extraSpace + 1);
if (mChecked){
mergeDrawableStates(states, CHECKED_STATE);
}
return states;
}
}
And selector:
<?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/checked"/>
<item android:drawable="#drawable/unchecked"/>
</selector>
Related
I want to use widget as CheckableImageButton on my application, but this code, image state not working correctly when I click on image that state must be changed
always I get true after change state
public class CheckableImageButton extends ImageButton implements Checkable {
private boolean mChecked;
private boolean mBroadcasting;
private int mPersonality;
private OnCheckedChangeListener mOnCheckedChangeListener;
private static final int[] CHECKED_STATE_SET = { R.attr.is_checked };
private static final int PERSONALITY_RADIO_BUTTON = 0;
private static final int PERSONALITY_CHECK_BOX = 1;
public CheckableImageButton(Context context) {
this(context, null);
}
public CheckableImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckableImageButton);
mPersonality = a.getInt(R.styleable.CheckableImageButton_personality, PERSONALITY_RADIO_BUTTON);
boolean checked = a.getBoolean(R.styleable.CheckableImageButton_is_checked, false);
setChecked(checked);
a.recycle();
}
public void toggle() {
setChecked(!mChecked);
}
#Override
public boolean performClick() {
if (mPersonality == PERSONALITY_RADIO_BUTTON) {
setChecked(true);
} else if (mPersonality == PERSONALITY_CHECK_BOX) {
toggle();
}
return super.performClick();
}
public boolean isChecked() {
return mChecked;
}
/**
* <p>
* Changes the checked state of this button.
* </p>
*
* #param checked
* true to check the button, false to uncheck it
*/
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
mBroadcasting = false;
}
}
/**
* Register a callback to be invoked when the checked state of this button changes.
*
* #param listener
* the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
/**
* Interface definition for a callback.
*/
public static interface OnCheckedChangeListener {
/**
* Called when the checked state of a button has changed.
*
* #param button
* The button view whose state has changed.
* #param isChecked
* The new checked state of button.
*/
void onCheckedChanged(CheckableImageButton button, boolean isChecked);
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
invalidate();
}
static class SavedState extends BaseSavedState {
boolean checked;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
checked = (Boolean) in.readValue(null);
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeValue(checked);
}
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.checked = isChecked();
return ss;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
requestLayout();
}
}
checkable_image_selector file content:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="4dp" />
<padding
android:left="4dp"
android:right="4dp"
android:top="4dp"
android:bottom="4dp" />
<gradient
android:angle="90"
android:startColor="#FFFFC700"
android:centerColor="#FFFFA600"
android:endColor="#FFFFC700" />
<stroke
android:width="3dp"
android:color="#FF80B0E0" />
</shape>
and checkable_image_button_state_pressed:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="4dp"/>
<padding
android:bottom="4dp"
android:left="4dp"
android:right="4dp"
android:top="4dp"/>
<solid
android:color="#00000000"/>
<stroke
android:width="3dp"
android:color="#FF80B0E0"/>
</shape>
and then i use that like with this code:
checkable_image_1.setOnCheckedChangeListener(new CheckableImageButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CheckableImageButton button, boolean isChecked) {
Log.e("CHECK: ", isChecked + "");
checkable_image_1.setChecked(false);
}
});
checkable_image_1.setChecked(true);
public class CheckableImageButton extends AppCompatImageButton implements Checkable {
private static final int[] DRAWABLE_STATE_CHECKED = new int[]{android.R.attr.state_checked};
private boolean mChecked;
public CheckableImageButton(Context context) {
this(context, null);
}
public CheckableImageButton(Context context, AttributeSet attrs) {
this(context, attrs, android.support.v7.appcompat.R.attr.imageButtonStyle);
}
public CheckableImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegateCompat() {
#Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setChecked(isChecked());
}
#Override
public void onInitializeAccessibilityNodeInfo(View host,
AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setCheckable(true);
info.setChecked(isChecked());
}
});
}
#Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
sendAccessibilityEvent(
AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
}
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
setChecked(!mChecked);
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
if (mChecked) {
return mergeDrawableStates(
super.onCreateDrawableState(extraSpace + DRAWABLE_STATE_CHECKED.length),
DRAWABLE_STATE_CHECKED);
} else {
return super.onCreateDrawableState(extraSpace);
}
}
}
I need to display an array of dots (ImageView) that behave like the RatingBar, here's an example:
This is pretty much an RatingBar rotated, but I've a problem, this application is pixel-perfect and therefore I need to add some margin between the dots. This cannot be done with an RatingBar. With all this issues that I'm facing trying to use the RatingBar I gave up and I decided to make my own component, so far this is the component:
public class DotContainerView extends LinearLayout {
#InjectView(R.id.view_dot_container)
LinearLayout vDotContainer;
private OnRatingBarChangeListener mListener;
public DotContainerView(Context context) {
super(context);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
View root = LayoutInflater.from(getContext()).inflate(R.layout.view_dot_container, this);
ButterKnife.inject(this, root);
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
return false;
}
});
}
public void setRating(int rating) {
for (int index = 0; index < rating; index++) {
vDotContainer.getChildAt(index).setSelected(false);
}
for (int index = rating; index < 10; index++) {
vDotContainer.getChildAt(index).setSelected(true);
}
}
public void setOnRatingBarChangeListener(DotContainerView.OnRatingBarChangeListener listener) {
mListener = listener;
}
//region OnTouch
#OnTouch(R.id.fragment_brightness_control_dot_1)
public boolean onDot1Touched() {
setRating(1);
mListener.onRatingChanged(this, 1, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_2)
public boolean onDot2Touched() {
setRating(2);
mListener.onRatingChanged(this, 2, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_3)
public boolean onDot3Touched() {
setRating(3);
mListener.onRatingChanged(this, 3, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_4)
public boolean onDot4Touched() {
setRating(4);
mListener.onRatingChanged(this, 4, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_5)
public boolean onDot5Touched() {
setRating(5);
mListener.onRatingChanged(this, 5, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_6)
public boolean onDot6Touched() {
setRating(6);
mListener.onRatingChanged(this, 6, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_7)
public boolean onDot7Touched() {
setRating(7);
mListener.onRatingChanged(this, 7, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_8)
public boolean onDot8Touched() {
setRating(8);
mListener.onRatingChanged(this, 8, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_9)
public boolean onDot9Touched() {
setRating(9);
mListener.onRatingChanged(this, 9, true);
return true;
}
#OnTouch(R.id.fragment_brightness_control_dot_10)
public boolean onDot10Touched() {
setRating(10);
mListener.onRatingChanged(this, 10, true);
return true;
}
//endregion
public interface OnRatingBarChangeListener {
public void onRatingChanged(DotContainerView ratingBar, float value, boolean fromUser);
}
}
This code works fine, if I tap in a dot all the previous dots'll get selected. The only issue with this is that if I drag my finger across the dots, they don't react as in a RatingBar, only if I tap in each dot. Any idea of how solve this?. And please avoid telling me "Use the RatingBar".
I ended up doing something like this:
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* #author astinx
* #since 0.2
* <p>
* Simple widget that shows an array of dots which can be tapped like a {#link android.widget.RatingBar}
*/
public class DotContainerView extends LinearLayout implements View.OnTouchListener {
#InjectView(R.id.view_dot_container)
LinearLayout vDotContainer;
private OnRatingBarChangeListener mListener;
public DotContainerView(Context context) {
super(context);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DotContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
private void initialize() {
View root = LayoutInflater.from(getContext()).inflate(R.layout.view_dot_container, this);
ButterKnife.inject(this, root);
vDotContainer.setOnTouchListener(this);
}
public void setRating(int rating) {
//If the rating = 5
for (int index = 0; index < rating; index++) {
//This sets the children 10, 9, 8, 5...
vDotContainer.getChildAt(Math.abs(index - 10) - 1).setSelected(true);
}
for (int index = rating; index < 10; index++) {
//Ant this sets the children 6, 7, 8, 9, 10
vDotContainer.getChildAt(Math.abs(index - 10) - 1).setSelected(false);
}
}
public void setOnRatingBarChangeListener(DotContainerView.OnRatingBarChangeListener listener) {
mListener = listener;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return true;
}
#Override
public boolean onInterceptHoverEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return super.onInterceptHoverEvent(event);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getY();
setRating(rawX, rawY);
return false;
}
protected void setRating(float rawX, float rawY) {
Log.d("DotContainer", "x=" + rawX + ";y=" + rawY);
int dotIndexByCoords = 10 - findDotIndexByCoords(rawX, rawY);
setRating(dotIndexByCoords);
mListener.onRatingChanged(this, dotIndexByCoords, true);
}
private View findViewByIndex(int childIndex) {
return vDotContainer.getChildAt(childIndex);
}
/**
* Iterates all over the {#link LinearLayout} searching for the closest child to x,y
* #param x The x axis
* #param y The y axis
* #return The index of the child, -1 if isn't found.
*/
private int findDotIndexByCoords(float x, float y) {
for (int childIndex = 0; childIndex < vDotContainer.getChildCount(); childIndex++) {
float y1 = vDotContainer.getChildAt(childIndex).getY();
float y2 = vDotContainer.getChildAt(childIndex + 1).getY();
if (y1 <= y && y <= y2) {
Log.d("DotContainer", "Child no "+ childIndex);
return childIndex;
}
}
return -1;
}
public interface OnRatingBarChangeListener {
public void onRatingChanged(DotContainerView ratingBar, float value, boolean fromUser);
}
}
I know that're things that can be improved, but for those who want to do something quick withouth yelling and cursing at the RatingBar this is something quick, that gets the job done. Basically is just a LinearLayout that contains an array of ImageView each one has a drawable selector that changes the drawable whether is pressed or not. This class overrides the methodn onInterceptTouchEvent and returns false so it's continiously called, inside this method we check which dot was clicked.
I am using PreferenceActivity for the setting of my app.
I want to add a new preference that allow the user to select an icon.
For this task I want to use a ListPreference, but I want also to show the icon in the list.
I tried to customize the ListPreference to use a custom layout, but the problem is that once I do that the list items are not clickable (it does show my custom layout and use the default value for the current selection).
I tested it on different emulator version and on Galaxy S2. When pressing the item I could see some effect of the pressed/unpressed, but the onClick method is not called.
I followed the instruction on Android: Checkable Linear Layout for adding custom layout (I also tried the option describe in How to customize list preference radio button, but the same result).
IconTypePreference.java (copied from ListPreference and modified):
public class IconTypePreference extends DialogPreference {
private IconType value;
private int clickedDialogIndex;
private boolean valueSet;
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public IconTypePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public IconTypePreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public IconTypePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public IconTypePreference(Context context) {
super(context);
}
public void setValue(String value) {
// Always persist/notify the first time.
final boolean changed = !TextUtils.equals(getValueText(), value);
if (changed || !valueSet) {
if (value == null) {
this.value = null;
} else {
this.value = IconType.valueOf(value);
}
valueSet = true;
persistString(value);
if (changed) {
notifyChanged();
}
}
}
public void setValueIndex(int index) {
setValue(IconType.values()[index].toString());
}
public IconType getValue() {
return value;
}
public String getValueText() {
return (value == null ? null : value.toString());
}
public int findIndexOfValue(String value) {
IconType[] values = IconType.values();
for (int i = values.length - 1; i >= 0; i--) {
if (values[i].toString().equals(value)) {
return i;
}
}
return -1;
}
private int getValueIndex() {
return findIndexOfValue(getValueText());
}
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
clickedDialogIndex = getValueIndex();
builder.setSingleChoiceItems(new IconTypeAdapter(getContext()), clickedDialogIndex,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
clickedDialogIndex = which;
IconTypePreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
}
});
/*
* The typical interaction for list-based dialogs is to have
* click-on-an-item dismiss the dialog instead of the user having to
* press 'Ok'.
*/
builder.setPositiveButton(null, null);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && clickedDialogIndex >= 0) {
String value = IconType.values()[clickedDialogIndex].toString();
if (callChangeListener(value)) {
setValue(value);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedString(getValueText()) : (String)defaultValue);
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
myState.value = getValueText();
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState)state;
super.onRestoreInstanceState(myState.getSuperState());
setValue(myState.value);
}
private static class SavedState extends BaseSavedState {
String value;
public SavedState(Parcel source) {
super(source);
value = source.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(value);
}
public SavedState(Parcelable superState) {
super(superState);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
private static class IconTypeAdapter extends ArrayAdapter<IconType> {
private final String[] iconTypeText;
private LayoutInflater inflater;
public IconTypeAdapter(Context context) {
super(context, R.layout.icon_type_item, IconType.values());
this.inflater = LayoutInflater.from(context);
iconTypeText = context.getResources().getStringArray(R.array.icon_type);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.icon_type_item, parent, false);
}
((TextView)convertView.findViewById(R.id.text)).setText(iconTypeText[position]);
convertView.setClickable(true);
// todo: set view text
return convertView;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public long getItemId(int position) {
return position;
}
}
}
CheckableLinearLayout.java
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private Checkable checkable;
public CheckableLinearLayout(Context context) {
super(context);
}
public CheckableLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
checkable = getCheckable(this);
if (checkable == null) {
throw new RuntimeException("Missing Checkable component");
}
}
private Checkable getCheckable(ViewGroup viewGroup) {
View v;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; ++i) {
v = getChildAt(i);
if (v instanceof Checkable) {
return (Checkable)v;
} else if (v instanceof ViewGroup) {
Checkable result = getCheckable((ViewGroup)v);
if (result != null) {
return result;
}
}
}
return null;
}
#Override
public void setChecked(boolean checked) {
checkable.setChecked(checked);
}
#Override
public boolean isChecked() {
return checkable.isChecked();
}
#Override
public void toggle() {
checkable.toggle();
}
}
icon_type_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.utils.ui.widget.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:id="#+id/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="false"
android:focusableInTouchMode="false"/>
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"/>
</com.utils.ui.widget.CheckableLinearLayout>
Added to settings.xml
<com.utils.ui.preference.IconTypePreference
android:key="icon_type"
android:defaultValue="type_b"
android:title="#string/icon_type_preference_title"/>
EDIT
There is a bug in CheckableLinearLayout.java
Replace the getCheckable method with this:
private Checkable getCheckable(ViewGroup viewGroup) {
View v;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; ++i) {
v = viewGroup.getChildAt(i);
if (v instanceof Checkable) {
return (Checkable)v;
} else if (v instanceof ViewGroup) {
Checkable result = getCheckable((ViewGroup)v);
if (result != null) {
return result;
}
}
}
return null;
}
Found the solution to the problem.
The problem was in the getView method of the adapter:
I changed
convertView.setClickable(true);
to
convertView.setClickable(false);
Question
Hello,
i have built a custom view with a RelativeLayout to use it as custom image button.
So far, selecting it works (PIC2) and even when I click it (using GoogleTV Remote), the view successfully changes it's state to PIC3 (thanks to android:duplicateParentState="true")
But unfortunately the onClickListener does not fire (doesn't matter if I click the View with the Remote "OK" Button or I use the touchpad..)
I really need the same behavior like a normal button.
How to accomplish that? I already spent a few hours on searching Google and StackOverflow... (BTW. when setting android:clickable="false" for the RelativeLayout, the OnClickListener is working, but only when I use the mouse pointer (Touchpad) and afterwards the focus is lost and the state (pic 3) is not displayed)
Pictures
PIC1
PIC2
PIC3
Code
rounded_button.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false">
<TextView
android:id="#+id/caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:background="#drawable/btn_rounded_corners"
android:paddingLeft="25dp"
android:textSize="15sp"
android:duplicateParentState="true"/>
<ImageView
android:id="#+id/icon"
style="#style/Menu_Button"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="-50dp"
android:layout_toLeftOf="#id/caption"
android:background="#drawable/btn_main_menu_back_shape"
tools:ignore="ContentDescription"
android:duplicateParentState="true" />
RoundedButton.java
public class RoundedButton extends RelativeLayout {
private String label;
private int icon;
/**
* #param context
*/
public RoundedButton(Context context)
{
super(context);
initAttributes(context, null);
}
/**
* #param context
* #param attrs
*/
public RoundedButton(Context context, AttributeSet attrs)
{
super(context, attrs);
initAttributes(context, attrs);
}
/**
* #param context
* #param attrs
* #param defStyle
*/
public RoundedButton(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initAttributes(context, attrs);
}
private void initAttributes(Context context, AttributeSet attrs)
{
LayoutInflater.from(context).inflate(R.layout.rounded_button, this, true);
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.RoundedButton);
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.RoundedButton_text:
setLabel(a.getString(attr));
break;
case R.styleable.RoundedButton_icon:
setIcon(a.getResourceId(attr, 0));
break;
}
}
a.recycle();
}
public String getLabel()
{
return this.label;
}
public void setLabel(final String label)
{
this.label = label;
((TextView)findViewById(R.id.caption)).setText(this.label);
}
/**
* #return the icon
*/
public int getIcon()
{
return icon;
}
/**
* #param icon the icon to set
*/
public void setIcon(int icon)
{
this.icon = icon;
((ImageView)findViewById(R.id.icon)).setImageResource(this.icon);
}
}
Relevant part of activity_main.xml
<eu.test.custom_views.RoundedButton
android:id="#+id/custombutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:icon="#drawable/hand_icon_green_left"
custom:text="Normal state" />
Main Activity
public class MainActivity extends Activity implements OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((RoundedButton) findViewById(R.id.custombutton)).setOnClickListener(this);
}
#Override
public void onClick(View arg0) {
if(arg0.getId() == R.id.custombutton) { Toast.makeText(this, "Clicked", Toast.LENGTH_SHORT).show(); }
}
}
I got it now.. the solution is so simple, that it took some time ;-)
Solution
Override dispatchKeyEvent(KeyEvent event) in RoundedButton.java and implement your own OnClickListener. Then write a public setOnClickListener function...
private OnClickListener listener;
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
if(listener != null) listener.onClick(this);
}
return super.dispatchTouchEvent(event);
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_UP && (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER || event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if(listener != null) listener.onClick(this);
}
return super.dispatchKeyEvent(event);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
Working RoundedButton.java
public class RoundedButton extends RelativeLayout
{
private OnClickListener listener;
private String label;
private int icon;
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
if(listener != null) listener.onClick(this);
}
return super.dispatchTouchEvent(event);
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_UP && (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER || event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if(listener != null) listener.onClick(this);
}
return super.dispatchKeyEvent(event);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
/**
* #param context
*/
public RoundedButton(Context context)
{
super(context);
initAttributes(context, null);
}
/**
* #param context
* #param attrs
*/
public RoundedButton(Context context, AttributeSet attrs)
{
super(context, attrs);
initAttributes(context, attrs);
}
/**
* #param context
* #param attrs
* #param defStyle
*/
public RoundedButton(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
this.setClickable(true);
this.setEnabled(true);
this.setFocusable(true);
this.setFocusableInTouchMode(true);
initAttributes(context, attrs);
}
private void initAttributes(Context context, AttributeSet attrs)
{
LayoutInflater.from(context).inflate(R.layout.rounded_button, this, true);
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.RoundedButton);
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.RoundedButton_text:
setLabel(a.getString(attr));
break;
case R.styleable.RoundedButton_icon:
setIcon(a.getResourceId(attr, 0));
break;
}
}
a.recycle();
}
public String getLabel()
{
return this.label;
}
public void setLabel(final String label)
{
this.label = label;
((TextView)findViewById(R.id.caption)).setText(this.label);
}
/**
* #return the icon
*/
public int getIcon()
{
return icon;
}
/**
* #param icon the icon to set
*/
public void setIcon(int icon)
{
this.icon = icon;
((ImageView)findViewById(R.id.icon)).setImageResource(this.icon);
}
}
I was creating instances of my custom view (which inherits from RelativeLayout) in a RecyclerView.Adapter and inflating list_item.xml into each new instance, and then attaching my listener in the onBindViewHolder() method but even if in theory I was doing things the right way it didn't work until I removed the android:clickable="true" attribute from the list_item.xml (my custom view)'s root layout.
I have created the page indicator for the viewpager.In which i am changing the indicator image when user swipe in viewpager.
Below is my code for onPageSelected#ViewPager.OnPageChangeListener
public void onPageSelected(int position) {
int childCount = this.layout.getChildCount();
for (int i = 0; i < childCount; i++) {
PageItem child = (PageItem) this.layout.getChildAt(position);
if (i == position) {
child.setActiveState();
} else {
child.resetState();
}
}
this.layout.invalidate();
}
Below is my PageItem class
public class PageItem extends ImageView {
private int activeBitmap;
private int inActiveBitmap;
private boolean isActive;
public PageItem(Context context, AttributeSet attrs, int defStyle,
int activeBitmapId, int inActiveBitmapId) {
super(context, attrs, defStyle);
this.activeBitmap = activeBitmapId;
this.inActiveBitmap = inActiveBitmapId;
this.setImageResource(inActiveBitmap);
isActive = false;
}
public PageItem(Context context, AttributeSet attrs, int activeBitmapId,
int inActiveBitmapId) {
super(context, attrs);
this.activeBitmap = activeBitmapId;
this.inActiveBitmap = inActiveBitmapId;
this.setImageResource(inActiveBitmap);
isActive = false;
}
public PageItem(Context context, int activeBitmapId, int inActiveBitmapId) {
super(context);
this.activeBitmap = activeBitmapId;
this.inActiveBitmap = inActiveBitmapId;
this.setImageResource(inActiveBitmap);
isActive = false;
}
public void resetState() {
this.setImageDrawable(this.getContext().getResources()
.getDrawable(inActiveBitmap));
this.invalidate();
isActive = false;
}
public void setActiveState() {
this.setImageDrawable(this.getContext().getResources()
.getDrawable(activeBitmap));
this.invalidate();
isActive = true;
}
}
Now after starting my application when i do swipe first time it change the image but then after it does not change the image.
thanks in advance.
Looks like it should be:
PageItem child = (PageItem) this.layout.getChildAt(i);