Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/179.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Android KeyboardView中实现涟漪效应?_Android_Android 5.0 Lollipop_Material Design_Rippledrawable - Fatal编程技术网

如何在Android KeyboardView中实现涟漪效应?

如何在Android KeyboardView中实现涟漪效应?,android,android-5.0-lollipop,material-design,rippledrawable,Android,Android 5.0 Lollipop,Material Design,Rippledrawable,我一直在试图找到一种方法,在键盘视图键被按下时实现涟漪效应。听起来很简单,但我已经尝试了所有添加ripple的方法,这些方法可以在其他类型的视图(listview、button等)上使用,但都没有成功 我的目标是创建一个数字键盘,它看起来像棒棒糖操作系统中默认的股票计算器应用程序中的键盘: GitHub()中有一个类似的计算器应用程序,它的键盘上有涟漪效应,但当我阅读代码时,它似乎在使用数字键的按钮,而不是键盘视图 我希望KeyboardView可以产生涟漪效应,因为我的应用程序已经实现了使用

我一直在试图找到一种方法,在键盘视图键被按下时实现涟漪效应。听起来很简单,但我已经尝试了所有添加ripple的方法,这些方法可以在其他类型的视图(listview、button等)上使用,但都没有成功

我的目标是创建一个数字键盘,它看起来像棒棒糖操作系统中默认的股票计算器应用程序中的键盘:

GitHub()中有一个类似的计算器应用程序,它的键盘上有涟漪效应,但当我阅读代码时,它似乎在使用数字键的按钮,而不是键盘视图

我希望KeyboardView可以产生涟漪效应,因为我的应用程序已经实现了使用KeyboardView的自定义数字键盘,我不想把它改为使用按钮

我已经尝试将ripple添加为样式中的
keyBackground
属性,如下所示:

<android.inputmethodservice.KeyboardView
    android:id="@+id/numeric_keypad"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    style="@style/my_numeric_keypad" />
numeric_keypad_pressed.xml和numeric_keypad_normal.xml只是每个特定状态的可绘制颜色,如下所示(两者完全相同,只是颜色属性不同):


我认为我上面的方法会奏效;但事实并非如此。在我的棒棒糖设备中,当我按下键盘时,它只显示正常按下的颜色,没有任何波纹,与我以前没有波纹的实现没有任何区别。我试着移除该层,因为我认为它在某种程度上与涟漪重叠,但仍然不起作用。向涟漪添加遮罩也不起作用

我也尝试过在选择器中使用rippledrawable作为pressed state drawable,而不是包装选择器,但它仍然不起作用。还尝试使用
?android:attr/selectableItemBackground
代替rippledrawable,但也不起作用


哦,我实际上是在Xamarin上开发应用程序,而不是在原生Android上开发,但我想这不会有什么区别。

只是回答我自己的问题,这样可能会帮助其他人

正如@alanv在评论中提到的,Ripples在KeyboardView中不起作用,因为它处理渲染和触摸交互的方式不同

所以答案是否定的,在Android KeyboardView中使用ripple effect是不可能的


希望这可以避免其他人浪费时间试图找出如何将ripple添加到KeyboardView:)

我的回答是:没有一种已经实现的方法(换句话说:一种简单的方法)可以做到这一点。但是我们谈论的是开源技术,所以

如果您有时间和耐心,可以从创建自定义键盘视图,以重新定义默认组件使用ripple兼容布局创建视图的方式


“如果你不能解决问题,就改变问题”(亨利·福特)

如果有人需要的话,顺便说一句,在可拉丝上有色调效果:

import java.util.List;

import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.Animator.AnimatorListener;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.os.Build;
import android.inputmethodservice.KeyboardView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.graphics.Region.Op;

public class RippleKeyboardView extends KeyboardView 
{
private Bitmap                      tintedBitmap,tintedBitmap2;
private static final Bitmap.Config  BITMAP_CONFIG               = Bitmap.Config.ARGB_8888;
private Paint                       mPaint                      = new Paint();
private int                         tintcolor                   = 0xffff0000;
private BitmapDrawable              mDrawable,mDrawable2;
private int                         invalidatekeyindex          = -1;
private static final int            NOT_A_KEY                   = -1;
private float                       animationcircleProgress;
private Paint                       circlePaint                 = new Paint();
private ValueAnimator               circleAnimator;
private static final int            ANIMATION_TIME_ID           = android.R.integer.config_shortAnimTime;
private float                       ripplex,rippley;
private float                       textoffsety;
private int                         keytextsize;
private int                         mLabelTextSize;
private Rect                        mDirtyRect = new Rect();
private boolean                     mDrawPending;
private Bitmap                      mBuffer;
private boolean                     mKeyboardChanged;
private Canvas                      mCanvas;
private Keyboard                    mKeyboard;
private List<Key>                   mkeys;
private Rect                        tobeinvalidated=new Rect();

@SuppressWarnings("deprecation")
@TargetApi(21)
public                              RippleKeyboardView(Context context, AttributeSet attrs) 
{
    super(context, attrs);
    if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    {
        tintedBitmap                    = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_delete,null)));
        tintedBitmap2                   = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_feedback_return,null)));
    }
    else
    {
        tintedBitmap                    = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_delete)));
        tintedBitmap2                   = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_feedback_return)));
    }
    mDrawable                       = new BitmapDrawable(getResources(), tintedBitmap);
    mDrawable2                      = new BitmapDrawable(getResources(), tintedBitmap2);

    TypedArray a                    = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RippleKeyboardView, 0, 0);
    keytextsize                     = a.getDimensionPixelSize(R.styleable.RippleKeyboardView_keytxtsize,60);
    mLabelTextSize                  = a.getDimensionPixelSize(R.styleable.RippleKeyboardView_lblsize, 40);
    a.recycle();

    animationcircleProgress         = 0;

    final int pressedAnimationTime  = getResources().getInteger(ANIMATION_TIME_ID);
    circleAnimator                  = ObjectAnimator.ofFloat(this, "animationlayerProgress", 100, 0f);
    circleAnimator.setDuration(pressedAnimationTime);
    circleAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

    mPaint.setColor(0xff000000);
    mPaint.setAntiAlias(true);

    mPaint.setTextAlign(Align.CENTER);
    mPaint.setAlpha(255);
    circlePaint.setColor(0x77989898);

    super.setPreviewEnabled(false);

    invalidateAllKeys();
}
private Bitmap                      getBitmapFromDrawable(Drawable drawable) 
{
    if (drawable == null) 
        return null;

    if (drawable instanceof BitmapDrawable) 
        return ((BitmapDrawable) drawable).getBitmap();

    try 
    {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
        Canvas canvas = new Canvas(bitmap);

        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    } 
    catch (OutOfMemoryError e) 
    {
        return null;
    }
}
private Bitmap                      generateIconBitmaps(Bitmap origin)
{
    if (origin == null) 
        return null;
    Bitmap bmp      = origin.copy(Bitmap.Config.ARGB_8888, true);
    Canvas canvas   = new Canvas(bmp);
    canvas.drawColor(tintcolor & 0x00ffffff | 0xff000000 , PorterDuff.Mode.SRC_IN);
    origin.recycle();
    return bmp;
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) 
{
    super.onSizeChanged(w, h, oldw, oldh);
    mBuffer = null;
}
@Override
public void setKeyboard(Keyboard keyboard) 
{
    super.setKeyboard(keyboard);
    mKeyboardChanged = true;
    mKeyboard = keyboard;
    invalidateAllKeys();
}
@Override
public void onDraw(Canvas canvas)
{
    if (mDrawPending || mBuffer == null || mKeyboardChanged) 
        onBufferDraw();
    canvas.drawBitmap(mBuffer, 0, 0, null);
}
public void                         onBufferDraw() 
{
    if (mBuffer == null || mKeyboardChanged) 
    {
        if (mBuffer == null || mKeyboardChanged && (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) 
        {
            final int width = Math.max(1, getWidth());
            final int height = Math.max(1, getHeight());
            mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBuffer);
            mkeys = getKeyboard().getKeys();
        }
        invalidateAllKeys();
        mKeyboardChanged = false;
    }
    final Canvas canvas = mCanvas;
    canvas.clipRect(mDirtyRect, Op.REPLACE);
    canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
    if (mKeyboard == null) 
        return;
    List<android.inputmethodservice.Keyboard.Key> keys = mkeys;
    for (android.inputmethodservice.Keyboard.Key key : keys) 
    {
        canvas.translate(key.x , key.y );
        if (key.codes[0] == 67) 
        {
            final int drawableX = (key.width - 0 - key.icon.getIntrinsicWidth()) / 2 + 0;
            final int drawableY = (key.height - 0 - key.icon.getIntrinsicHeight()) / 2 + 0;
            canvas.translate(drawableX, drawableY);
            mDrawable.setBounds(0, 0, key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
            mDrawable.draw(canvas);
            canvas.translate(-drawableX, -drawableY);
        }
        else if (key.codes[0] == 66) 
        {
            final int drawableX = (key.width - 0 - key.icon.getIntrinsicWidth()) / 2 + 0;
            final int drawableY = (key.height - 0 - key.icon.getIntrinsicHeight()) / 2 + 0;
            canvas.translate(drawableX, drawableY);
            mDrawable2.setBounds(0, 0, key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
            mDrawable2.draw(canvas);
            canvas.translate(-drawableX, -drawableY);
        }
        else
        {
            String label = key.label.toString();
            if (label.length()>1 && key.codes.length < 2)
            {
                mPaint.setTextSize(mLabelTextSize);
                mPaint.setTypeface(Typeface.DEFAULT_BOLD);
            }
            else
            {
                mPaint.setTextSize(keytextsize);
                mPaint.setTypeface(Typeface.DEFAULT);
            }
            textoffsety= (mPaint.getTextSize() - mPaint.descent()) / 2;
            canvas.drawText(label,(key.width ) / 2,(key.height ) / 2+ textoffsety, mPaint);
        }
        canvas.translate(-key.x , -key.y ); 
    }
    if (invalidatekeyindex!=-1)
        canvas.drawCircle(ripplex , rippley, getAnimationlayerProgress(), circlePaint);
    mDrawPending = false;
    mDirtyRect.setEmpty();
}
@Override
public void invalidateAllKeys() 
{
    mDirtyRect.union(0, 0, getWidth(), getHeight());
    mDrawPending = true;
}
@Override
public void invalidateKey(int keyIndex) 
{
    List<android.inputmethodservice.Keyboard.Key> mKeys = mkeys;
    if (mKeys == null) return;
    if (keyIndex < 0 || keyIndex >= mKeys.size()) 
        return;
    final Key key = mKeys.get(keyIndex);

    mDirtyRect.union(key.x , key.y , key.x + key.width , key.y + key.height );
    invalidate(key.x , key.y , key.x + key.width , key.y + key.height );
}
@Override
public boolean                      performClick() 
{
    super.performClick();
    return true;
}
@Override
public boolean                      onTouchEvent(MotionEvent me) 
{
    boolean ret = super.onTouchEvent(me);
    final int action = me.getAction();
    int primaryIndex = NOT_A_KEY;
    if (action == MotionEvent.ACTION_UP)
    {
        int [] nearestKeyIndices=getKeyboard().getNearestKeys((int)me.getX(),(int) me.getY());
        final int keyCount = nearestKeyIndices.length;
        List<android.inputmethodservice.Keyboard.Key> keys = mkeys;
        for (int i = 0; i < keyCount; i++) 
        {
            final android.inputmethodservice.Keyboard.Key key = keys.get(nearestKeyIndices[i]);
            boolean isInside = key.isInside((int)me.getX(),(int)me.getY());
            if (isInside) 
                primaryIndex = nearestKeyIndices[i];
        }
    }
    if (primaryIndex!=NOT_A_KEY)
    {
        DrawCustomRipple(primaryIndex);
    }
    performClick();
    return ret;
}
private void                        DrawCustomRipple(int keyindex)
{
    if (circleAnimator.isRunning())
    {
        tobeinvalidated.set((int)(ripplex-animationcircleProgress-4),
                            (int)(rippley-animationcircleProgress-4),
                            (int)(ripplex+animationcircleProgress+4),
                            (int)(rippley+animationcircleProgress+4));
        circleAnimator.removeAllListeners();
        invalidatekeyindex = -1;
        mDrawPending = true;
        invalidatekeyindex = -1;
        mDirtyRect.union(tobeinvalidated);
        invalidate(tobeinvalidated);
    }
    invalidatekeyindex=keyindex;
    List<android.inputmethodservice.Keyboard.Key> keys = mkeys;
    final android.inputmethodservice.Keyboard.Key cKey=keys.get(invalidatekeyindex);
    if (cKey.label == null && cKey.codes[0] != 67 && cKey.codes[0] != 66)
        return;

    circleAnimator.setFloatValues(30.0f,100.0f);
    circleAnimator.addListener(new AnimatorListener() 
    {
        @Override
        public void onAnimationStart(Animator animation) 
        {
            final Rect bounds=new Rect();
            if (cKey.label!=null)
            {
                String label = cKey.label.toString();
                mPaint.getTextBounds(label, 0, 1, bounds);
            }
            ripplex =cKey.x+(cKey.width ) / 2+bounds.width()/2;
            rippley = cKey.y + (cKey.height ) / 2;
        }
        @Override
        public void onAnimationEnd(Animator animation) 
        {
            invalidatekeyindex = -1;
            mDrawPending = true;
            tobeinvalidated.set((int)(ripplex-animationcircleProgress-4),
                                (int)(rippley-animationcircleProgress-4),
                                (int)(ripplex+animationcircleProgress+4),
                                (int)(rippley+animationcircleProgress+4));
            mDirtyRect.union(tobeinvalidated);
            onBufferDraw();
            invalidate(tobeinvalidated);
        }
        @Override
        public void onAnimationCancel(Animator animation) 
        {
        }
        @Override
        public void onAnimationRepeat(Animator animation) 
        {
        }
    });
    circleAnimator.start();
}
public float                        getAnimationlayerProgress()
{
    return animationcircleProgress;
}
public void                         setAnimationlayerProgress(float animationlayerProgress)
{
    this.animationcircleProgress = animationlayerProgress;

    tobeinvalidated.set((int)(ripplex-animationcircleProgress-4),
                        (int)(rippley-animationcircleProgress-4),
                        (int)(ripplex+animationcircleProgress+4),
                        (int)(rippley+animationcircleProgress+4));

    mDirtyRect.union((int)(ripplex-animationcircleProgress-4),
            (int)(rippley-animationcircleProgress-4),
            (int)(ripplex+animationcircleProgress+4),
            (int)(rippley+animationcircleProgress+4) );

    mDrawPending = true;

    invalidate( (int)(ripplex-animationcircleProgress-4),
                (int)(rippley-animationcircleProgress-4),
                (int)(ripplex+animationcircleProgress+4),
                (int)(rippley+animationcircleProgress+4));
}
import java.util.List;
导入com.nineodeldroids.animation.Animator;
导入com.nineodeldroids.animation.ObjectAnimator;
导入com.nineodeldroids.animation.ValueAnimator;
导入com.nineodeldroids.animation.Animator.AnimatorListener;
导入android.annotation.TargetApi;
导入android.content.Context;
导入android.content.res.TypedArray;
导入android.graphics.Bitmap;
导入android.graphics.Canvas;
导入android.graphics.Paint;
导入android.graphics.Paint.Align;
导入android.graphics.PorterDuff;
导入android.graphics.Rect;
导入android.graphics.Typeface;
导入android.graphics.drawable.BitmapDrawable;
导入android.graphics.drawable.drawable;
导入android.inputmethodservice.Keyboard;
导入android.inputmethodservice.Keyboard.Key;
导入android.os.Build;
导入android.inputmethodservice.KeyboardView;
导入android.util.AttributeSet;
导入android.view.MotionEvent;
导入android.view.animation.AcceleratedCelerateInterpolator;
导入android.graphics.Region.Op;
公共类RippleKeyboardView扩展了KeyboardView
{
私有位图着色位图,着色位图2;
私有静态最终位图.Config Bitmap\u Config=Bitmap.Config.ARGB\u 8888;
私有油漆mPaint=新油漆();
私有int tintcolor=0xffff0000;
私有位图可绘制mDrawable,mDrawable2;
private int invalidatekeyindex=-1;
私有静态final int NOT_A_KEY=-1;
私有浮动动画循环程序;
private Paint circlePaint=新油漆();
私人价值动画师circleAnimator;
私有静态最终int动画\u时间\u ID=android.R.integer.config\u shortAnimTime;
私人浮动ripplex,rippley;
私人浮动文本偏移;
私有int-keytextsize;
私有int-mLabelTextSize;
private Rect mDirtyRect=new Rect();
私有布尔mDrawPending;
私家侦探;
私有布尔值已更改;
私人帆布mCanvas;
专用键盘键盘;
私人名单;
private Rect tobeinvalidated=新Rect();
@抑制警告(“弃用”)
@塔吉塔皮(21)
公共RippleKeyboardView(上下文、属性集属性)
{
超级(上下文,attrs);
if(android.os.Build.VERSION.SDK\u INT>=Build.VERSION\u CODES.M)
{

tintedBitmap=generateConBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym\u keyboard\u delete,null)); tintedBitmap2=generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_键盘\反馈\返回,null)); } 其他的 {
<?xml version="1.0" encoding="UTF-8" ?>
<ripple 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?android:colorControlHighlight">
    <item android:drawable="@drawable/numeric_keypad_states"/>
</ripple>
<?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/numeric_keypad_pressed" />
    <item android:drawable="@drawable/numeric_keypad_normal" />
</selector>
<?xml version="1.0" encoding="UTF-8" ?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/numeric_keypad_pressed_color"/>
        </shape>
    </item>
</layer-list>
import java.util.List;

import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.Animator.AnimatorListener;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.os.Build;
import android.inputmethodservice.KeyboardView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.graphics.Region.Op;

public class RippleKeyboardView extends KeyboardView 
{
private Bitmap                      tintedBitmap,tintedBitmap2;
private static final Bitmap.Config  BITMAP_CONFIG               = Bitmap.Config.ARGB_8888;
private Paint                       mPaint                      = new Paint();
private int                         tintcolor                   = 0xffff0000;
private BitmapDrawable              mDrawable,mDrawable2;
private int                         invalidatekeyindex          = -1;
private static final int            NOT_A_KEY                   = -1;
private float                       animationcircleProgress;
private Paint                       circlePaint                 = new Paint();
private ValueAnimator               circleAnimator;
private static final int            ANIMATION_TIME_ID           = android.R.integer.config_shortAnimTime;
private float                       ripplex,rippley;
private float                       textoffsety;
private int                         keytextsize;
private int                         mLabelTextSize;
private Rect                        mDirtyRect = new Rect();
private boolean                     mDrawPending;
private Bitmap                      mBuffer;
private boolean                     mKeyboardChanged;
private Canvas                      mCanvas;
private Keyboard                    mKeyboard;
private List<Key>                   mkeys;
private Rect                        tobeinvalidated=new Rect();

@SuppressWarnings("deprecation")
@TargetApi(21)
public                              RippleKeyboardView(Context context, AttributeSet attrs) 
{
    super(context, attrs);
    if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    {
        tintedBitmap                    = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_delete,null)));
        tintedBitmap2                   = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_feedback_return,null)));
    }
    else
    {
        tintedBitmap                    = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_delete)));
        tintedBitmap2                   = generateIconBitmaps(getBitmapFromDrawable(getResources().getDrawable(R.drawable.sym_keyboard_feedback_return)));
    }
    mDrawable                       = new BitmapDrawable(getResources(), tintedBitmap);
    mDrawable2                      = new BitmapDrawable(getResources(), tintedBitmap2);

    TypedArray a                    = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RippleKeyboardView, 0, 0);
    keytextsize                     = a.getDimensionPixelSize(R.styleable.RippleKeyboardView_keytxtsize,60);
    mLabelTextSize                  = a.getDimensionPixelSize(R.styleable.RippleKeyboardView_lblsize, 40);
    a.recycle();

    animationcircleProgress         = 0;

    final int pressedAnimationTime  = getResources().getInteger(ANIMATION_TIME_ID);
    circleAnimator                  = ObjectAnimator.ofFloat(this, "animationlayerProgress", 100, 0f);
    circleAnimator.setDuration(pressedAnimationTime);
    circleAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

    mPaint.setColor(0xff000000);
    mPaint.setAntiAlias(true);

    mPaint.setTextAlign(Align.CENTER);
    mPaint.setAlpha(255);
    circlePaint.setColor(0x77989898);

    super.setPreviewEnabled(false);

    invalidateAllKeys();
}
private Bitmap                      getBitmapFromDrawable(Drawable drawable) 
{
    if (drawable == null) 
        return null;

    if (drawable instanceof BitmapDrawable) 
        return ((BitmapDrawable) drawable).getBitmap();

    try 
    {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
        Canvas canvas = new Canvas(bitmap);

        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    } 
    catch (OutOfMemoryError e) 
    {
        return null;
    }
}
private Bitmap                      generateIconBitmaps(Bitmap origin)
{
    if (origin == null) 
        return null;
    Bitmap bmp      = origin.copy(Bitmap.Config.ARGB_8888, true);
    Canvas canvas   = new Canvas(bmp);
    canvas.drawColor(tintcolor & 0x00ffffff | 0xff000000 , PorterDuff.Mode.SRC_IN);
    origin.recycle();
    return bmp;
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) 
{
    super.onSizeChanged(w, h, oldw, oldh);
    mBuffer = null;
}
@Override
public void setKeyboard(Keyboard keyboard) 
{
    super.setKeyboard(keyboard);
    mKeyboardChanged = true;
    mKeyboard = keyboard;
    invalidateAllKeys();
}
@Override
public void onDraw(Canvas canvas)
{
    if (mDrawPending || mBuffer == null || mKeyboardChanged) 
        onBufferDraw();
    canvas.drawBitmap(mBuffer, 0, 0, null);
}
public void                         onBufferDraw() 
{
    if (mBuffer == null || mKeyboardChanged) 
    {
        if (mBuffer == null || mKeyboardChanged && (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) 
        {
            final int width = Math.max(1, getWidth());
            final int height = Math.max(1, getHeight());
            mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBuffer);
            mkeys = getKeyboard().getKeys();
        }
        invalidateAllKeys();
        mKeyboardChanged = false;
    }
    final Canvas canvas = mCanvas;
    canvas.clipRect(mDirtyRect, Op.REPLACE);
    canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
    if (mKeyboard == null) 
        return;
    List<android.inputmethodservice.Keyboard.Key> keys = mkeys;
    for (android.inputmethodservice.Keyboard.Key key : keys) 
    {
        canvas.translate(key.x , key.y );
        if (key.codes[0] == 67) 
        {
            final int drawableX = (key.width - 0 - key.icon.getIntrinsicWidth()) / 2 + 0;
            final int drawableY = (key.height - 0 - key.icon.getIntrinsicHeight()) / 2 + 0;
            canvas.translate(drawableX, drawableY);
            mDrawable.setBounds(0, 0, key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
            mDrawable.draw(canvas);
            canvas.translate(-drawableX, -drawableY);
        }
        else if (key.codes[0] == 66) 
        {
            final int drawableX = (key.width - 0 - key.icon.getIntrinsicWidth()) / 2 + 0;
            final int drawableY = (key.height - 0 - key.icon.getIntrinsicHeight()) / 2 + 0;
            canvas.translate(drawableX, drawableY);
            mDrawable2.setBounds(0, 0, key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
            mDrawable2.draw(canvas);
            canvas.translate(-drawableX, -drawableY);
        }
        else
        {
            String label = key.label.toString();
            if (label.length()>1 && key.codes.length < 2)
            {
                mPaint.setTextSize(mLabelTextSize);
                mPaint.setTypeface(Typeface.DEFAULT_BOLD);
            }
            else
            {
                mPaint.setTextSize(keytextsize);
                mPaint.setTypeface(Typeface.DEFAULT);
            }
            textoffsety= (mPaint.getTextSize() - mPaint.descent()) / 2;
            canvas.drawText(label,(key.width ) / 2,(key.height ) / 2+ textoffsety, mPaint);
        }
        canvas.translate(-key.x , -key.y ); 
    }
    if (invalidatekeyindex!=-1)
        canvas.drawCircle(ripplex , rippley, getAnimationlayerProgress(), circlePaint);
    mDrawPending = false;
    mDirtyRect.setEmpty();
}
@Override
public void invalidateAllKeys() 
{
    mDirtyRect.union(0, 0, getWidth(), getHeight());
    mDrawPending = true;
}
@Override
public void invalidateKey(int keyIndex) 
{
    List<android.inputmethodservice.Keyboard.Key> mKeys = mkeys;
    if (mKeys == null) return;
    if (keyIndex < 0 || keyIndex >= mKeys.size()) 
        return;
    final Key key = mKeys.get(keyIndex);

    mDirtyRect.union(key.x , key.y , key.x + key.width , key.y + key.height );
    invalidate(key.x , key.y , key.x + key.width , key.y + key.height );
}
@Override
public boolean                      performClick() 
{
    super.performClick();
    return true;
}
@Override
public boolean                      onTouchEvent(MotionEvent me) 
{
    boolean ret = super.onTouchEvent(me);
    final int action = me.getAction();
    int primaryIndex = NOT_A_KEY;
    if (action == MotionEvent.ACTION_UP)
    {
        int [] nearestKeyIndices=getKeyboard().getNearestKeys((int)me.getX(),(int) me.getY());
        final int keyCount = nearestKeyIndices.length;
        List<android.inputmethodservice.Keyboard.Key> keys = mkeys;
        for (int i = 0; i < keyCount; i++) 
        {
            final android.inputmethodservice.Keyboard.Key key = keys.get(nearestKeyIndices[i]);
            boolean isInside = key.isInside((int)me.getX(),(int)me.getY());
            if (isInside) 
                primaryIndex = nearestKeyIndices[i];
        }
    }
    if (primaryIndex!=NOT_A_KEY)
    {
        DrawCustomRipple(primaryIndex);
    }
    performClick();
    return ret;
}
private void                        DrawCustomRipple(int keyindex)
{
    if (circleAnimator.isRunning())
    {
        tobeinvalidated.set((int)(ripplex-animationcircleProgress-4),
                            (int)(rippley-animationcircleProgress-4),
                            (int)(ripplex+animationcircleProgress+4),
                            (int)(rippley+animationcircleProgress+4));
        circleAnimator.removeAllListeners();
        invalidatekeyindex = -1;
        mDrawPending = true;
        invalidatekeyindex = -1;
        mDirtyRect.union(tobeinvalidated);
        invalidate(tobeinvalidated);
    }
    invalidatekeyindex=keyindex;
    List<android.inputmethodservice.Keyboard.Key> keys = mkeys;
    final android.inputmethodservice.Keyboard.Key cKey=keys.get(invalidatekeyindex);
    if (cKey.label == null && cKey.codes[0] != 67 && cKey.codes[0] != 66)
        return;

    circleAnimator.setFloatValues(30.0f,100.0f);
    circleAnimator.addListener(new AnimatorListener() 
    {
        @Override
        public void onAnimationStart(Animator animation) 
        {
            final Rect bounds=new Rect();
            if (cKey.label!=null)
            {
                String label = cKey.label.toString();
                mPaint.getTextBounds(label, 0, 1, bounds);
            }
            ripplex =cKey.x+(cKey.width ) / 2+bounds.width()/2;
            rippley = cKey.y + (cKey.height ) / 2;
        }
        @Override
        public void onAnimationEnd(Animator animation) 
        {
            invalidatekeyindex = -1;
            mDrawPending = true;
            tobeinvalidated.set((int)(ripplex-animationcircleProgress-4),
                                (int)(rippley-animationcircleProgress-4),
                                (int)(ripplex+animationcircleProgress+4),
                                (int)(rippley+animationcircleProgress+4));
            mDirtyRect.union(tobeinvalidated);
            onBufferDraw();
            invalidate(tobeinvalidated);
        }
        @Override
        public void onAnimationCancel(Animator animation) 
        {
        }
        @Override
        public void onAnimationRepeat(Animator animation) 
        {
        }
    });
    circleAnimator.start();
}
public float                        getAnimationlayerProgress()
{
    return animationcircleProgress;
}
public void                         setAnimationlayerProgress(float animationlayerProgress)
{
    this.animationcircleProgress = animationlayerProgress;

    tobeinvalidated.set((int)(ripplex-animationcircleProgress-4),
                        (int)(rippley-animationcircleProgress-4),
                        (int)(ripplex+animationcircleProgress+4),
                        (int)(rippley+animationcircleProgress+4));

    mDirtyRect.union((int)(ripplex-animationcircleProgress-4),
            (int)(rippley-animationcircleProgress-4),
            (int)(ripplex+animationcircleProgress+4),
            (int)(rippley+animationcircleProgress+4) );

    mDrawPending = true;

    invalidate( (int)(ripplex-animationcircleProgress-4),
                (int)(rippley-animationcircleProgress-4),
                (int)(ripplex+animationcircleProgress+4),
                (int)(rippley+animationcircleProgress+4));
}
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources >
<resources>
    <declare-styleable name="RippleKeyboardView">
        <attr name="keytxtsize" format="dimension" />   
        <attr name="lblsize"    format="dimension" />   
    </declare-styleable>
</resources>