问题描述
它在点击时就像一个按钮。我希望涟漪效应仅覆盖视图的圆角矩形部分,但我正在努力实现这一目标。下面是涟漪现在的样子:
注意波纹是如何一直延伸到顶部,而不是停在圆角矩形的边缘。这是我第一次制作自定义视图,我完全迷失了。
public class HeaderButton extends androidx.appcompat.widget.AppCompatTextView {
Paint paint;
float scaledUnit;
RectF rectBounds;
String text = "My text";
public HeaderButton(Context context) {
super(context);
init();
}
public HeaderButton(Context context,@Nullable AttributeSet attrs) {
super(context,attrs);
init();
}
public HeaderButton(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {
super(context,attrs,defStyleAttr);
init();
}
@Override
protected void onSizeChanged(int w,int h,int oldw,int oldh) {
super.onSizeChanged(w,h,oldw,oldh);
float screenWidthPixel = this.getResources().getdisplayMetrics().widthPixels;
scaledUnit = screenWidthPixel * 0.001f;
rectBounds = new RectF(3 * scaledUnit,15 * scaledUnit,getWidth() - (3 * scaledUnit),getHeight() - (3 * scaledUnit));
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
float[] outerRadii = new float[8];
Arrays.fill(outerRadii,7 * scaledUnit);
RoundRectShape shape = new RoundRectShape(outerRadii,null,null);
ShapeDrawable mask = new ShapeDrawable(shape);
ColorStateList stateList = ColorStateList.valueOf(ContextCompat.getColor(getContext(),R.color.colorError));
setBackgroundDrawable(new rippledrawable(stateList,mask));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float textWidth = paint.measureText(text);
float textStart = Math.round(getWidth() / 2.) - (textWidth / 2);
float textEnd = Math.round(getWidth() / 2.) + (textWidth / 2);
paint.setStyle(Paint.Style.FILL);
int scaledTextSize = getResources().getDimensionPixelSize(R.dimen.header_button_label_text_size);
paint.setTextSize(scaledTextSize);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#777777"));
paint.setTypeface(Typeface.create(Typeface.DEFAULT,Typeface.BOLD));
canvas.drawText(text,Math.round(getWidth() / 2.) - (textWidth / 2),24 * scaledUnit,paint);
// Removes the portion of the rounded rectangle that's behind the text.
canvas.clipRect(textStart - (10 * scaledUnit),textEnd + (10 * scaledUnit),20,Region.Op.DIFFERENCE);
paint.setColor(getResources().getColor(R.color.colorMainActivityBottomButtonBorderBackground));
paint.setStyle(Paint.Style.stroke);
paint.setstrokeWidth(3 * scaledUnit);
canvas.drawRoundRect(rectBounds,7 * scaledUnit,paint);
}
}
解决方法
尝试将您的 shape
包装到 LayerList
- 在那里,至少在 XML 中,每个 <item
标记可能包含 padding
值
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="8dp"
android:right="8dp"
android:top="8dp"
android:bottom="8dp">
<shape
...
</item>
<item ...
</layer-list>
没有在下面尝试过,但在编程上它可能看起来与此类似
ShapeDrawable mask = new ShapeDrawable(shape);
Drawable[] layers = {mask};
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.setPadding(0,someValue,0);
ColorStateList stateList = ColorStateList.valueOf(ContextCompat.getColor(getContext(),R.color.colorError));
setBackgroundDrawable(new RippleDrawable(stateList,null,layerDrawable));
,
似乎试图以编程方式实现涟漪是一个死胡同。但是我找到了一个使用 XML 的解决方案:
button_ripple.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorControlHighlight">
<item
android:id="@android:id/mask"
android:bottom="3dp"
android:left="3dp"
android:right="3dp"
android:top="8dp">
<shape>
<solid android:color="@color/colorBackground"/>
<corners android:radius="3dp"/>
</shape>
</item>
</ripple>
layout.xml:
...
<myPackage.HeaderButton
android:id="@+id/headerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter phone number"
android:textSize="17dp"
android:gravity="center"
android:background="@drawable/button_ripple"/>
...
HeaderButton.java:
public class HeaderButton extends androidx.appcompat.widget.AppCompatTextView {
Paint paint;
RectF rectBounds;
String text = "My text";
public HeaderButton(Context context) {
super(context);
init();
}
public HeaderButton(Context context,@Nullable AttributeSet attrs) {
super(context,attrs);
init();
}
public HeaderButton(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {
super(context,attrs,defStyleAttr);
init();
}
@Override
protected void onSizeChanged(int w,int h,int oldw,int oldh) {
super.onSizeChanged(w,h,oldw,oldh);
// These measurements must exactly match the paddings you set in button_ripple.xml
rectBounds = new RectF(dpToPx(3,getContext()),dpToPx(8,getWidth() - (dpToPx(3,getContext())),getHeight() - (dpToPx(3,getContext())));
isLayoutRequested();
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float textWidth = paint.measureText(text);
float textStart = Math.round(getWidth() / 2.) - (textWidth / 2);
float textEnd = Math.round(getWidth() / 2.) + (textWidth / 2);
paint.setStyle(Paint.Style.FILL);
int scaledTextSize = getResources().getDimensionPixelSize(R.dimen.header_button_label_text_size);
paint.setTextSize(scaledTextSize);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#777777"));
paint.setTypeface(Typeface.create(Typeface.DEFAULT,Typeface.BOLD));
canvas.drawText(text,Math.round(getWidth() / 2.) - (textWidth / 2),dpToPx(12,paint);
// Removes the portion of the rounded rectangle that's behind the text.
canvas.clipRect(textStart - (dpToPx(7,textEnd + (dpToPx(7,getHeight() / 2f,Region.Op.DIFFERENCE);
paint.setColor(getResources().getColor(R.color.colorMainActivityBottomButtonBorderBackground));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(dpToPx(1.5f,getContext()));
canvas.drawRoundRect(rectBounds,dpToPx(3,paint);
}
}
我最初尝试过这种方法并拒绝了它,因为它没有在所有设备上提供一致的结果。但后来我意识到我的 dpToPx()
方法与 Android 内部使用的方法不同。这很重要,因为您在 button_ripple.xml 和 HeaderButton.java 中设置的填充值必须完美匹配。所以请确保您使用的是这个:
//Converts a dp measurement to a px measurement.
public static int dpToPx(float dp,Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,context.getResources().getDisplayMetrics());
}