Android custom CheckableImageButton not working correctly - android

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);
}
}
}

Related

how to get expandable textview width to set its trim length to 1 line

i'm using expandable textview to display some part of the text and when user clicks on this textview then user can see whole String of that text for that i'm using this example but the problem is the trim length of the expandable textview its set to fixed, but i want to set the trim length dynamic based on screen size with only one line, when i use trim_length = 200 the text displayed is of 3 lines, here is my code...
ExpandableTextView.java
public class ExpandableTextView extends TextView {
private static final int DEFAULT_TRIM_LENGTH = 200;
private static final String ELLIPSIS = ".....";
private CharSequence originalText;
private CharSequence trimmedText;
private BufferType bufferType;
private boolean trim = true;
private int trimLength;
public ExpandableTextView(Context context) {
this(context, null);
}
public ExpandableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
typedArray.recycle();
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
trim = !trim;
setText();
requestFocusFromTouch();
}
});
}
private void setText() {
super.setText(getDisplayableText(), bufferType);
}
private CharSequence getDisplayableText() {
return trim ? trimmedText : originalText;
}
#Override
public void setText(CharSequence text, BufferType type) {
originalText = text;
trimmedText = getTrimmedText(text);
bufferType = type;
setText();
}
private CharSequence getTrimmedText(CharSequence text) {
if (originalText != null && originalText.length() > trimLength) {
return new SpannableStringBuilder(originalText, 0, trimLength + 1).append(ELLIPSIS);
} else {
return originalText;
}
}
public CharSequence getOriginalText() {
return originalText;
}
public void setTrimLength(int trimLength) {
this.trimLength = trimLength;
trimmedText = getTrimmedText(originalText);
setText();
}
public int getTrimLength() {
return trimLength;
}
}
ExpandableTextView expandableTextView = (ExpandableTextView) findViewById(R.id.lorem_ipsum);
expandableTextView.setText(yourText);
you can do it like this
public class ExpandableTextView extends TextView
{
// copy off TextView.LINES
private static final int MAXMODE_LINES = 1;
// private OnExpandListener onExpandListener;
private final int maxLines;
private boolean expanded;
public ExpandableTextView(final Context context)
{
this(context, null);
}
public ExpandableTextView(final Context context, final AttributeSet attrs)
{
this(context, attrs, 0);
}
public ExpandableTextView(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
// read attributes
final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView, defStyle, 0);
// this.animationDuration = attributes.getInt(R.styleable.ExpandableTextView_trimLength, 200);
attributes.recycle();
// keep the original value of maxLines
this.maxLines = this.getMaxLines();
}
#Override
public int getMaxLines()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
return super.getMaxLines();
}
try
{
final Field mMaxMode = TextView.class.getField("mMaxMode");
mMaxMode.setAccessible(true);
final Field mMaximum = TextView.class.getField("mMaximum");
mMaximum.setAccessible(true);
final int mMaxModeValue = (int) mMaxMode.get(this);
final int mMaximumValue = (int) mMaximum.get(this);
return mMaxModeValue == MAXMODE_LINES ? mMaximumValue : -1;
}
catch (final Exception e)
{
return -1;
}
}
public boolean toggle()
{
return this.expanded
? this.collapse()
: this.expand();
}
public boolean expand()
{
if (!this.expanded && this.maxLines >= 0)
{
// set maxLines to MAX Integer, so we can calculate the expanded height
this.setMaxLines(Integer.MAX_VALUE);
// keep track of current status
ExpandableTextView.this.expanded = true;
return true;
}
return false;
}
public boolean collapse()
{
if (this.expanded && this.maxLines >= 0)
{
ExpandableTextView.this.setMaxLines(ExpandableTextView.this.maxLines);
// keep track of current status
ExpandableTextView.this.expanded = false;
return true;
}
return false;
}
}
activity.xml
<com.yourpackagename.ExpandableTextView
android:id="#+id/expandableTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
/>
activity.java
ExpandableTextView expandableTextView = (ExpandableTextView) this.findViewById(R.id.expandableTextView);
expandableTextView.setOnClickListener(new View.OnClickListener()
{
#SuppressWarnings("ConstantConditions")
#Override
public void onClick(final View v)
{
expandableTextView.toggle();
}
});

how to use getForeground and setForeground on API below 23 [duplicate]

I would like to know how to apply or emulate foreground effect in a view different from FrameLayout, as LinearLayout or RelativeLayout
This is what I have now:
<?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"
android:id="#+id/cardContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/row_background"
android:foreground="#drawable/foreground_row">
...
</FrameLayout>
And I want something like:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/row_background"
app:foreground="#drawable/foreground_row">
...
</RelativeLayout>
Thanks in advance!!
The idea is to surround your layout with a FrameLayout, and set the selector and the onClick event to this layout.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/selectableItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foreground="#drawable/foreground_row"
>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/row_background">
...
</RelativeLayout>
</FrameLayout>
You can find a full explanation at my blog:
http://antonioleiva.com/unveiling-bandhook-foreground-any-layout/
Or you can extend rhis FRelativeLayout https://gist.github.com/shakalaca/6199283
Checkout ForegroundView library with Gradle integration. It supports following views
ForegroundImageView
ForegroundButton
ForegroundTextView
ForegroundImageButton
ForegroundEditText
ForegroundWebView
ForegroundLinearLayout
ForegroundRelativeLayout
ForegroundGridLayout
ForegroundGridView
ForegroundHorizontalScrollView
ForegroundListView
ForegroundScrollViewForegroundImageView
Here is a possible implementation, one which also supports checking a layout.
A nearly identical solution can be applied to any layout view you wish (only the CTOR is different).
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private boolean mChecked;
private static final String TAG = CheckableLinearLayout.class.getCanonicalName();
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
private Drawable mForeground;
public CheckableLinearLayout(final Context context) {
this(context, null, 0);
}
public CheckableLinearLayout(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckableLinearLayout(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context
.obtainStyledAttributes(attrs, R.styleable.CheckableLinearLayout, defStyle, 0);
setForegroundEx(a.getDrawable(R.styleable.CheckableLinearLayout_foreground));
a.recycle();
}
public void setForegroundEx(final Drawable drawable) {
this.mForeground = drawable;
}
#Override
protected int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
final Drawable drawable = getBackground();
boolean needRedraw = false;
final int[] myDrawableState = getDrawableState();
if (drawable != null) {
drawable.setState(myDrawableState);
needRedraw = true;
}
if (mForeground != null) {
mForeground.setState(myDrawableState);
needRedraw = true;
}
if (needRedraw)
invalidate();
}
#Override
protected void onSizeChanged(final int width, final int height, final int oldwidth, final int oldheight) {
super.onSizeChanged(width, height, oldwidth, oldheight);
if (mForeground != null)
mForeground.setBounds(0, 0, width, height);
}
#Override
protected void dispatchDraw(final Canvas canvas) {
super.dispatchDraw(canvas);
if (mForeground != null)
mForeground.draw(canvas);
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void setChecked(final boolean checked) {
mChecked = checked;
refreshDrawableState();
//TODO think if you wish to also check inner views, maybe even recursively
}
#Override
public void toggle() {
setChecked(!mChecked);
}
#Override
public Parcelable onSaveInstanceState() {
// Force our ancestor class to save its state
final Parcelable superState = super.onSaveInstanceState();
final SavedState savedState = new SavedState(superState);
savedState.checked = isChecked();
return savedState;
}
#Override
public void onRestoreInstanceState(final Parcelable state) {
final SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
setChecked(savedState.checked);
requestLayout();
}
#SuppressLint("ClickableViewAccessibility")
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public boolean onTouchEvent(final MotionEvent e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && //
e.getActionMasked() == MotionEvent.ACTION_DOWN && //
mForeground != null)
mForeground.setHotspot(e.getX(), e.getY());
return super.onTouchEvent(e);
}
// /////////////
// SavedState //
// /////////////
private static class SavedState extends BaseSavedState {
boolean checked;
SavedState(final Parcelable superState) {
super(superState);
}
private SavedState(final Parcel in) {
super(in);
checked = (Boolean) in.readValue(null);
}
#Override
public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags);
out.writeValue(checked);
}
#Override
public String toString() {
return TAG + ".SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked
+ "}";
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
#Override
public SavedState createFromParcel(final Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
}
}
attr.xml
<declare-styleable name="CheckableLinearLayout">
<attr name="foreground"/>
</declare-styleable>
Here's a more minimal way to do it, using Kotlin, and without the part of being checkable :
class LinearLayoutEx #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : LinearLayout(context, attrs, defStyle) {
var foregroundDrawable: Drawable? = null
set(value) {
field = value
invalidate()
}
init {
val a = context
.obtainStyledAttributes(attrs, R.styleable.LinearLayoutEx, defStyle, 0)
foregroundDrawable = a.getDrawable(R.styleable.LinearLayoutEx_foreground)
a.recycle()
}
override fun drawableStateChanged() {
super.drawableStateChanged()
val drawable = background
var needRedraw = false
val myDrawableState = drawableState
if (drawable != null) {
drawable.state = myDrawableState
needRedraw = true
}
if (foregroundDrawable != null) {
foregroundDrawable!!.state = myDrawableState
needRedraw = true
}
if (needRedraw)
invalidate()
}
override fun onSizeChanged(width: Int, height: Int, oldwidth: Int, oldheight: Int) {
super.onSizeChanged(width, height, oldwidth, oldheight)
foregroundDrawable?.setBounds(0, 0, width, height)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
foregroundDrawable?.draw(canvas)
}
#SuppressLint("ClickableViewAccessibility")
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun onTouchEvent(e: MotionEvent): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && e.actionMasked == MotionEvent.ACTION_DOWN)
foregroundDrawable?.setHotspot(e.x, e.y)
return super.onTouchEvent(e)
}
}
attr.xml
<declare-styleable name="LinearLayoutEx" tools:ignore="MissingDefaultResource">
<attr name="foreground"/>
</declare-styleable>

Custom layout for ListPreference

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);

How to set foreground attribute to other non FrameLayout view

I would like to know how to apply or emulate foreground effect in a view different from FrameLayout, as LinearLayout or RelativeLayout
This is what I have now:
<?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"
android:id="#+id/cardContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/row_background"
android:foreground="#drawable/foreground_row">
...
</FrameLayout>
And I want something like:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/row_background"
app:foreground="#drawable/foreground_row">
...
</RelativeLayout>
Thanks in advance!!
The idea is to surround your layout with a FrameLayout, and set the selector and the onClick event to this layout.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/selectableItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foreground="#drawable/foreground_row"
>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/row_background">
...
</RelativeLayout>
</FrameLayout>
You can find a full explanation at my blog:
http://antonioleiva.com/unveiling-bandhook-foreground-any-layout/
Or you can extend rhis FRelativeLayout https://gist.github.com/shakalaca/6199283
Checkout ForegroundView library with Gradle integration. It supports following views
ForegroundImageView
ForegroundButton
ForegroundTextView
ForegroundImageButton
ForegroundEditText
ForegroundWebView
ForegroundLinearLayout
ForegroundRelativeLayout
ForegroundGridLayout
ForegroundGridView
ForegroundHorizontalScrollView
ForegroundListView
ForegroundScrollViewForegroundImageView
Here is a possible implementation, one which also supports checking a layout.
A nearly identical solution can be applied to any layout view you wish (only the CTOR is different).
public class CheckableLinearLayout extends LinearLayout implements Checkable {
private boolean mChecked;
private static final String TAG = CheckableLinearLayout.class.getCanonicalName();
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
private Drawable mForeground;
public CheckableLinearLayout(final Context context) {
this(context, null, 0);
}
public CheckableLinearLayout(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckableLinearLayout(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context
.obtainStyledAttributes(attrs, R.styleable.CheckableLinearLayout, defStyle, 0);
setForegroundEx(a.getDrawable(R.styleable.CheckableLinearLayout_foreground));
a.recycle();
}
public void setForegroundEx(final Drawable drawable) {
this.mForeground = drawable;
}
#Override
protected int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
final Drawable drawable = getBackground();
boolean needRedraw = false;
final int[] myDrawableState = getDrawableState();
if (drawable != null) {
drawable.setState(myDrawableState);
needRedraw = true;
}
if (mForeground != null) {
mForeground.setState(myDrawableState);
needRedraw = true;
}
if (needRedraw)
invalidate();
}
#Override
protected void onSizeChanged(final int width, final int height, final int oldwidth, final int oldheight) {
super.onSizeChanged(width, height, oldwidth, oldheight);
if (mForeground != null)
mForeground.setBounds(0, 0, width, height);
}
#Override
protected void dispatchDraw(final Canvas canvas) {
super.dispatchDraw(canvas);
if (mForeground != null)
mForeground.draw(canvas);
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void setChecked(final boolean checked) {
mChecked = checked;
refreshDrawableState();
//TODO think if you wish to also check inner views, maybe even recursively
}
#Override
public void toggle() {
setChecked(!mChecked);
}
#Override
public Parcelable onSaveInstanceState() {
// Force our ancestor class to save its state
final Parcelable superState = super.onSaveInstanceState();
final SavedState savedState = new SavedState(superState);
savedState.checked = isChecked();
return savedState;
}
#Override
public void onRestoreInstanceState(final Parcelable state) {
final SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
setChecked(savedState.checked);
requestLayout();
}
#SuppressLint("ClickableViewAccessibility")
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public boolean onTouchEvent(final MotionEvent e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && //
e.getActionMasked() == MotionEvent.ACTION_DOWN && //
mForeground != null)
mForeground.setHotspot(e.getX(), e.getY());
return super.onTouchEvent(e);
}
// /////////////
// SavedState //
// /////////////
private static class SavedState extends BaseSavedState {
boolean checked;
SavedState(final Parcelable superState) {
super(superState);
}
private SavedState(final Parcel in) {
super(in);
checked = (Boolean) in.readValue(null);
}
#Override
public void writeToParcel(final Parcel out, final int flags) {
super.writeToParcel(out, flags);
out.writeValue(checked);
}
#Override
public String toString() {
return TAG + ".SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked
+ "}";
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
#Override
public SavedState createFromParcel(final Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
}
}
attr.xml
<declare-styleable name="CheckableLinearLayout">
<attr name="foreground"/>
</declare-styleable>
Here's a more minimal way to do it, using Kotlin, and without the part of being checkable :
class LinearLayoutEx #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : LinearLayout(context, attrs, defStyle) {
var foregroundDrawable: Drawable? = null
set(value) {
field = value
invalidate()
}
init {
val a = context
.obtainStyledAttributes(attrs, R.styleable.LinearLayoutEx, defStyle, 0)
foregroundDrawable = a.getDrawable(R.styleable.LinearLayoutEx_foreground)
a.recycle()
}
override fun drawableStateChanged() {
super.drawableStateChanged()
val drawable = background
var needRedraw = false
val myDrawableState = drawableState
if (drawable != null) {
drawable.state = myDrawableState
needRedraw = true
}
if (foregroundDrawable != null) {
foregroundDrawable!!.state = myDrawableState
needRedraw = true
}
if (needRedraw)
invalidate()
}
override fun onSizeChanged(width: Int, height: Int, oldwidth: Int, oldheight: Int) {
super.onSizeChanged(width, height, oldwidth, oldheight)
foregroundDrawable?.setBounds(0, 0, width, height)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
foregroundDrawable?.draw(canvas)
}
#SuppressLint("ClickableViewAccessibility")
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun onTouchEvent(e: MotionEvent): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && e.actionMasked == MotionEvent.ACTION_DOWN)
foregroundDrawable?.setHotspot(e.x, e.y)
return super.onTouchEvent(e)
}
}
attr.xml
<declare-styleable name="LinearLayoutEx" tools:ignore="MissingDefaultResource">
<attr name="foreground"/>
</declare-styleable>

ListView item background hell

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>

Categories

Resources