I must create EditText with button inside and assign functions which will run after pressing the button. Then I would like to make it reusable - somehow add it as an element to my Activity. I was wondering about extending EditTextView and adding button and creating functions inside of it.
Any suggestions/ tutorials?
This is what you need to do:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionHint="yourhint"
android:dropDownHeight="match_parent"
android:hint="From"
android:padding="20dp"
android:visibility="visible"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#id/image"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
</LinearLayout>
And set OnClicklistener to the button in your OnCreate method like this:
Button image = (Button) findViewById(R.id.image);
image.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
//handle your on click event here
}
});
You need to Create Custom View Components
Below links has sample and will solve your problems
https://developer.android.com/training/custom-views/index.html
https://developer.android.com/guide/topics/ui/custom-components.html
If you want some icon or button inside edit text you can achieve like this,
Note: In this just I am using setCompoundDrawablesWithIntrinsicBounds,
So if you want to change the icon position you can achieve that using
setCompoundDrawablesWithIntrinsicBounds in setIcon.
Create some CustomEditText like this,
public class MKEditText extends AppCompatEditText {
public interface IconClickListener {
public void onClick();
}
private IconClickListener mIconClickListener;
private static final String TAG = MKEditText.class.getSimpleName();
private final int EXTRA_TOUCH_AREA = 50;
private Drawable mDrawable;
private boolean touchDown;
public MKEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MKEditText(Context context) {
super(context);
}
public MKEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void showRightIcon() {
mDrawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_android_black_24dp);
setIcon();
}
public void setIconClickListener(IconClickListener iconClickListener) {
mIconClickListener = iconClickListener;
}
private void setIcon() {
Drawable[] drawables = getCompoundDrawables();
setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], mDrawable, drawables[3]);
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
setSelection(getText().length());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int right = getRight();
final int drawableSize = getCompoundPaddingRight();
final int x = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x + EXTRA_TOUCH_AREA >= right - drawableSize && x <= right + EXTRA_TOUCH_AREA) {
touchDown = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
if (x + EXTRA_TOUCH_AREA >= right - drawableSize && x <= right + EXTRA_TOUCH_AREA && touchDown) {
touchDown = false;
if (mIconClickListener != null) {
mIconClickListener.onClick();
}
return true;
}
touchDown = false;
break;
}
return super.onTouchEvent(event);
}
}
If you want to change the touch area you can change the
EXTRA_TOUCH_AREA values default I gave as 50.
And for Enable the button and click listener you can call from your Activity or Fragment like this,
MKEditText mkEditText = (MKEditText) findViewById(R.id.password);
mkEditText.showRightIcon();
mkEditText.setIconClickListener(new MKEditText.IconClickListener() {
#Override
public void onClick() {
// You can do action here. ex you can start activity here like this
startActivityForResult(new Intent(MainActivity.this, SampleActivity.class), 1);
}
});
You can extend LinearLayout or RelativeLayout and add both Button and EditText in the constructor. However, you need to use a getter method to get the EditText or Button. Here is an example. I haven't tested it, but it should give you an idea. Just remember that, if you want to use xml attributes of Button or EditText, you should define them in attr.xml and then get them with TypedArray. Since the object is a LinearLayout, you cannot use EditText or Button attributes without defining them in attr.xml.
public class CustomEditText extends LinearLayout {
private AppCompatButton mButton;
private AppCompatEditText mEditText;
public CustomEditText(Context context) {
this(context, null, 0);
}
public CustomEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOrientation(HORIZONTAL);
/** Get Attributes **/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomEditText);
int lines = typedArray.getInt(R.styleable.CustomEditText_android_lines, 1);
int gravity = typedArray.getInt(R.styleable.CustomEditText_android_gravity, Gravity.CENTER_VERTICAL);
int inputType = typedArray.getInt(R.styleable.CustomEditText_android_inputType, InputType.TYPE_CLASS_TEXT);
int imeOptions = typedArray.getInt(R.styleable.CustomEditText_android_imeOptions, EditorInfo.IME_ACTION_DONE);
String hint = typedArray.getString(R.styleable.CustomEditText_hint);
typedArray.recycle();
/** Construct Button **/
mButton = new AppCompatButton(context);
addView(mButton);
/** Construct EditText **/
mEditText = new AppCompatEditText(context);
mEditText.setGravity(gravity);
mEditText.setHint(hint);
mEditText.setImeOptions(imeOptions | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mEditText.setInputType(inputType | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
if (!((inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) == InputType.TYPE_TEXT_FLAG_MULTI_LINE
&& lines == 1)) mEditText.setLines(lines);
addView(mEditText);
}
public AppCompatEditText getEditText() {
return mEditText;
}
public AppCompatButton getButton() {
return mButton;
}
}
Related
I'm trying to make a custom EditText for currency which means I need to have a prefix of it for the currency and I have to limit users' input to numbers only.
This is my custom EditText code
public OpenSansEditText(Context context, AttributeSet attrs) {
super(context, attrs);
paint = getPaint();
applyCustomFont(context, attrs);
}
public OpenSansEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = getPaint();
applyCustomFont(context, attrs);
}
private void applyCustomFont(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.OpenSansET);
...
// Prefix
String prefix = a.getString(R.styleable.OpenSansET_prefix);
if (prefix != null) {
mPrefix = prefix;
} else {
mPrefix = "";
}
// Prefix Color
int prefixColor = a.getColor(R.styleable.OpenSansET_prefixColor, 0);
if (prefix != null) {
mPrefixColor = prefixColor;
} else {
mPrefixColor = ContextCompat.getColor(context, R.color.miBlack);
}
a.recycle();
}
...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mPrefix.equals("")) {
getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
mPrefixRect.right += getPaint().measureText(" "); // add some offset
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mPrefix.equals("")) {
paint.setColor(mPrefixColor);
canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), paint);
}
}
#Override
public int getCompoundPaddingLeft() {
return mPrefix.equals("") ? super.getCompoundPaddingLeft()
: super.getCompoundPaddingLeft() + mPrefixRect.width();
}
This is how I use it in xml :
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMinRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/min"
android:textColor="#color/miBlack"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMaxRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/max"
android:inputType="number"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
Which yields :
Only the first one without inputType as number has the currency sign shown, whereas the second ET doesn't have its currency sign shown.
How to achieve currency prefix as text and still keeping inputType to numbers only for user? And I don't want to use two views, namely EditText and TextView to left of it, both inside a ViewGroup to achieve that.
For this type of scenarios I use Compound views. Please see below code for more information.
First create a layout for your custom view like below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txt_prefix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$" />
<EditText
android:id="#+id/et_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number" />
</LinearLayout>
Now create a new class which should extends the LinearLayout. See below code.
public class OpenSansEditText extends LinearLayout {
private TextView txtPrefix;
private EditText etValue;
private String prefix = "$";
private int prefixColor = Color.BLACK;
public OpenSansEditText(Context context) {
super(context);
initializeViews(context, null);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initializeViews(context, attrs);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context, attrs);
}
private void initializeViews(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.opensansedittext_view, this,true);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.OpenSansEditText);
prefix = a.getString(R.styleable.OpenSansEditText_prefix);
prefixColor = a.getColor(R.styleable.OpenSansEditText_prefixColor, Color.BLACK);
}
}
public CharSequence getValue(){
return etValue.getText();
}
public CharSequence getPrefix(){
return txtPrefix.getText();
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
txtPrefix = (TextView) findViewById(R.id.txt_prefix);
etValue = (EditText) findViewById(R.id.et_value);
txtPrefix.setText(prefix);
txtPrefix.setTextColor(prefixColor);
}
}
And then add your attributes to attribute xml file Ex: (attrs.xml in my case)
<resources>
<declare-styleable name="OpenSansEditText">
<attr name="prefix" format="string"/>
<attr name="prefixColor" format="color"/>
</declare-styleable>
</resources>
Now you can use it anywhere in the project as below
<com.asta.www.classes.OpenSansEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:prefix="$"
app:prefixColor="#f00"/>
Hope this will help you to solve your problem. Thanks...
In the end I found this link https://gist.github.com/kennydude/5407963 which helps me in the right direction. So what it does is I think making the prefix as Drawable using this class :
private class TagDrawable extends Drawable {
public String text = "";
public void setText(String s){
text = s;
// Tell it we need to be as big as we want to be!
setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
invalidateSelf();
}
#Override
public void draw(#NonNull Canvas canvas) {
// I don't know why this y works here, but it does :)
// (aka if you are from Google/are Jake Wharton and I have done it wrong, please tell me!)
canvas.drawText( text, 0, mLine0Baseline + canvas.getClipBounds().top, mTextPaint );
}
#Override public void setAlpha(int i) {}
#Override public void setColorFilter(ColorFilter colorFilter) {}
#Override public int getOpacity() {return PixelFormat.UNKNOWN;}
#Override public int getIntrinsicHeight (){
return (int)mFontHeight;
}
#Override public int getIntrinsicWidth(){
return (int)mTextPaint.measureText( text );
}
}
And draw it to the left of the TextView like
TagDrawable left = new TagDrawable();
left.setText("$");
setCompoundDrawablesRelative(left, null, null, null);
The link I supplied even has suffix support which I haven't tried.
I have a next problem, which happens in 2 cases:
First case.
1). I have some custom veiw which draw photos on it with different opacity. here is method MyView.onDraw:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.getClipBounds(clipRect);
int i1 = Math.min(testColors.length-1, (int)Math.floor(posX/PHOTO_DISTANCE));
int c1 = testColors[Math.max(0, i1)];
int i2 = Math.min(testColors.length-1, (int)Math.ceil(posX/PHOTO_DISTANCE));
int c2 = testColors[Math.max(0, i2)];
paint.setColor(c1);
float r = (255f/PHOTO_DISTANCE*posX)%255;
paint.setAlpha(255);
if(photoA != null){//bitmap != null
bitmapRect.set(0, 0, photoA.getWidth(), photoA.getHeight());
canvas.drawBitmap(photoA, bitmapRect, clipRect, paint);
}
paint.setAlpha((int)(r));
if(photoB != null){//bitmap != null
bitmapRect.set(0, 0, photoB.getWidth(), photoB.getHeight());
canvas.drawBitmap(photoB, bitmapRect, clipRect, paint);
}
}
testColors - array of colors(int);
photoA, photoB - bitmaps;
i1, i2 - image indexes;
c1, c2 - colors. they are not importatant.
I added this view to FrameView:
viewHolder.myFrame.addView(viewHolder.myView, 0);
And in this FrameView I have some clickable RelativeLayout's:
<com.app.custom.view.ClickableRelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/share_action_container"
app1:pressedStateColor="#color/app_pressed_default"
app1:unpressedStateColor="#color/color_white"
android:background="#color/transparent"
android:layout_centerHorizontal="true">
<RelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/circle_background"
android:id="#+id/share_icon"
android:layout_centerHorizontal="true">
<ImageView
android:id="#+id/img_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:tag="icon"
android:src="#drawable/ic_share"/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_below="#id/share_icon"
android:text="Share"
android:layout_marginTop="3dp"
android:shadowColor="#color/text_shadow"
android:shadowDx="#integer/shadowDX"
android:shadowDy="#integer/shadowDY"
android:shadowRadius="#integer/shadowRadius"
android:background="#color/transparent"
android:layout_centerHorizontal="true"
android:textSize="#dimen/icon_text_size"
android:tag="text"
android:textColor="#android:color/white"
android:id="#+id/txt_share_action"/>
</com.app.custom.view.ClickableRelativeLayout>
Here is a Java code of ClickableRelativeLayout:
public class ClickableRelativeLayout extends RelativeLayout implements View.OnTouchListener {
private ViewHolder viewHolder;
private int pressedStateColor;
private int unpressedStateColor;
private final int DEFAULT_PRESSED_STATE_COLOR;
private final int DEFAULT_UNPRESSED_STATE_COLOR;
public ClickableRelativeLayout(Context context) {
super(context);
DEFAULT_PRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_pressed_default);
DEFAULT_UNPRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_blue_without_transparent);
setup();
initColors(context, null);
}
public ClickableRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
DEFAULT_PRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_pressed_default);
DEFAULT_UNPRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_blue_without_transparent);
setup();
initColors(context, attrs);
}
public ClickableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
DEFAULT_PRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_pressed_default);
DEFAULT_UNPRESSED_STATE_COLOR = context.getResources().getColor(R.color.app_blue_without_transparent);
setup();
initColors(context, attrs);
}
private void setup(){
setOnTouchListener(this);
}
private void initColors(Context context, AttributeSet attrs){
if(attrs != null) {
TypedArray styledAttributes = context.obtainStyledAttributes(attrs, R.styleable.ClickableRelativeLayout);
pressedStateColor = styledAttributes.getColor(R.styleable.ClickableRelativeLayout_pressedStateColor,
DEFAULT_PRESSED_STATE_COLOR);
unpressedStateColor = styledAttributes.getColor(R.styleable.RowLayout_android_verticalSpacing,
DEFAULT_UNPRESSED_STATE_COLOR);
styledAttributes.recycle();
}else{
pressedStateColor = DEFAULT_PRESSED_STATE_COLOR;
unpressedStateColor = DEFAULT_UNPRESSED_STATE_COLOR;
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
viewHolder = new ViewHolder(
(TextView) findViewWithTag("text"),
(ImageView) findViewWithTag("icon")
);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
if (hasOnClickListeners()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
select();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
unSelect();
break;
}
}
}
return false;
}
private void select(){
if(isInitializedCorrect()){
final int color = pressedStateColor;
viewHolder.text.setTextColor(color);
ImageHelper.INSTANCE.applyColorFilterToImage(viewHolder.icon.getDrawable(), color);
}
}
private void unSelect(){
if(isInitializedCorrect()){
final int color = unpressedStateColor;
viewHolder.text.setTextColor(color);
viewHolder.icon.setColorFilter(color);
}
}
private boolean isInitializedCorrect(){
return viewHolder != null && viewHolder.icon != null && viewHolder.text != null;
}
private class ViewHolder{
ImageView icon;
TextView text;
public ViewHolder(TextView text, ImageView icon) {
this.text = text;
this.icon = icon;
}
}
}
And when I clicked on this layout, background of MyView shrinks, and sets to this ClickableRelativeLayout:
And Second case.
I have some text view on same frame, it is invisible by default, and when you scrolled 10 photos, I apply AlphaAnimation for this TextView and it draws slowly. Here is a code of alpha animation:
AlphaAnimation animation1 = new AlphaAnimation(0.0f, 1.0f);
animation1.setDuration(1300);
animation1.setFillAfter(true);
//here is my TextView
viewHolder.gotItView.setVisibility(View.VISIBLE);
viewHolder.gotItView.startAnimation(animation1);
And happens the same this. On background of this TextView appears content of MyView
I have a custom widget which is basically an EditText which has a small 'clear' button to the right, which cleans the edittext.
I built it as a subclass of LinearLayout, which adds the EditText and the Button in the constructor.
Everything works the way it should, except that I want the whole LinearLayout to have a Focused style when the EditText has focus; I want the user to see it as an EditText, not as a container with an EditText.
I attempted using addStatesFromChildren, but it does not seem to work. Code attached.
ErasableField.java
public class ErasableField extends LinearLayout {
private EditText editText;
private View button;
public ErasableField(Context context) {
super(context);
init(null);
}
public ErasableField(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ErasableField(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
this.setOrientation(HORIZONTAL);
LayoutInflater inflater = LayoutInflater.from(getContext());
editText = (EditText) inflater.
inflate(R.layout.erasable_field_edittext, this, false);
button = inflater.inflate(R.layout.erasable_field_button, this, false);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ErasableField);
boolean password = a.getBoolean(R.styleable.ErasableField_password, false);
if (password) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT);
}
}
editText.setFocusable(true);
editText.setClickable(true);
editText.setFocusableInTouchMode(true);
this.setClickable(false);
this.setFocusable(false);
this.setFocusableInTouchMode(false);
this.setGravity(Gravity.CENTER_VERTICAL);
this.setBackgroundResource(android.R.drawable.editbox_background);
addView(editText);
addView(button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
editText.getText().clear();
}
});
this.addStatesFromChildren();
}
public Editable getText() {
return editText.getText();
}
public void setText(CharSequence text) {
editText.setText(text);
}
public void setText(int resId) {
editText.setText(resId);
}
public void setText(char[] text, int start, int len) {
editText.setText(text, start, len);
}
}
erasable_field_edittext.xml
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="fill_parent"
android:background="#color/white"
android:singleLine="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textCursorDrawable="#color/grey"
>
</EditText>
erasable_field_button.xml
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/Button.Delete"
>
</ImageButton>
Nevermind the missing style, it's just a red button with a little cross, it looks like this:
When the user taps the edittext area, the style should change to a focused style.
I.e. (ignore the different button look ):
The style I'm trying to use a built in from Android:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="#drawable/editbox_background_focus_yellow" />
<item android:drawable="#drawable/editbox_background_normal" />
</selector>
Any help?
It's fixed by changing
this.addStatesFromChildren();
to
this.setAddStatesFromChildren(true);
...
I have around twenty custom buttons in my app, with a different image for each button.
I know that to create a click effect I have to create an XML resource like this, I have to created 20 different XML resources for my buttons.
Is there a better way to get the same result without creating separate XML resources for each button?
Update:
Can we make the button translucent when it is clicked.
Found the solution Here.
public class SAutoBgButton extends Button {
public SAutoBgButton(Context context) {
super(context);
}
public SAutoBgButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SAutoBgButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setBackgroundDrawable(Drawable d) {
// Replace the original background drawable (e.g. image) with a LayerDrawable that
// contains the original drawable.
SAutoBgButtonBackgroundDrawable layer = new SAutoBgButtonBackgroundDrawable(d);
super.setBackgroundDrawable(layer);
}
/**
* The stateful LayerDrawable used by this button.
*/
protected class SAutoBgButtonBackgroundDrawable extends LayerDrawable {
// The color filter to apply when the button is pressed
protected ColorFilter _pressedFilter = new LightingColorFilter(Color.LTGRAY, 1);
// Alpha value when the button is disabled
protected int _disabledAlpha = 100;
public SAutoBgButtonBackgroundDrawable(Drawable d) {
super(new Drawable[] { d });
}
#Override
protected boolean onStateChange(int[] states) {
boolean enabled = false;
boolean pressed = false;
for (int state : states) {
if (state == android.R.attr.state_enabled)
enabled = true;
else if (state == android.R.attr.state_pressed)
pressed = true;
}
mutate();
if (enabled && pressed) {
setColorFilter(_pressedFilter);
} else if (!enabled) {
setColorFilter(null);
setAlpha(_disabledAlpha);
} else {
setColorFilter(null);
}
invalidateSelf();
return super.onStateChange(states);
}
#Override
public boolean isStateful() {
return true;
}
}
}
Use this in xml:
<net.shikii.widgets.SAutoBgButton
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:background="#drawable/button_blue_bg"
android:text="Button with background image" />
I've created a custom view which should change it's background image when pressed, highlighted or disabled. The app runs but the button doesn't change it's background.
here's my code:
public class CustomImageButton extends View {
public CustomImageButton(Context context) {
super(context);
setFocusable(true);
setClickable(true);
}
public CustomImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
setClickable(true);
}
public CustomImageButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setFocusable(true);
setClickable(true);
}
protected Drawable background = super.getBackground();
#Override
public void setBackgroundDrawable(Drawable d) {
// Replace the original background drawable (e.g. image) with a LayerDrawable that
// contains the original drawable slightly edited.
CustomImageButtonBackgroundDrawable layer = new CustomImageButtonBackgroundDrawable(d);
super.setBackgroundDrawable(layer);
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int drawableWidth = super.getBackground().getMinimumWidth();
int drawableHeight = super.getBackground().getMinimumHeight();
setMeasuredDimension(drawableWidth, drawableHeight);
}
protected class CustomImageButtonBackgroundDrawable extends LayerDrawable {
protected Drawable lowerlayer;
protected Drawable _highlightedDrawable;
protected int _disabledAlpha = 100;
protected Drawable _pressedDrawable;
public CustomImageButtonBackgroundDrawable(Drawable d) {
super(new Drawable[] { d });
}
#Override
protected boolean onStateChange(int[] states) {
boolean enabled = false;
boolean highlighted = false;
boolean pressed = false;
for (int state : states) {
if (state == android.R.attr.state_enabled)
enabled = true;
else if (state == android.R.attr.state_selected)
highlighted = true;
else if (state == android.R.attr.state_pressed)
pressed = true;
}
mutate();
if (enabled && highlighted) {
ColorFilter colourFilter = new LightingColorFilter(Color.YELLOW, 1);
ScaleDrawable resizedImage = new ScaleDrawable(background, 0, 1.25f, 1.25f);
lowerlayer = resizedImage.getDrawable();
lowerlayer.setColorFilter(colourFilter);
Drawable[] aD = new Drawable[2];
aD[0] = lowerlayer;
aD[1] = background;
LayerDrawable _highlightedDrawable = new LayerDrawable(aD);
setBackgroundDrawable(_highlightedDrawable); // buttons need transparent backgrounds
} else if (!enabled) {
setColorFilter(null);
setAlpha(_disabledAlpha);
} else if (enabled && pressed){
ScaleDrawable smaller = new ScaleDrawable(background, 0, 0.75f, 0.75f);
setBackgroundDrawable(smaller.getDrawable());
} else if(enabled){
setBackgroundDrawable(background);
setColorFilter(null);
}
invalidateSelf();
return super.onStateChange(states);
}
}
}
Here's my xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff">
<ImageButton
android:id="#+id/title"
android:layout_width="250dp"
android:layout_height="58dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin ="25dp"
android:background="#drawable/skintonetitle" />
<custombuttons.CustomImageButton
android:id="#+id/skina1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/title"
android:layout_below="#+id/title"
android:layout_marginTop="35dp"
android:background="#drawable/test_circle"
android:clickable="true"
android:focusable="true" />
</RelativeLayout>
Something I've missed?
It extends from View, not button, so it's not clickable or focusable by default. Adjust with
android:clickable="true"
android:focusable="true"
in your XML.
You can also set these in the constructor of your View class if you want to do it in java:
setFocusable(true);
setClickable(true);
in my case I was using a custom view with a constraint layout as the root .and I was not getting click events on setOnClickListener of my custom view,it turns out that I needed to set android:clickable="false" in the root of my xml for the custom view.apparently , the click event is dispatched to the root of my custom view xml rather than to the custom view it self (i.e setOnClickListener of the custom view )