Search code examples
androidandroid-canvasandroid-viewandroid-custom-view

drawTextOnPath in Path is displaying text from wrong coordinate or starting from right side


I am trying to draw a custom view with image and text, everything is working great, except that my text should start from left to right but it is starting from right to left with upside down.

I had used paths, rect for this drawing

My XML input is as follows,

with xml input as follows,
 <re.ui.customView.SideMenu
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:icon="@drawable/ic_cart"
            app:padding="15dp"
            app:text_size="25dp"
            app:text="Vikram Singh works with a1 Technology in  mohali"
            app:text_color="@color/wAuth_headerBottomColor"
            app:icon_background="@color/solid_order"
    />

This is my custom view class,

import android.content.Context
import android.graphics.*
import android.text.TextPaint
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import re.R
import re.util.getBitmapFromDrawable
import re.util.withStyledAttributes

class SideMenu(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
View(context, attrs, defStyleAttr) {

private lateinit var mName: String
private lateinit var mBitmap: Bitmap

private var mSizeName: Int = 0
private var mColorName: Int = 0

private var mPadding: Int = 0
private var mColorIcon: Int = 0

private lateinit var mIconPaint: Paint
private lateinit var mRectPaint: Paint
private lateinit var mTextPaint: TextPaint

private lateinit var mRect: Rect
private lateinit var mPath: Path

constructor(context: Context) : this(context, null, 0)

constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)

init {
    context.withStyledAttributes(attrs, R.styleable.SideMenu) {
        mName = getString(R.styleable.SideMenu_text)!!

        mSizeName = getDimensionPixelSize(R.styleable.SideMenu_text_size, 0)
        mPadding = getDimensionPixelSize(R.styleable.SideMenu_padding, 0)

        mColorName = getColor(R.styleable.SideMenu_text_color, 0)
        mColorIcon = getColor(R.styleable.SideMenu_icon_background, 0)

        val drawableResId = getResourceId(R.styleable.SideMenu_icon, -1);
        mBitmap = context.getBitmapFromDrawable(context, drawableResId)
    }

    initAttributes()
}

private fun initAttributes() {

    mIconPaint = Paint()
    with(mIconPaint)
    {
        isAntiAlias = true
        color = mColorIcon
        style = Paint.Style.FILL
    }

    mRectPaint = Paint()
    with(mRectPaint)
    {
        isAntiAlias = true
        color = mColorName
        style = Paint.Style.FILL
    }

    mTextPaint = TextPaint()
    with(mTextPaint)
    {
        isAntiAlias = true
        textSize = mSizeName.toFloat()
        color = Color.BLACK
        textAlign = Paint.Align.CENTER
    }

    mRect = Rect()
    mPath = Path()
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)

    val heightBitmap = mBitmap.height

    val heightNew = heightBitmap + mPadding * 2 + paddingTop + paddingBottom
    setMeasuredDimension(widthMeasureSpec, heightNew)
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    var xLeftCoorinatePath = (paddingLeft).toFloat()
    var yTopCoorinatePath = (paddingTop).toFloat()

    var xRightCoorinatePath = (xLeftCoorinatePath + mPadding*2 + mBitmap.width).toFloat()
    var yBottomCoorinatePath = (height - paddingBottom).toFloat()

    mPath.moveTo(xLeftCoorinatePath, yTopCoorinatePath)
    mPath.lineTo(xRightCoorinatePath, yTopCoorinatePath)
    mPath.lineTo(xRightCoorinatePath, yBottomCoorinatePath)
    mPath.lineTo(xLeftCoorinatePath, yBottomCoorinatePath)
    mPath.lineTo(xLeftCoorinatePath, yTopCoorinatePath)

    mPath.close()
    canvas.drawPath(mPath, mIconPaint)
    mPath.reset()

    val xLeftCoorinateImage = (paddingLeft + mPadding).toFloat()
    val yTopCoorinateImage = (paddingTop + mPadding).toFloat()

    canvas.drawBitmap(mBitmap, xLeftCoorinateImage, yTopCoorinateImage, mIconPaint)

    xLeftCoorinatePath = xRightCoorinatePath
    xRightCoorinatePath = (width - paddingRight).toFloat()

    mPath.moveTo(xLeftCoorinatePath, yTopCoorinatePath)
    mPath.lineTo(xRightCoorinatePath, yTopCoorinatePath)
    mPath.lineTo(xRightCoorinatePath, yBottomCoorinatePath)
    mPath.lineTo(xLeftCoorinatePath, yBottomCoorinatePath)
    mPath.lineTo(xLeftCoorinatePath, yTopCoorinatePath)
    mPath.close()
    canvas.drawPath(mPath, mRectPaint)

    mRect.left = xLeftCoorinatePath.toInt()
    mRect.right = xRightCoorinatePath.toInt()
    mRect.top = yTopCoorinatePath.toInt()
    mRect.bottom = yBottomCoorinatePath.toInt()

    val txt = TextUtils.ellipsize(mName, mTextPaint, mRect.width().toFloat(), TextUtils.TruncateAt.END)
    val charArray: CharArray = txt.toString().toCharArray()

    val yCenter = ((height - (mTextPaint.descent() + mTextPaint.ascent())) / 2)
    canvas.drawTextOnPath(charArray, 0, charArray.size, mPath, 0f , yCenter, mTextPaint)
}
}

My original output is as following, Original Output

And my current output from above file is as follows, My output


Solution

  • The problem is with your mTextPaint alignment,

    with(mTextPaint) { isAntiAlias = true textSize = mSizeName.toFloat() color = Color.BLACK textAlign = Paint.Align.LEFT }