customize a button with click effect on Android

Damon
Usually in Android when we need button click effect we will define different state images and create a drawable as button background.

But sometime it is really troublesome. coz you have to create different drawable files for different button. I am so lazy so try to find some generic ways.

So I just extends the Button and when get touch event change the colors.

Following codes.


public class EffectButton extends Button {

 int textColor ;
 
 public EffectButton(Context context, AttributeSet attrs) {
  
  super(context, attrs);
  
  int[] attrsArray = new int[] {
          android.R.attr.textColor
      };
      TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);
      textColor = ta.getColor(0, Color.TRANSPARENT);
      ta.recycle();
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN: {
   getBackground()
     .setColorFilter(0x88000000, PorterDuff.Mode.SRC_ATOP);
   setTextColor(Color.parseColor("#88000000"));
   invalidate();
   break;
  }
  case MotionEvent.ACTION_UP: {
   getBackground().clearColorFilter();
   setTextColor(textColor);
   invalidate();
   break;
  }
  }
  return super.onTouchEvent(event);
 }

}
After get the drawable of the button you can change the color using color filter. so add a simple darker effect. But there is one problem the text color not changed after that . So when init this button get the default text color. After that just change text color as what you want .

So from this way I think I can also change the ImageButton. The only difference is besides change the background we also need to change the image of that ImageButton.

Codes following:


public class EffectImageButton extends ImageButton {

 int textColor ;
 
 public EffectImageButton(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN: {
   getBackground()
     .setColorFilter(0x88000000, PorterDuff.Mode.SRC_ATOP);
   getDrawable().setColorFilter(0x88000000, PorterDuff.Mode.SRC_ATOP);
   invalidate();
   break;
  }
  case MotionEvent.ACTION_UP: {
   getBackground().clearColorFilter();
   getDrawable().clearColorFilter();
   invalidate();
   break;
  }
  }
  return super.onTouchEvent(event);
 }

}
After this continue... I want to find some more generic ways. So I changed the RelativeLayout.
public class EffectRelativeLayout extends RelativeLayout {

 boolean isDown = false;

 public EffectRelativeLayout(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);

 }

 @SuppressLint("NewApi")
 @Override
 protected void dispatchDraw(Canvas canvas) {
  super.dispatchDraw(canvas);
  if (isDown) {
   canvas.save();
   canvas.drawColor(Color.parseColor("#88000000"),
     PorterDuff.Mode.SRC_ATOP);
   canvas.restore();
  }
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN: {
   isDown = true;
   invalidate();
   return true;
  }
  case MotionEvent.ACTION_UP: {
   isDown = false;
   invalidate();
   if (isInsideClick(event)) {
    performClick();
   }
   return true;
  }
  }
  return super.onTouchEvent(event);
 }

 private boolean isInsideClick(MotionEvent event) {
  if(event.getRawX()>=getLeft()
    && event.getRawX()<= getRight()
    && event.getRawY()>=getTop()
    && event.getRawY()<=getBottom()){
   return true;
  }
  return false;
 }

}
This time there is a bit difference. coz I can not just directly get the background and child views. So I decide to change the canvas directly.

At first I change that inside of onDraw(). If so I can only change the layout color not the child views. So I moved the methods to dispatchDraw().
But there is another problem : coz the touch up event will be consumed and not come to layout this level. So I need to consume this first.
 So the next problem came out . Because of this the layout will not call onClickListener. So I have to performclick by myself. And then added a check whether user click inside of the layout.

So thats it. Now no need to create some many different buttons drawable xml.

Still have issues :
like the layout can not draw that shadow effect only on valid part. Now cover all the layout even some parts are transparent.

There should be some better ways????????

Comments

Popular posts from this blog

A wired issue of MediaPlayer on Android

How to add pre-receive hook in GitLab

Problem when using Jackson json parser in Android.