自定义View中文本居中显示

在开发中遇到一个这样的需求,在选择星期时设计狮想要这个这样的效果:




看到效果,想当然应该是用CheckBox实现吧,选中时背景蓝色文字白色,未选中时背景透明文字黑色。
那最方便的实现方式应该是直接给切图,然而这种用切图,除了因为要做国际化图片量会很大外,另一个原因就是显示效果也不是非常好。

另外一种就给CheckBox定义样式,实现思路应该是下面这个样子(样式代码见底部):
1,设置背景drawable,用 selector.xml来控制它的选中状态
2,文字用/color文件夹下的drawable来实现,设置文字的选择状态的颜色

-----------------------------------------------------------------------------------------------------------------------------------

以上比较瞎扯,最终我是选择用自定义View继承CheckBox实现的,然而遇到的效果是这的:


“一”怎么了,为什么是在上面显示并没有对齐,而且旁边的“日”也没有居中显示。关键代码实现如下:

        if (isChecked()) {
            // 绘制背景圆
            canvas.drawCircle(centerX, centerY, centerX-mPadding, mBgPaint);
            // 设置文字画笔颜色
            mFontPaint.setColor(mFontCheckColor);
            // 获取文字TEXT,以及文字的Rect,来得到文字的高度和宽度
            String text = getText().toString();
            Rect fontRect = new Rect();
            mFontPaint.getTextBounds(text, 0, text.length(), fontRect);
            // 设置文字左下角的起始点坐标,绘制文字
            canvas.drawText(text, centerX-fontRect.width()/2, centerY+fontRect.height()/2, mFontPaint);
        }


由此猜测应该是 getTextBounds()获取文字的RectA与实际的drawText时文字的RectB不是一样的,先看看RectA的大小,
给出三个String,“日”,“一”,“二一”,如下图:

得到如下信息:
1,“日”的Baseline与RectA.bottom基准线不一致, drawText()方法要设置的Y轴坐标是绘制文本Baseline的坐标,并不是文字最底部的坐标。


2,中间的“一”文本之所以没有显示,就可以在第三个文本“二一”中找到:
从“二一”中可以看出,“一”的底部距离Baseline有比较大的偏差,所以中间的会显示为空白。也就明白了最上面“一”显示偏上的问题。
绘制getTextBounds()获取Text的Rect的代码如下:

    @Override
    protected void onDraw(Canvas canvas) {
        // 设置文字画笔颜色
        mFontPaint.setColor(mFontCheckColor);
        // 获取文字TEXT,以及文字的Rect,来得到文字的高度和宽度
        String text = getText().toString();
        Rect fontRect = new Rect();
        mFontPaint.getTextBounds(text, 0, text.length(), fontRect);


        fontRect.offset(0, -fontRect.top);
        canvas.drawText(text, 0, fontRect.bottom, mFontPaint);

        Paint linePaint = new Paint();
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setColor(Color.RED);
        linePaint.setStrokeWidth(2);
        canvas.drawRect(fontRect, linePaint);
    }


参考: http://stackoverflow.com/questions/7549182/android-paint-measuretext-vs-gettextbounds

-----------------------------------------------------------------------------------------------------------------------------------
要想让文本居中显示,使用 getFontMetrics()方法获取要绘制文字的高度。
1,getFontMetrics(),getFontMetricsInt()

getFontMetrics(),getFontMetricsInt()用于返回字符串的测量,而两个方法的区别就是返回值得类型。返回值一共有五个属性:

  • Top : baseline到文本顶部的最大的距离
  • Ascent : baseline到文本顶部的推荐距离
  • Descent : baseline到文本底部的推荐距离
  • Bottom : baseline到文本底部的最大距离
  • Baseline : 两行文本之间推荐的额外距离,一般为0


在Android的坐标系之中,向下为Y轴正方向,向上位Y轴负方向,所以baseline之上的Top与Ascent都是负数,而baselin之下的Descent、Bottom都是正数。如果要让字符串在垂直方向上居中,则需要在纵坐标上增加Asecent绝对值与descent的差。 

2、setTextAlign

setTextAlign可以设置画笔绘制文本的对齐方式,选择center即可完成文本的水平居中。

mPaint.setTextAlign(Paint.Align.CENTER);

3、多行文本居中,代码如下:

private void textCenter(String[] strings, Paint paint, Canvas canvas, Point point,Paint.Align align){
    mPaint.setTextAlign(align);
    Paint.FontMetrics fontMetrics= mPaint.getFontMetrics();
    float top = fontMetrics.top;
    float bottom = fontMetrics.bottom;
    int length = strings.length;
    float total = (length-1)*(-top+bottom)+(-fontMetrics.ascent+fontMetrics.descent);
    float offset = total/2-bottom;
    for (int i=0; i<length; i++){
        float yAxis = -(length-i-1)*(-top+bottom)+offset;
        canvas.drawText(strings[i],point.x,point.y+yAxis,mPaint);
    }
}

  • 1、drawText每次只能绘制一行,所以我们有个for循环
  • 2、更具上图,假设字符串数组大于1,每个字符串的高度应该为-top+bottom,总高度就为length*(-top+bottom);
  • 3、偏移量就等于length*(-top+bottom)/2-bottom;
  • 4、如果顺序向上(即Y负方向)排列的话,那么第i个字符串的高度就是-(length-i-1)*(-top+bottom);
  • 5、每个字符串高度的减去偏移量,就应该是每个字符串的baseline的Y坐标 



-----------------------------------------------------------------------------------------------------------------------------------

最终实现星期选择效果的代码如下:

public class FontBgCheckBox extends CheckBox {

    private Paint mBgPaint;
    private Paint mFontPaint;

    private int mFontCheckColor, mFontUncheckColor, mBgCheckColor;
    private int mPadding;

    public FontBgCheckBox(Context context, AttributeSet attrs) {
        super(context, attrs);

        mFontCheckColor = 0xFFFFFFFF;
        mFontUncheckColor = Color.parseColor("#FF616161");
        mBgCheckColor = Color.parseColor("#FF50BDE7");
        mPadding = dp2px(6, getContext());

        mBgPaint = new Paint();
        mBgPaint.setColor(mBgCheckColor);
        mBgPaint.setAntiAlias(true);

        mFontPaint = new Paint();
        mFontPaint.setTextSize(dp2px(16, getContext()));
        mFontPaint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        if (isChecked()) {
            canvas.drawCircle(centerX, centerY, centerX - mPadding, mBgPaint);
            mFontPaint.setColor(mFontCheckColor);
            String text = getText().toString();
            textCenter(new String[]{text}, mFontPaint, canvas, centerX, centerY, Align.CENTER);
        } else {
            mFontPaint.setColor(mFontUncheckColor);
            String text = getText().toString();
            textCenter(new String[]{text}, mFontPaint, canvas, centerX, centerY, Align.CENTER);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
    }

    public static int dp2px(float value, Context context) {
        final float scale = context.getResources().getDisplayMetrics().densityDpi;
        return (int) (value * (scale / 160) + 0.5f);
    }
}

xml布局文件引用示例如下:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <com.ionesmile.testalbum.view.FontBgCheckBox
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:checked="true"
            android:text="日" />
        
        ……
        
</LinearLayout>


使用样式的方式完成如上效果的代码如下:

<CheckBox
    android:id="@+id/Sunday"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Sun" 
    style="@style/checkbox_week_style" />

<style name="checkbox_week_style">
	<item name="android:textColor">@drawable/text_color_selector</item>
	<item name="android:button">@null</item>        
	<item name="android:background">@drawable/bg_circle_blue</item>
	<item name="android:gravity">center</item>
	<item name="android:width">45dp</item>
	<item name="android:height">45dp</item>
</style>

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#FFFFFFFF" android:state_checked="true"></item>
    <item android:color="@color/common_gray"></item>
</selector>

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true">
        <shape android:shape="oval">
            <solid android:color="@color/common_theme" />
            <size android:width="45dp" android:height="45dp" />
        </shape>
    </item>
</selector>





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果在uniapp自定义toast时,设置了image属性,但是图片显示居中,可以通过在样式设置background-position属性来调整图片位置。 以下是一个示例代码: ```html <template> <view class="container"> <button @tap="showToast">显示toast</button> </view> </template> <script> export default { methods: { showToast() { uni.showToast({ title: '自定义toast', icon: 'none', duration: 2000, image: '/static/custom-toast.png', mask: true, success: (res) => { console.log('toast显示成功', res) }, fail: (err) => { console.log('toast显示失败', err) }, complete: () => { console.log('toast显示完成') } }) } } } </script> <style> .container { display: flex; justify-content: center; align-items: center; height: 100vh; } /* 自定义toast样式 */ .custom-toast { background-image: url('/static/custom-toast.png'); background-repeat: no-repeat; background-size: 40px 40px; background-position: center 10px; /* 调整图片位置 */ padding: 60px 40px 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); font-size: 16px; color: #fff; text-align: center; } </style> ``` 在以上代码,我们在样式定义了一个.custom-toast类,其设置了background-image、background-repeat、background-size和background-position属性,通过调整background-position属性的值,可以实现图片居中显示。 在uni.showToast()方法,我们指定了一个自定义的类名,即className属性为'custom-toast',这样就可以应用我们自己定义的样式。 需要注意的是,如果自定义的toast样式设置了padding属性,需要根据实际情况调整background-position属性的值,以确保图片居中显示

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值