I use :
public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked, };
private boolean mChecked;
public CheckableRelativeLayout(Context context) {
super(context);
}
public CheckableRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
#Override
public boolean isChecked() {
return mChecked;
}
#Override
public void toggle() {
setChecked(!mChecked);
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
based on this and i use it like this :
<com.example.components.CheckableRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/CheckableRelativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector_background_navigation_item"
android:orientation="horizontal"
android:padding="10dp"
android:gravity="center_vertical" >
<ImageView
android:id="#+id/navigationDrawerItemImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/navigationDrawerItemTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/navigationDrawerItemImageView"
android:layout_margin="5dp"
android:layout_toRightOf="#+id/navigationDrawerItemImageView"
android:textColor="#drawable/selector_text_navigation_item"
android:textSize="#dimen/text_size_small" />
</com.example.components.CheckableRelativeLayout>
in a list item where selector_background_navigation_item defined like this :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="false" android:state_pressed="false" android:drawable="#color/darkblue"></item>
<item android:state_pressed="true" android:drawable="#color/sirinblue"></item>
<item android:state_checked="true" android:drawable="#color/white"></item>
</selector>
and selector_text_navigation_item defined like this :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="false" android:state_pressed="false" android:color="#color/white"></item>
<item android:state_pressed="true" android:color="#color/lightblue"></item>
<item android:state_checked="true" android:color="#color/lightblue"></item>
</selector>
Now, the problem is that the TextView Selector does not respond to the check events, while the CheckableRelativeLayout does, but both respond correctly to simple presses, whats the best way to fix this? maybe CheckableRelativeLayout missing some super call that handles its children checked state?
adding android:duplicateParentState="true" to the TextView did the trick!
Related
I want to have the following:
a textview that
.)changes its background when clicked
.)maintains that background until it is clicked again
it all comes down to the "checkable" state, but i couldnt figure out how this exactly works. here is the xml i am using for background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="#drawable/menuselected"
android:state_pressed="true" />
<!-- checked -->
<item android:drawable="#drawable/menuselected"
android:state_checked="true" />
<!-- default -->
<item android:drawable="#drawable/transpixel"/>
</selector>
Update: it partly works now. I adopted most of the code from http://kmansoft.com/2011/01/11/checkable-image-button/ for my custom Textview. I did this as actually, I need the functionality of a radio button as well.
Now I can check a Textview, but I cant uncheck it. Does anybody see why that could be the case?
You can use CheckedTextView with
checkMark null
and
background your selectable
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="#null"
android:background="#drawable/selectable"/>
your selectable can be
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/selector" />
</selector>
Make a custom TextView implementing android.widget.Checkable interface. That should be sufficient to make your selector work.
Below is the example implementation:
public class CheckableTextView extends TextView implements Checkable {
private boolean isOn=false;
public CheckableTextView(Context context) {
super(context);
}
public CheckableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked())
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
return drawableState;
}
#Override
public void setChecked(boolean checked) {
isOn=checked;
refreshDrawableState();
}
#Override
public boolean isChecked() {
return isOn;
}
#Override
public void toggle() {
isOn=!isOn;
refreshDrawableState();
}
}
I need a "Button" that would have a texture background, and dynamic text. The end result should be like this, but the background is a textured picture.
I tried to use a RelativeLayout with a TextView inside, but the problem is that the result CustomView does not wrap_content in width/height.
Here is the custom_buttonXML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#drawable/custom_button_selector"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_centerInParent="true"
/>
</RelativeLayout>
Here is the Code:
public class CustomButton extends RelativeLayout {
private LayoutInflater mInflater;
private RelativeLayout mContainer;
private TextView mTextView;
public CustomButton(Context context) {
super(context);
mInflater = LayoutInflater.from(context);
customInit(context);
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
mInflater = LayoutInflater.from(context);
customInit(context);
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
customInit(context);
}
private void customInit(Context ctx) {
mContainer = (RelativeLayout) mInflater.inflate(R.layout.cutom_button_view, this, true);
mTextView = (TextView) mContainer.findViewById(R.id.tv);
}
public RelativeLayout getContainer() {
return mContainer;
}
public TextView getTextView() {
return mTextView;
}
}
This is how I use it at the moment, and it works:
<com.myapp.views.CustomButton
android:id="#+id/btn"
android:layout_width="80dp"
android:layout_height="50dp"/>
But I want to use it like this (for dynamic text), and it won't work:
<com.myapp.views.CustomButton
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
It will actually stretch across the whole screen, ignoring the layout rules.
If this solution is a bad idea, could you recommend an alternative that would produce the same results?
You need to Shape file for that..
try out this.
<shape android:shape="rectangle">
<corners android:radius="56dp" />
<solid android:color="Color Whaterver You Want" />
<padding android:bottom="4dp" android:left="4dp" android:right="4dp" android:top="4dp" />
</shape>
Put this xml file under res/drawal folder.
now just in Your view pass this attribute..
android:background="#drawable/your file name"
Hop it works for you..
Custom layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/my_background">
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center" />
</RelativeLayout>
Custom layout java:
public class CustomButton extends FrameLayout {
private RelativeLayout mContainer;
private TextView mTextView;
public CustomButton(Context context) {
super(context);
customInit(context);
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
customInit(context);
}
public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
customInit(context);
}
private void customInit(Context ctx) {
LayoutInflater.from(ctx).inflate(R.layout.custom_button_viewa, this, true);
mContainer = (RelativeLayout) findViewById(R.id.container);
mTextView = (TextView) findViewById(R.id.tv);
}
public RelativeLayout getContainer() {
return mContainer;
}
public TextView getTextView() {
return mTextView;
}
}
Now can be used as:
<com.androidbolts.databindingsample.model.CustomButton
android:id="#+id/customButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
My problem is when my CheckableLinearLayout is checked my draw from the selector can't overlap the unchecked image.
My CheckableLinearLayout
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 };
public CheckableLinearLayout(final Context context) {
super(context);
setClickable(true);
setLongClickable(true);
}
public CheckableLinearLayout(final Context context,
final AttributeSet attrs) {
super(context, attrs);
setClickable(true);
setLongClickable(true);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public CheckableLinearLayout(final Context context,
final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
setClickable(true);
setLongClickable(true);
}
#Override
public void setChecked(final boolean checked) {
mChecked = checked;
refreshDrawableState();
}
#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();
if (drawable != null) {
final int[] myDrawableState = getDrawableState();
drawable.setState(myDrawableState);
invalidate();
}
}
#Override
public boolean performClick() {
Toast.makeText(getContext(), "click", Toast.LENGTH_SHORT).show();
if (isChecked()) {
toggle();
return false;
} else
return super.performClick();
}
#Override
public boolean performLongClick() {
toggle();
Toast.makeText(getContext(), "long click", Toast.LENGTH_SHORT).show();
return super.performLongClick();
}
#Override
public boolean isChecked() {
return mChecked;
}
#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();
}
// /////////////
// SavedState //
// /////////////
private static class SavedState extends BaseSavedState {
boolean checked;
#SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR;
static {
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];
}
};
}
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 + "}";
}
}
}
My layout.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/topics_preview_main_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/favorites_header_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="2dp"
android:orientation="vertical" >
<TextView
android:id="#+id/favorites_header_title"
style="#style/CardText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:background="#drawable/card"
android:gravity="left"
android:paddingBottom="10dp"
android:paddingLeft="5dp"
android:paddingTop="10dp"
android:text="#string/favorites_header" />
<ui.CheckableLinearLayout
android:id="#+id/favorites_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:background="#drawable/card_selector_w_shadow"
android:clickable="true"
android:longClickable="true"
android:orientation="vertical"
android:paddingBottom="16dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp" >
<TextView
android:id="#+id/favorites_title"
style="#style/CardTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="title" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="4dp"
android:background="#color/C_Favorites_Pink" />
<LinearLayout
android:id="#+id/favorites_description_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="4dp" >
<TextView
android:id="#+id/favorites_description"
style="#style/CardText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:ellipsize="end"
android:maxLines="4"
android:text="Lorem ipsum ..." />
</LinearLayout>
</ui.CheckableLinearLayout>
</LinearLayout>
</FrameLayout>
card_selector_w_shadow-> this draw will call the selector
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape
android:dither="true"
android:shape="rectangle" >
<corners android:radius="2dp" />
<solid android:color="#ccc" />
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle" >
<corners android:radius="2dp" />
<solid android:color="#android:color/white" />
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp" />
</shape>
</item>
<item android:drawable="#drawable/drawable_states"/>
</layer-list>
My drawable_states
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/checked_background"
android:state_checked="true"/>
</selector>
This is what's happening:
This is what I intend:
I think this is because my CheckableLinearLayout only changes the state of the children, so the draw only overlap the children. Any advices?
I want to have the following:
a textview that
.)changes its background when clicked
.)maintains that background until it is clicked again
it all comes down to the "checkable" state, but i couldnt figure out how this exactly works. here is the xml i am using for background:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="#drawable/menuselected"
android:state_pressed="true" />
<!-- checked -->
<item android:drawable="#drawable/menuselected"
android:state_checked="true" />
<!-- default -->
<item android:drawable="#drawable/transpixel"/>
</selector>
Update: it partly works now. I adopted most of the code from http://kmansoft.com/2011/01/11/checkable-image-button/ for my custom Textview. I did this as actually, I need the functionality of a radio button as well.
Now I can check a Textview, but I cant uncheck it. Does anybody see why that could be the case?
You can use CheckedTextView with
checkMark null
and
background your selectable
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checkMark="#null"
android:background="#drawable/selectable"/>
your selectable can be
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/selector" />
</selector>
Make a custom TextView implementing android.widget.Checkable interface. That should be sufficient to make your selector work.
Below is the example implementation:
public class CheckableTextView extends TextView implements Checkable {
private boolean isOn=false;
public CheckableTextView(Context context) {
super(context);
}
public CheckableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public int[] onCreateDrawableState(final int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked())
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
return drawableState;
}
#Override
public void setChecked(boolean checked) {
isOn=checked;
refreshDrawableState();
}
#Override
public boolean isChecked() {
return isOn;
}
#Override
public void toggle() {
isOn=!isOn;
refreshDrawableState();
}
}
I want to color to background of an image View when it get pressed.
I tried to do it with selector that changes the image for the different states (pressed and not pressed).
The problem now is that the image replaces the previous with the exact same size and I want it be to much larger.
I use a custom layout that implements checkable, and I tried also to overload the onCreateDrawableState function, but it never being called.
Help please..
This is how it looks:
before pressing:
after pressing:
This is the item list custom layout:
public class ListItem extends RelativeLayout implements Checkable, OnClickListener {
private View mItemChecked = null;
private ImageView imageSkinEdit = null;
/*private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_pressed
};*/
public ListItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListItem(Context context) {
super(context);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
try {
mItemChecked = this.findViewById(R.id.itemlist_checkedd);
imageSkinEdit = (ImageView)this.findViewById(R.id.skinEdit);
imageSkinEdit.setOnClickListener(this);
} catch (Exception e) {
}
}//onFinishInflate
#Override
public boolean isChecked() {
if(mItemChecked != null) {
return ((Checkable)mItemChecked).isChecked();
}
return false;
}
#Override
public void setChecked(boolean checked) {
if(mItemChecked != null) {
if (checked) {
mItemChecked.setVisibility(View.VISIBLE);
} else {
mItemChecked.setVisibility(View.GONE);
}
}
}
#Override
public void toggle() {
if(mItemChecked != null) {
((Checkable)mItemChecked).toggle();
}
}
#Override
public void onClick(View v) {
final int id = v.getId();
switch (id) {
case R.id.skinEdit:
//handleEdit();
break;
}//switch
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
Log.d("onCreateDrawableState", "onCreateDrawableState");
return super.onCreateDrawableState(extraSpace + 1);
/*final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
Log.d("ListItem", "isChecked");
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;*/
}
}
The selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#drawable/ic_go_edit_pressed" />
<item
android:state_pressed="false"
android:drawable="#drawable/ic_go_edit" />
</selector>
Image View XML:
<ImageView
android:id="#+id/skinEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:src="#drawable/edit_skin_selector" />
Try calling refreshDrawableState() at the end of setChecked. That will get onCreateDrawableState to be called
#Override
public void setChecked(boolean checked) {
if(mItemChecked != null) {
if (checked) {
mItemChecked.setVisibility(View.VISIBLE);
} else {
mItemChecked.setVisibility(View.GONE);
}
}
refreshDrawableState();
}
Finally I did it using a different method. You can see it in my next question
you should add a default state to your selector too
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#drawable/button_pressed" /> <!-- pressed -->
<item android:state_focused="true"
android:drawable="#drawable/button_focused" /> <!-- focused -->
<item android:state_hovered="true"
android:drawable="#drawable/button_focused" /> <!-- hovered -->
<item android:drawable="#drawable/button_normal" /> <!-- default -->
</selector>
I have known, that onCreateDrawableState not call i.e. ListItem has not background drawable.