I was making a simple custom DrawingView class which can draw with small brushes. But When I try to run it gets Nullpointer Exception
this is the stacktrace.
Process: com.mahidev.kidsdrawingapp, PID: 19949
java.lang.NullPointerException
at com.mahidev.kidsdrawingapp.DrawingView.onDraw(DrawingView.kt:64)
at android.view.View.draw(View.java:22635)
at android.view.View.updateDisplayListIfDirty(View.java:21472)
at android.view.View.draw(View.java:22335)
at android.view.ViewGroup.drawChild(ViewGro
this is drawingview - 64th line. I didn't get any clue what is happening?
canvas.drawBitmap(mCanvasBitmap!!, 0f, 0f, mCanvasPaint)
package com.mahidev.kidsdrawingapp
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.util.Size
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
class DrawingView(context: Context, attrs: AttributeSet) : View(context, attrs)
{
private var mDrawPath : CustomPath? = null
private var mCanvasBitmap: Bitmap? = null
private var mDrawPaint: Paint? = null
private var mCanvasPaint: Paint? = null
private var mBrushSize: Float = 0.toFloat()
private var color = Color.BLACK
private var canvas : Canvas? = null
private val mPaths = ArrayList<CustomPath>()
init
{
Log.i("Mahi", "inside init")
setUpDrawing()
}
private fun setUpDrawing()
{
Log.i("Mahi", "inside setUpDrawing")
mDrawPaint = Paint()
mDrawPath = CustomPath(color, mBrushSize)
mDrawPaint!!.color = color
mDrawPaint!!.style = Paint.Style.STROKE
mDrawPaint!!.strokeJoin = Paint.Join.ROUND
mDrawPaint!!.strokeCap = Paint.Cap.ROUND
mCanvasPaint = Paint(Paint.DITHER_FLAG)
// mBrushSize = 20.toFloat()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int)
{
Log.i("Mahi", "inside onSizeChanged")
super.onSizeChanged(w, h, oldw, oldh)
mCanvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
canvas = Canvas(mCanvasBitmap!!)
}
fun setSizeForBrush(newSize: Float)
{
mBrushSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, newSize, resources.displayMetrics)
mDrawPaint!!.strokeWidth = mBrushSize
}
override fun onDraw(canvas: Canvas) {
Log.i("Mahi", "onDraw")
super.onDraw(canvas)
canvas.drawBitmap(mCanvasBitmap!!, 0f, 0f, mCanvasPaint)
for(path in mPaths)
{
mDrawPaint!!.strokeWidth = path!!.brushThickness
mDrawPaint!!.color = mDrawPath!!.color
canvas.drawPath(path, mDrawPaint!!)
}
if(!mDrawPath!!.isEmpty)
{
mDrawPaint!!.strokeWidth = mDrawPath!!.brushThickness
mDrawPaint!!.color = mDrawPath!!.color
canvas.drawPath(mDrawPath!!, mDrawPaint!!)
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean
{
Log.i("Mahi", "inside onTouchEvent")
val touchX = event?.x
val touchY = event?.y
when(event?.action)
{
MotionEvent.ACTION_DOWN ->
{
mDrawPath!!.color = color
mDrawPath!!.brushThickness = mBrushSize
mDrawPath!!.reset()
if (touchX != null) {
if (touchY != null) {
mDrawPath!!.moveTo(touchX, touchY)
}
}
}
MotionEvent.ACTION_MOVE ->
{
if (touchY != null) {
if (touchX != null) {
mDrawPath!!.lineTo(touchX, touchY)
}
}
}
MotionEvent.ACTION_UP ->
{
mPaths.add(mDrawPath!! )
mDrawPath = CustomPath(color, mBrushSize)
}
else -> return false
}
invalidate()
return true
return super.onTouchEvent(event)
}
internal inner class CustomPath(var color: Int, var brushThickness: Float) : Path()
{
}
}
Related
I am trying to build a realtime text recognization app using google MLKit vision, it's showing text properly but when I am trying to click on a particular line it is only showing the last line text.
Here is Overlay Code :
TextGraphic.kt
class TextGraphic(overlay: GraphicOverlay?,
private val element: Text.Line,
font: Typeface,
fontSize: Float,
color: Int) : Graphic(overlay!!) {
private val rectPaint: Paint = Paint()
private val textPaint: Paint
override fun draw(canvas: Canvas?) {
val rect = RectF(element.boundingBox)
canvas!!.drawRect(rect, rectPaint)
canvas.drawText(element.text, rect.left, rect.bottom, textPaint)
}
companion object {
private const val TAG = "TextGraphic"
private const val TEXT_COLOR = Color.BLACK
private const val STROKE_WIDTH = 2.0f
}
init {
rectPaint.color = color
rectPaint.style = Paint.Style.FILL_AND_STROKE
rectPaint.strokeWidth = STROKE_WIDTH
textPaint = Paint()
textPaint.color = TEXT_COLOR
textPaint.textSize = fontSize
textPaint.typeface = font
postInvalidate()
}}
GraphicOverlay.kt
class GraphicOverlay(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val lock = Any()
private var previewWidth = 0
private var widthScaleFactor = 1.0f
private var previewHeight = 0
private var heightScaleFactor = 1.0f
private var facing = CameraCharacteristics.LENS_FACING_BACK
private val graphics: MutableSet<Graphic> = HashSet()
abstract class Graphic(private val overlay: GraphicOverlay) {
abstract fun draw(canvas: Canvas?)
fun scaleX(horizontal: Float): Float {
return horizontal * overlay.widthScaleFactor
}
fun scaleY(vertical: Float): Float {
return vertical * overlay.heightScaleFactor
}
val applicationContext: Context
get() = overlay.context.applicationContext
fun translateX(x: Float): Float {
return if (overlay.facing == CameraCharacteristics.LENS_FACING_FRONT) {
overlay.width - scaleX(x)
} else {
scaleX(x)
}
}
fun translateY(y: Float): Float {
return scaleY(y)
}
fun postInvalidate() {
overlay.postInvalidate()
}
}
fun clear() {
synchronized(lock) { graphics.clear() }
postInvalidate()
}
fun add(graphic: Graphic) {
synchronized(lock) { graphics.add(graphic) }
postInvalidate()
}
fun remove(graphic: Graphic) {
synchronized(lock) { graphics.remove(graphic) }
postInvalidate()
}
fun setCameraInfo(previewWidth: Int, previewHeight: Int, facing: Int) {
synchronized(lock) {
this.previewWidth = previewWidth
this.previewHeight = previewHeight
this.facing = facing
}
postInvalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
synchronized(lock) {
if (previewWidth != 0 && previewHeight != 0) {
widthScaleFactor = width.toFloat() / previewWidth.toFloat()
heightScaleFactor = height.toFloat() / previewHeight.toFloat()
}
for (graphic in graphics) {
graphic.draw(canvas)
}
}
}}
Inside my Fragment where I am clicking :
private fun processTextFromImage(visionText: Text, imageProxy: ImageProxy) {
binding.graphicOverlay.clear()
for (block in visionText.textBlocks) {
for (line in block.lines) {
val textGraphic = TextGraphic(binding.graphicOverlay, line, font, fontSize, color = fontColor)
binding.graphicOverlay.apply {
add(textGraphic)
setOnClickListener {
Toast.makeText(it.context, line.text, Toast.LENGTH_SHORT).show()
}
}
for (element in line.elements) {
textFoundListener(element.text)
}
}
}
}
Is there any better way to to display overlay, this overlay is too fast and my click is only displays the last line text.
If anyone can help me in this, thanks a lot.
I'm trying to make custom image view that have rounded corners and a custom transition to change a border radius smoothly.
In CircleTransition, I try to get imageCornerRadius but it's always return 0 which ruined the transaction. But in activity, when I get imageCornerRadius, it returns the value in xml file. So how i can get the imageCornerRadius to perform the transition.
This is declare of my custom view
RoundedImageView
custom attribute
<declare-styleable name="RoundedImageView">
<attr name="imageCornerRadius" format="dimension" />
</declare-styleable>
class RoundedImageView : AppCompatImageView {
constructor(context: Context) : super(context) {
Log.d("debug", "first constructor")
}
constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) {
Log.d("debug", "second constructor")
init(attrSet)
}
constructor(context: Context, attrSet: AttributeSet, defStyleAttr: Int) : super(
context,
attrSet,
defStyleAttr
) {
Log.d("debug", "third constructor")
init(attrSet)
}
private fun init(attrSet: AttributeSet){
context.theme.obtainStyledAttributes(
attrSet,
R.styleable.RoundedImageView,
0,
0
).apply {
try {
imageCornerRadius = getDimensionPixelSize(
R.styleable.RoundedImageView_imageCornerRadius,
0
).toFloat()
} finally {
recycle()
}
}
}
// Custom attr
var imageCornerRadius: Float = 0F
//Attr for drawing
private lateinit var bitmapRect: RectF
val rect = RectF(drawable.bounds)
val holePath = Path()
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
Log.d("size changed", "w = $w h = $h")
bitmapRect = RectF(0f, 0f, w.toFloat(), h.toFloat())
}
override fun onDraw(canvas: Canvas?) {
val drawableWidth = this.width
val drawableHeight = this.height
/* Clip */
holePath.apply {
reset()
addRoundRect(
0F,
0F,
rect.right + drawableWidth,
rect.bottom + drawableHeight,
imageCornerRadius,
imageCornerRadius,
Path.Direction.CW
)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas?.clipPath(holePath)
} else {
#Suppress("DEPRECATION")
canvas?.clipPath(holePath, Region.Op.REPLACE)
}
// Draw image
super.onDraw(canvas)
}
}
My custom transition change Size, Coordinate, imageCornerRadius
CircleTransition.kt
class CircleTransition() : Transition() {
private val TAG = CircleTransition::class.java.simpleName
private val BOUNDS = TAG + ":viewBounds"
private val CORNER_RADIUS = TAG + ":imageCornerRadius"
private val PROPS = arrayOf(BOUNDS, CORNER_RADIUS)
init {
Log.d("debug", "Circle Transition called")
}
override fun captureStartValues(transitionValues: TransitionValues?) {
captureValues(transitionValues)
}
override fun captureEndValues(transitionValues: TransitionValues?) {
captureValues(transitionValues)
}
fun captureValues(transitionValues: TransitionValues?) {
val view = transitionValues?.view
//get View Bound
val bound = RectF()
bound.left = view?.left?.toFloat() ?: return
bound.top = view.top.toFloat()
bound.right = view.right.toFloat()
bound.bottom = view.bottom.toFloat()
transitionValues.values.put(BOUNDS, bound)
//get view Corner radius
if(view is RoundedImageView){
val cornerRadius = view.imageCornerRadius
transitionValues.values.put(CORNER_RADIUS, cornerRadius)
}
}
override fun getTransitionProperties(): Array<String> {
return PROPS
}
override fun createAnimator(
sceneRoot: ViewGroup?,
startValues: TransitionValues?,
endValues: TransitionValues?
): Animator? {
if (startValues == null || endValues == null) {
return null
}
val view = endValues.view as RoundedImageView
//startScene
val sBound = startValues.values[BOUNDS] as RectF? ?: return null
//How I get imageCornerRadius
val sCornerRadius = startValues.values[CORNER_RADIUS] as Float? ?: return null
val sWidth = sBound.right - sBound.left
val sHeight = sBound.top - sBound.bottom
//endScene
val eBound = endValues.values[BOUNDS] as RectF? ?: return null
//How I get imageCornerRadius
val eCornerRadius = endValues.values[CORNER_RADIUS] as Float? ?: return null
val eWidth = eBound.right - eBound.left
val eHeight = eBound.top - eBound.bottom
if (sBound == eBound && sCornerRadius == eCornerRadius) {
return null
}
val widthAnimator: ValueAnimator =
ValueAnimator.ofInt(sWidth.toInt(), eWidth.toInt()).apply {
addUpdateListener {
val layoutParams = view.layoutParams
layoutParams.width = it.animatedValue as Int
view.layoutParams = layoutParams
}
}
val heightAnimator: ValueAnimator =
ValueAnimator.ofInt(sHeight.toInt() * -1, eHeight.toInt() * -1).apply {
interpolator = AccelerateInterpolator()
addUpdateListener {
val layoutParams = view.layoutParams
layoutParams.height = it.animatedValue as Int
view.layoutParams = layoutParams
}
}
val cornerRadiusAnimator = ValueAnimator.ofFloat(96F, 0F).apply {
addUpdateListener {
view.imageCornerRadius = it.animatedValue as Float
}
}
// set endView have the same size, coorinate like startScene
view.x = sBound.left
view.y = sBound.top
// view.layoutParams = ViewGroup.LayoutParams(sBound.width().toInt(), sBound.height().toInt())
// move view
val startX = sBound.left
val startY = sBound.top
val moveXTo = eBound.left
val moveYTo = eBound.top
val moveXAnimator: Animator =
ObjectAnimator.ofFloat(view, "x", startX, moveXTo.toFloat())
val moveYAnimator: Animator =
ObjectAnimator.ofFloat(view, "y", startY, moveYTo.toFloat()).apply {
addUpdateListener {
view.invalidate()
}
}
val animatorSet = AnimatorSet()
animatorSet.playTogether(
widthAnimator,
heightAnimator,
cornerRadiusAnimator,
moveXAnimator,
moveYAnimator
)
return animatorSet
}
}
So im trying to to Undo/Redo action and there is a few answers on stackoverflow about this problem, but any of them is not helping with my issue. So I have my custom view for canvas implementation, where I have an arrays to store paths of my drawing, but any time Im start storing in it just do nothing. Any advices or link are appreciated.
My custom view class:
private const val STROKE_WIDTH = 12f
class CanvasCustomView #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
private var path = Path()
private val paths = ArrayList<Path>()
private val undonePaths = ArrayList<Path>()
private lateinit var extraCanvas: Canvas
private lateinit var extraBitmap: Bitmap
private var motionTouchEventX = 0f
private var motionTouchEventY = 0f
private var currentX = 0f
private var currentY = 0f
private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop
private fun touchStart() {
path.reset()
path.moveTo(motionTouchEventX, motionTouchEventY)
currentX = motionTouchEventX
currentY = motionTouchEventY
}
private fun touchMove() {
val distanceX = abs(motionTouchEventX - currentX)
val distanceY = abs(motionTouchEventY - currentY)
if (distanceX >= touchTolerance || distanceY >= touchTolerance) {
path.quadTo(
currentX,
currentY,
(motionTouchEventX + currentX) / 2,
(motionTouchEventY + currentY) / 2)
currentX = motionTouchEventX
currentY = motionTouchEventY
extraCanvas.drawPath(path, paint)
}
invalidate()
}
private fun touchUp() {
path.reset()
}
fun undoCanvasDrawing(){
// im trying to do undo here
}
fun clearCanvasDrawing() {
extraCanvas.drawColor(0, PorterDuff.Mode.CLEAR)
path.reset()
invalidate()
}
private val backgroundColor = ResourcesCompat.getColor(resources, R.color.colorBackground, null)
private val drawColor = ResourcesCompat.getColor(resources, R.color.colorPaint, null)
private val paint = Paint().apply {
color = drawColor
isAntiAlias = true
isDither = true
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
strokeWidth = STROKE_WIDTH
}
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
super.onSizeChanged(width, height, oldWidth, oldHeight)
if (::extraBitmap.isInitialized) extraBitmap.recycle()
extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
extraCanvas = Canvas(extraBitmap)
extraCanvas.drawColor(backgroundColor)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawBitmap(extraBitmap, 0f, 0f, null)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event == null)
return false
motionTouchEventX = event.x
motionTouchEventY = event.y
when (event.action) {
MotionEvent.ACTION_DOWN -> touchStart()
MotionEvent.ACTION_MOVE -> touchMove()
MotionEvent.ACTION_UP -> touchUp()
}
return true
}
}
So I decide to not use Bitmap in case you need to store a collection of Pathses,and it's very expensive. So there is my solution with undo/redo and reset functionality
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.core.content.res.ResourcesCompat
import kotlin.math.abs
class CanvasCustomView #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
companion object {
private const val STROKE_WIDTH = 12f
}
private var path = Path()
private val paths = ArrayList<Path>()
private val undonePaths = ArrayList<Path>()
private val extraCanvas: Canvas? = null
private var motionTouchEventX = 0f
private var motionTouchEventY = 0f
private var currentX = 0f
private var currentY = 0f
private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop
private val paint = Paint().apply {
color = ResourcesCompat.getColor(resources, R.color.colorBlack, null)
isAntiAlias = true
isDither = true
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
strokeWidth = STROKE_WIDTH
}
fun resetCanvasDrawing() {
path.reset() // Avoiding saving redo from Path()
paths.clear()
invalidate()
}
fun undoCanvasDrawing() {
if (paths.size > 0) {
undonePaths.add(paths.removeAt(paths.size - 1))
invalidate()
} else {
Log.d("UNDO_ERROR", "Something went wrong with UNDO action")
}
}
fun redoCanvasDrawing() {
if (undonePaths.size > 0) {
paths.add(undonePaths.removeAt(undonePaths.size - 1))
invalidate()
} else {
Log.d("REDO_ERROR", "Something went wrong with REDO action")
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
for (Path in paths) {
canvas?.drawPath(Path, paint)
}
canvas?.drawPath(path, paint)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event == null)
return false
motionTouchEventX = event.x
motionTouchEventY = event.y
when (event.action) {
MotionEvent.ACTION_DOWN -> {
undonePaths.clear()
path.reset()
path.moveTo(motionTouchEventX, motionTouchEventY)
currentX = motionTouchEventX
currentY = motionTouchEventY
invalidate()
}
MotionEvent.ACTION_MOVE -> {
val distanceX = abs(motionTouchEventX - currentX)
val distanceY = abs(motionTouchEventY - currentY)
if (distanceX >= touchTolerance || distanceY >= touchTolerance) {
path.quadTo(
currentX,
currentY,
(motionTouchEventX + currentX) / 2,
(currentY + motionTouchEventY) / 2)
currentX = motionTouchEventX
currentY = motionTouchEventY
}
invalidate()
}
MotionEvent.ACTION_UP -> {
path.lineTo(currentX, currentY)
extraCanvas?.drawPath(path, paint)
paths.add(path)
path = Path()
}
}
return true
}
}
Created CircleImageView extending NetworkImageView and using on layout to show circular image dynamically from url. but in xml layout its gives "InflateException" Binary XML file.
The layout i have used :
<com.xxxx.xxxxx.utility.CircleImageView
android:id="#+id/img"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="2dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:scaleType="fitCenter"
android:background="#drawable/rounded_all_blue"/>
CircleImageView class file in kotlin :
1) CircleImageView :
class CircleImageView : NetworkImageView {
private val SCALE_TYPE = ScaleType.CENTER_CROP
private val BITMAP_CONFIG = Bitmap.Config.ARGB_8888
private val COLORDRAWABLE_DIMENSION = 2
private val DEFAULT_BORDER_WIDTH = 0
private val DEFAULT_BORDER_COLOR = Color.BLACK
private val DEFAULT_BORDER_OVERLAY = false
private val mDrawableRect = RectF()
private val mBorderRect = RectF()
private val mShaderMatrix = Matrix()
private val mBitmapPaint = Paint()
private val mBorderPaint = Paint()
private var mBorderColor = DEFAULT_BORDER_COLOR
private var mBorderWidth = DEFAULT_BORDER_WIDTH
private var mBitmap: Bitmap? = null
private var mBitmapShader: BitmapShader? = null
private var mBitmapWidth: Int = 0
private var mBitmapHeight: Int = 0
private var mDrawableRadius: Float = 0.toFloat()
private var mBorderRadius: Float = 0.toFloat()
private var mColorFilter: ColorFilter? = null
private var mReady: Boolean = false
private var mSetupPending: Boolean = false
private var mBorderOverlay: Boolean = false
constructor(context:Context) : super(context) {
init()
}
private fun init() {
super.setScaleType(SCALE_TYPE)
mReady = true
if (mSetupPending) {
setup()
mSetupPending = false
}
}
override fun getScaleType(): ImageView.ScaleType {
return SCALE_TYPE
}
override fun setScaleType(scaleType: ImageView.ScaleType) {
if (scaleType != SCALE_TYPE) {
throw IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType))
}
}
override fun setAdjustViewBounds(adjustViewBounds: Boolean) {
if (adjustViewBounds) {
throw IllegalArgumentException("adjustViewBounds not supported.")
}
}
override fun onDraw(canvas: Canvas) {
if (drawable == null) {
return
}
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), mDrawableRadius, mBitmapPaint)
if (mBorderWidth !== 0) {
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), mBorderRadius, mBorderPaint)
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
setup()
}
fun getBorderColor(): Int {
return mBorderColor
}
fun setBorderColor(borderColor: Int) {
if (borderColor == mBorderColor) {
return
}
mBorderColor = borderColor
mBorderPaint.color = mBorderColor
invalidate()
}
fun setBorderColorResource(#ColorRes borderColorRes: Int) {
setBorderColor(context.resources.getColor(borderColorRes))
}
fun getBorderWidth(): Int {
return mBorderWidth
}
fun setBorderWidth(borderWidth: Int) {
if (borderWidth == mBorderWidth) {
return
}
mBorderWidth = borderWidth
setup()
}
fun isBorderOverlay(): Boolean {
return mBorderOverlay
}
fun setBorderOverlay(borderOverlay: Boolean) {
if (borderOverlay == mBorderOverlay) {
return
}
mBorderOverlay = borderOverlay
setup()
}
override fun setImageBitmap(bm: Bitmap) {
super.setImageBitmap(bm)
mBitmap = bm
setup()
}
override fun setImageDrawable(drawable: Drawable?) {
super.setImageDrawable(drawable)
mBitmap = getBitmapFromDrawable(drawable)
setup()
}
override fun setImageResource(#DrawableRes resId: Int) {
super.setImageResource(resId)
mBitmap = getBitmapFromDrawable(drawable)
setup()
}
override fun setImageURI(uri: Uri) {
super.setImageURI(uri)
mBitmap = getBitmapFromDrawable(drawable)
setup()
}
override fun setColorFilter(cf: ColorFilter) {
if (cf === mColorFilter) {
return
}
mColorFilter = cf
mBitmapPaint.colorFilter = mColorFilter
invalidate()
}
private fun getBitmapFromDrawable(drawable: Drawable?): Bitmap? {
if (drawable == null) {
return null
}
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
try {
val bitmap: Bitmap
if (drawable is ColorDrawable) {
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG)
} else {
bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, BITMAP_CONFIG)
}
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
} catch (e: OutOfMemoryError) {
return null
}
}
private fun setup() {
if (!mReady) {
mSetupPending = true
return
}
if (mBitmap == null) {
return
}
mBitmapShader = BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
mBitmapPaint.isAntiAlias = true
mBitmapPaint.shader = mBitmapShader
mBorderPaint.style = Paint.Style.STROKE
mBorderPaint.isAntiAlias = true
mBorderPaint.color = mBorderColor
mBorderPaint.setStrokeWidth(mBorderWidth.toFloat())
mBitmapHeight = mBitmap!!.height
mBitmapWidth = mBitmap!!.width
mBorderRect.set(0F, 0F, width.toFloat(), height.toFloat())
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2)
mDrawableRect.set(mBorderRect)
if (!mBorderOverlay) {
mDrawableRect.inset(mBorderWidth.toFloat(), mBorderWidth.toFloat())
}
mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2)
updateShaderMatrix()
invalidate()
}
private fun updateShaderMatrix() {
val scale: Float
var dx = 0f
var dy = 0f
mShaderMatrix.set(null)
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
scale = mDrawableRect.height() / mBitmapHeight as Float
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f
} else {
scale = mDrawableRect.width() / mBitmapWidth as Float
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f
}
mShaderMatrix.setScale(scale, scale)
mShaderMatrix.postTranslate((dx + 0.5f).toInt() + mDrawableRect.left, (dy + 0.5f).toInt() + mDrawableRect.top)
mBitmapShader?.setLocalMatrix(mShaderMatrix)
}
}
2) Method to call Image dynamically
fun loadImage(url:String, avatar: CircleImageView){
val imageLoader: ImageLoader by lazy {
ImageLoader(ApiConnect.instance.requestQueue, LruBitmapCache())
}
avatar.setImageUrl(url,imageLoader);
}
3) we can load image
APIController().loadImage(imageurlpath,holder.img)
Image is showing when i have used "NetworkImageView" but the problem is its not going to fit and circular, i have read and convert "CircleImageView" into kotlin and used but it gives error please help.
// Error log :
2019-07-05 18:29:20.721 21385-21385/com.xxxx.xxxxxx E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xxxx.xxxxxx, PID: 21385
android.view.InflateException: Binary XML file line #11: Binary XML file line #11: Error inflating class com.xxxx.xxxxxx.utility.CircleImageView
Caused by: android.view.InflateException: Binary XML file line #11: Error inflating class com.xxxx.xxxxx.utility.CircleImageView
Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
at java.lang.Class.getConstructor0(Class.java:2327)
at java.lang.Class.getConstructor(Class.java:1725)
The problem is that your custom view does not correctly override the necessary constructors. The error is a NoSuchMethodException since a constructor with a Context and AttributeSet is expected. This does not exist.
You could create this constructor and invoke super(context, attributeSet) or use the JvmOverload annotation.
So you could implement the constructors like this:
class CustomView : View {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
//do stuff
}
}
Or use the annotation like this:
class CustomView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}
I create an override method called draw in SurfaceView. I want to see the paint I set in my SurfaceView but nothing shows up when I touched the screen and trying to draw a line. What should I do to make this work?
private var mPaint: Paint
private val mPaths: ArrayList<Path> = ArrayList<Path>()
private val mEraserPath: Path = Path()
init {
mPaint = Paint()
mPaint.isAntiAlias = true
mPaint.isDither = true
mPaint.style = Paint.Style.STROKE
mPaint.strokeJoin = Paint.Join.ROUND
mPaint.strokeCap = Paint.Cap.ROUND
mPaint.strokeWidth = 3f
mPaint.alpha = 255
mPaint.color = android.graphics.Color.BLACK
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
override fun draw(canvas: Canvas) {
canvas.drawPaint(mPaint)
val action: EditAction? = this.getEditAction()
for (path: Path in mPaths) {
when (action) {
EditAction.COLOR -> {
setPaintColor(this.getStrokeColor()) // android.graphics.Color.BLACK
setPaintSize(this.getStrokeSize()) // 5f
canvas.drawPath(path, mPaint)
}
EditAction.SIZE -> {
setPaintColor(this.getStrokeColor()) // android.graphics.Color.BLACK
setPaintSize(this.getStrokeSize()) // 5f
canvas.drawPath(path, mPaint)
}
EditAction.ERASER -> {
}
}
}
canvas.drawPath(mEraserPath, mPaint)
super.draw(canvas)
}
Instead of using draw, use the SurfaceHolder.Callback functions instead, as shown below. I have mof
class SlowSurfaceView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0)
: SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback {
private var mPaint: Paint = Paint()
init {
holder.addCallback(this)
mPaint.isAntiAlias = true
mPaint.isDither = true
mPaint.style = Paint.Style.STROKE
mPaint.strokeJoin = Paint.Join.ROUND
mPaint.strokeCap = Paint.Cap.ROUND
mPaint.strokeWidth = 3f
mPaint.alpha = 255
mPaint.color = android.graphics.Color.RED
mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
// Do nothing for now
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
}
override fun surfaceCreated(holder: SurfaceHolder) {
if (isAttachedToWindow) {
val canvas = holder.lockCanvas()
canvas?.let {
it.drawRect(Rect(100, 100, 200, 200), mPaint)
holder.unlockCanvasAndPost(it)
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val desiredWidth = suggestedMinimumWidth + paddingLeft + paddingRight
val desiredHeight = suggestedMinimumHeight + paddingTop + paddingBottom
setMeasuredDimension(View.resolveSize(desiredWidth, widthMeasureSpec),
View.resolveSize(desiredHeight, heightMeasureSpec))
}
}
Refer to the above modify the code, and hopefully you should get what you want.