Эффект ряби на фоне формы на кнопке

1

[Обратите внимание, что я использую Xamarin.Droid который является кроссплатформенным фреймворком, использующим Mono.NET С#, код действительно близок к Android Java, и я принимаю ответы Java, так как это легко перевести на С# ]

Я leftDrawable подкласс Button, и я применяю shape с цветом и leftDrawable. Все работает нормально, за исключением того факта, что я потерял эффект Ripple при нажатии кнопки.

Я попытался добавить ?attr/selectableItemBackground но с кодом, так как у меня нет XML для этого подкласса, и волновой эффект все еще не появляется.

Здесь мой подкласс кнопки

public class LTButton : Android.Support.V7.Widget.AppCompatButton
    {
        Context context;

        public LTButton(Context pContext, IAttributeSet attrs) : base(pContext, attrs)
        {
            context = pContext;
            Elevation = 0;
        }

        protected override void OnDraw(Canvas canvas)
        {
            base.OnDraw(canvas);

            Init();
        }

        void Init()
        {
            // Adding a left drawable
            Drawable img = ContextCompat.GetDrawable(context, Resource.Drawable.chevron_right);
            img.SetBounds(0, 0, 70, 70);
            SetCompoundDrawables(img, null, null, null);
            TextSize = 16;

            // The shape with background color and rounded corners
            var shape = new GradientDrawable();
            // This is a full rounded button
            shape.SetCornerRadius(Height / 2);
            shape.SetColor(ContextCompat.GetColor(context, Resource.Color.LTGreen));

            Background = shape;

            // Foreground => To have a ripple effect
            int[] attrs = { Android.Resource.Attribute.SelectableItemBackground };

            TypedArray ta = context.ObtainStyledAttributes(attrs);
            Drawable selectableDrawable = ta.GetDrawable(0);

            ta.Recycle();

            Foreground = selectableDrawable;
        }
    }
Теги:
xamarin
xamarin.android
button

3 ответа

1
Лучший ответ

Создайте XML файл для рисования пульсаций примерно так:

my_button_background_v21.xml

<?xml version="1.0" encoding="UTF-8" ?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="@color/black"
tools:targetApi="lollipop">

<item>
    <shape android:shape="rectangle">
        <corners android:radius="100dp" /> 
        <solid android:color="@color/LTGreen" />
    </shape>
</item>

<item android:id="@android:id/mask">
    <shape android:shape="rectangle">
        <corners android:radius="100dp" /> 
        <solid android:color="@color/black" />
    </shape>
</item>

Затем в свой метод кнопки onDraw добавьте эффект ряби примерно так:

 protected override void OnDraw(Canvas canvas)
{
    base.OnDraw(canvas);

    Drawable img = ContextCompat.GetDrawable(context, Resource.Drawable.chevron_right);
    img.SetBounds(0, 0, 70, 70);
    SetCompoundDrawables(img, null, null, null);
    TextSize = 16;

    if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
    {
        SetBackgroundResource(Resource.Layout.my_button_background);
    }
    else
    {
        SetBackgroundResource(Resource.Layout.my_button_background_v21);
    }
}
0

Использовать Java вы можете сделать так:

Пользовательские RippleButton

public class RippleButton extends Button {


    private RippleDrawable rippleDrawable;
    private Paint paint;


    public RippleButton(Context context) {
        this(context,null);
    }

    public RippleButton(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public RippleButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        rippleDrawable = new RippleDrawable();
        //Set refresh interface, View has been implemented ---> source button inheritance child drawable.callback
        rippleDrawable.setCallback(this);

        //rippleDrawable
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //Set the refresh area ---> source code
        rippleDrawable.setBounds(0,0,getWidth(),getHeight());
    }

    @Override
    protected boolean verifyDrawable(Drawable who) {
        return who == rippleDrawable || super.verifyDrawable(who);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        rippleDrawable.onTouch(event);
        return true;
    }
}

Пользовательские RippleDrawable:

public class RippleDrawable extends Drawable {

private Paint mPaint;
private Bitmap bitmap;
private int rippleColor;
private float mRipplePointX = 0;
private float mRipplePointY = 0;
private float mRippleRadius = 0;
private int mAlpha = 200;

private float mCenterPointX,mCenterPointY;
private float mClickPointX;
private float mClickPointY;
//Maximum radius
private float MaxRadius;
//Starting radius
private float startRadius;
//End radius
private float endRadius;

//Record whether to raise your finger--->boolean
private boolean mUpDone;
//Record whether the animation is finished
private boolean mEnterDone;


/**Enter animation*/
//Enter the progress value of the animation
private float mProgress;
//Each incremental time
private float mEnterIncrement = 16f/360;
//Enter the animation add interpolator
private DecelerateInterpolator mEnterInterpolator = new DecelerateInterpolator(2);
private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            mEnterDone = false;
            mCircleAlpha = 255;
            mProgress = mProgress + mEnterIncrement;
            if (mProgress > 1){
                onEnterPrograss(1);
                enterDone();
                return;
            }

            float interpolation = mEnterInterpolator.getInterpolation(mProgress);
            onEnterPrograss(interpolation);
            scheduleSelf(this, SystemClock.uptimeMillis() + 16);
        }


};

/**How to enter the animation refresh
 * @parms realProgress */
public void onEnterPrograss(float realPrograss){
    mRippleRadius = getCenter(startRadius,endRadius,realPrograss);
    mRipplePointX = getCenter(mClickPointX,mCenterPointX,realPrograss);
    mRipplePointY = getCenter(mClickPointY,mCenterPointY,realPrograss);
    mBgAlpha = (int) getCenter(0,182,realPrograss);


    invalidateSelf();
}

private void enterDone() {
    mEnterDone = true;
    if(mUpDone)
        startExitRunnable();
}


/**Exit animation*/
//Exit the progress value of the animation
private float mExitProgress;
//Each incremental time
private float mExitIncrement = 16f/280;
//Exit animation add interpolator
private AccelerateInterpolator mExitInterpolator = new AccelerateInterpolator(2);
private Runnable exitRunnable = new Runnable() {
    @Override
    public void run() {
        if (!mEnterDone){
            return;
        }

        mExitProgress = mExitProgress + mExitIncrement;
        if (mExitProgress > 1){
            onExitPrograss(1);
            exitDone();
            return;
        }

        float interpolation = mExitInterpolator.getInterpolation(mExitProgress);
        onExitPrograss(interpolation);
        scheduleSelf(this, SystemClock.uptimeMillis() + 16);
    }
};


/**Exit animation refresh method
 * @parms realProgress */
public void onExitPrograss(float realPrograss){
    //Set background
    mBgAlpha = (int) getCenter(182,0,realPrograss);
    //Set the circular area
    mCircleAlpha = (int) getCenter(255,0,realPrograss);

    invalidateSelf();
}

private void exitDone() {
    mEnterDone = false;
}

//Set the gradient effect including radius / bg color / center position, etc.
public float getCenter(float start,float end,float prograss){
    return start + (end - start)*prograss;
}





public RippleDrawable() {

    //Anti-aliased brush
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    //Set anti-aliasing
    mPaint.setAntiAlias(true);
    //Set anti-shake
    mPaint.setDither(true);

    setRippleColor(0x60000000);
}

//private int mPaintAlpha  = 255;
//Background transparency
private int mBgAlpha;
//Transparency of the circular area
private int mCircleAlpha;
@Override
public void draw(Canvas canvas) {
    //Get the transparency of user settings
    int preAlpha = mPaint.getAlpha();
    //current background
    int bgAlpha = (int) (preAlpha * (mBgAlpha/255f));
    //bg + prebg Operational background
    int maxCircleAlpha = getCircleAlpha(preAlpha,bgAlpha);
    int circleAlpha = (int) (maxCircleAlpha * (mCircleAlpha/255f));

    mPaint.setAlpha(bgAlpha);
    canvas.drawColor(mPaint.getColor());

    mPaint.setAlpha(circleAlpha);
    canvas.drawCircle(mRipplePointX,mRipplePointY,mRippleRadius,mPaint);

    //Set the initial transparency to ensure that the next entry into the operation will not go wrong
    mPaint.setAlpha(preAlpha);

}

public int getCircleAlpha(int preAlpha,int bgAlpha){
    int dAlpha = preAlpha - bgAlpha;
    return (int) ((dAlpha*255f)/(255f - bgAlpha));
}



public void onTouch(MotionEvent event){
    switch (event.getActionMasked()){
        case MotionEvent.ACTION_DOWN:
            //press
            mClickPointX = event.getX();
            mClickPointY = event.getY();
            onTouchDown(mClickPointX, mClickPointY);


            break;
        case MotionEvent.ACTION_MOVE:
            //move

            //onTouchMove(moveX,moveY);


            break;
        case MotionEvent.ACTION_UP:
            //up

            onTouchUp();


            break;
        case MotionEvent.ACTION_CANCEL:
            //exit
            //onTouchCancel();

            break;

    }

}

public void onTouchDown(float x,float y){
    //Log.i("onTouchDown====",x + "" + y );
    //unscheduleSelf(runnable);
    mUpDone = false;
    mRipplePointX = x;
    mRipplePointY = y;
    mRippleRadius = 0;
    startEnterRunnable();

}

public void onTouchMove(float x,float y){

}

public void onTouchUp(){
    mUpDone = true;
    if (mEnterDone){
        startExitRunnable();
    }

}

public void onTouchCancel(){
    mUpDone = true;
    if (mEnterDone){
        startExitRunnable();
    }

}

/**
 * Open to enter animation
 * */
public void startEnterRunnable(){


    mProgress = 0;
    //mEnterDone = false;
    unscheduleSelf(exitRunnable);
    unscheduleSelf(runnable);
    scheduleSelf(runnable,SystemClock.uptimeMillis());

}

/**
 * Open exit animation
 * */
public void startExitRunnable(){
    mExitProgress = 0;
    unscheduleSelf(runnable);
    unscheduleSelf(exitRunnable);
    scheduleSelf(exitRunnable,SystemClock.uptimeMillis());

}

public int changeColorAlpha(int color,int alpha){
    //Set transparency
    int a = (color >> 24) & 0xFF;
    a = a * alpha/255;

    int red = Color.red(color);
    int green = Color.green(color);
    int blue = Color.blue(color);
    int argb = Color.argb(a, red, green, blue);
    return argb;
}



//Take the center point of the drawn area
@Override
protected void onBoundsChange(Rect bounds) {
    super.onBoundsChange(bounds);
    mCenterPointX  = bounds.centerX();
    mCenterPointY = bounds.centerY();

    MaxRadius = Math.max(mCenterPointX,mCenterPointY);
    startRadius = MaxRadius * 0.1f;
    endRadius = MaxRadius * 0.8f;

}

@Override
public void setAlpha(int alpha) {
    mAlpha = alpha;
    onColorOrAlphaChange();

}

@Override
public int getAlpha() {
    return mAlpha;
}

@Override
public void setColorFilter(ColorFilter colorFilter) {
    //Filter effect
    if (mPaint.getColorFilter() != colorFilter){
        mPaint.setColorFilter(colorFilter);
        invalidateSelf();
    }
}

@Override
public int getOpacity() {
    //Return transparency
    if (mAlpha == 255){
        return PixelFormat.OPAQUE;
    }else if (mAlpha == 0){
        return PixelFormat.TRANSPARENT;
    }else{
        return PixelFormat.TRANSLUCENT;
    }

}

public void setRippleColor(int rippleColor) {
    this.rippleColor = rippleColor;
    onColorOrAlphaChange();
}

private void onColorOrAlphaChange() {
    //Set brush color
    mPaint.setColor(rippleColor);
    if (mAlpha != 255){
        int pAlpha = mPaint.getAlpha();
        int realAlpha = (int) (pAlpha * (mAlpha/255f));
        //Set transparency
        mPaint.setAlpha(realAlpha);
    }
    invalidateSelf();

}


}

Если будет полезно, пожалуйста, пометьте ответ, чтобы помочь другим.

0

Благодаря @G.Hakim и этой теме я смог воспроизвести свой фон и добавить эффект ряби только с помощью XML.

my_button_background.xml

<?xml version="1.0" encoding="UTF-8" ?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:color="@color/black"
    tools:targetApi="lollipop">

    <item>
        <shape android:shape="rectangle">
            <corners android:radius="100dp" /> 
            <solid android:color="@color/LTGreen" />
        </shape>
    </item>

    <item android:id="@android:id/mask">
        <shape android:shape="rectangle">
            <corners android:radius="100dp" /> 
            <solid android:color="@color/black" />
        </shape>
    </item>
</ripple>

MyButton.cs

    protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);

        Drawable img = ContextCompat.GetDrawable(context, Resource.Drawable.chevron_right);
        img.SetBounds(0, 0, 70, 70);
        SetCompoundDrawables(img, null, null, null);
        TextSize = 16;

        if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
        {
            SetBackgroundResource(Resource.Layout.my_button_background);
        }
        else
        {
      SetBackgroundResource(Resource.Layout.my_button_background_v21);
        }
    }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню