I have a Custom EditTextClass that Im using for all EditTexts on my App -
Class CustomEditText : EditText {
...
}
I want to change the background image, so that it should be reflected in all the usages of this CustomEditText.
I tried this -
override fun onDraw(canvas: Canvas ? ) {
val d = AppCompatResources.getDrawable(mContext!!, R.drawable.new_drawable)
d?.draw(canvas!!)
super.onDraw(canvas)
}
and this -
fun init(context: Context ? , attrs : AttributeSet ? ) {
background = ResourcesCompat.getDrawable(context!!.getResources(), R.drawable.new_drawable, null)
setBackground(background)
}
Both the ways, don't work. Can anyone tell me what is the correct solution ?
You need to setBounds to drawable when draw drawable.
So you should make your draw method like this.
override fun onDraw(canvas: Canvas? ) {
val d = AppCompatResources.getDrawable(context!!, R.drawable.new_drawable)
d?.setBounds(0, 0, width, height)
d?.draw(canvas!!)
super.onDraw(canvas)
}
First draw to canvas and pass the canvas to parent meaning super.onDraw should be at bottom
override fun onDraw(canvas: Canvas ? ) {
val d = AppCompatResources.getDrawable(mContext!!, R.drawable.new_drawable)
d?.draw(canvas!!)
super.onDraw(canvas)
}
In my opinion, you should not override onDraw method as this could cause issues when super.onDraw(canvas) is called before your operations (your drawable could cover the text, hits, drawable elements, etc.).
I've made a demo, which worked for me:
class CustomEditText : AppCompatEditText {
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,
attrs,
defStyleAttr) {
init()
}
private fun init() {
setBackgroundResource(R.drawable.new_drawable)
}
}
Related
I want to have an imageView that is fully visible at the top, but further down it fades out, resulting in being fully transparent in the bottom. How do I achieve this?
I have a solution for this, but it is not perfect
class LinearGradientView : View {
var bitmap: Bitmap
private val paint = Paint()
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(MeasureSpec.makeMeasureSpec(bitmap.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(bitmap.height, MeasureSpec.EXACTLY))
}
override fun onDraw(canvas: Canvas) {
val shaderA: Shader = LinearGradient(0F, 0F, 0F, bitmap.height.toFloat(), -0x1, 0x00ffffff, Shader.TileMode.CLAMP)
val shaderB: Shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paint.shader = ComposeShader(shaderA, shaderB, PorterDuff.Mode.SRC_IN)
canvas.drawRect(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat(), paint)
}
init {
bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ktor)
}
}
The key is to leverage ComposeShader with LinearGradient and BitmapShader.
reference:
Android extends imageView gradient transparent vertical linear from input bitmap
Drawing a Bitmap to a Canvas with an alpha gradient
I am trying to set up a canvas to redraw whenever the user clicks a button. Whenever I trigger the redraw via invalidate() the app freezes and crashes. I am very new to android development so I am struggling to see what I neglecting. Any insight would be appriciated.
The relevent classes are included below.
Thanks,
mainactivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun submit(view: View) {
val text = findViewById<View>(R.id.editTextNumberDecimal) as EditText
val canvas = findViewById<View>(R.id.customCanvas) as CustomCanvas
val value = text.text.toString()
val t = findViewById<View>(R.id.textView) as TextView
t.text = value
canvas.setWeightandRedraw(value.toFloat())
}
}
canvas
class CustomCanvas : 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) {
}
var weight: Float = 0.0f;
override fun onDraw(canvas: Canvas?) {
drawBar(canvas);
drawWeightKg(canvas, this.weight)
}
fun setWeightandRedraw(_weight: Float) {
weight = _weight;
invalidate();
}
fun drawBar(canvas: Canvas?) {
.........
}
fun drawWeightKg(canvas: Canvas?, weight: Float) {
.........
}
}
Moving invalidate() to onDraw() appeard to fix this.
override fun onDraw(canvas: Canvas?) {
drawBar(canvas);
drawWeightKg(canvas, this.weight)
invalidate();
}
In Android UI, we can create a custom view by overloading View as shown below.
class CustomView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0)
: View(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Perform the needing drawing
if (isAttachedToWindow) invalidate()
}
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))
}
}
Can we wrap JetpackCompose in this CustomView, so that the underlying Drawing it using JetpackCompose instead?
I check https://developer.android.com/jetpack/compose/interop/interop-apis, don't seems to have it stated.
To get it to work, we can have
class CustomComposeView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {
#Composable
override fun Content() {
// JetpacCompose code here
}
}
In the XML, we can have something like this
<com.package.CustomComposeView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="700dp" />
I made a custom toggle view by inheriting SwitchCompat class.
In image, the first toggle is MaterialToggle provided by default system.
And Second is my custom toggle view.
I found that when I pressed the toggle the thing like a shadow show appear like this image(the area outside the red circle and inside a black circle) And this also applied to my custom view.
I want to remove that but I cannot find the attribute related to that.
How can I remove that?
My customView toggle code(Kotlin)
class Toggle : SwitchCompat {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs){
initView(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs)
var isDisabled : Boolean = false
set(isDisabled) {
field = isDisabled
isEnabled = !isDisabled
}
fun setToggleIsDisabled(isDisabled: Boolean){
this.isDisabled = isDisabled
}
private fun initView(context: Context, attrs: AttributeSet?){
if(attrs !=null){
val attributes : TypedArray = context.obtainStyledAttributes(attrs, R.styleable.Toggle)
isDisabled = attributes.getBoolean(R.styleable.Toggle_toggleIsDisabled, false)
setToggleInfo()
attributes.recycle()
}else{
setToggleInfo()
}
}
private fun setToggleInfo() {
trackDrawable = AppCompatResources.getDrawable(context, R.drawable.toggle_track)
thumbDrawable = AppCompatResources.getDrawable(context, R.drawable.toggle_thumb)
}
}
I have recyclerView with and item as seen below:
Now I want to be able to click anywhere on the item and editText should come into focus.
I can do that by setting onTouchListener on my view like this:
row_item.setOnTouchListener{ _, _ ->
editText.requestFocus()
view.background = Color.GREEN.toDrawable()
true
}
I also want to run some additional code whenever the item is clicked. Here I'm putting background color change for the sake of the example.
The problem is that whenever I click editText itself it is getting focused, but row_item touchListener is ignored, and the background doesn't change its color.
From my research, I've found that I should somehow intercept touch event. I thought I can do that by returning true in row_item.setOnTouchListener, but it doesn't work as you can see.
How can I intercept such touch event?
You should create a custom container class and then override onInterceptTouchEvent method and do your stuff there, then using this custom container class as the root of the item. Look at the following code:
class CustomFrameLayout : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
private var mOnInterceptTouchEventListener: OnTouchListener? = null
fun setOnInterceptTouchEventListener(onInterceptTouchEventListener: OnTouchListener) {
this.mOnInterceptTouchEventListener = onInterceptTouchEventListener
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
if(mOnInterceptTouchEventListener != null && mOnInterceptTouchEventListener?.onTouch(this, ev) == true)
return true;
return super.onInterceptTouchEvent(ev)
}
}
And then adding this listener to your row_item
row_item.setOnInterceptTouchEventListener(object: View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
editText.requestFocus()
v?.background = Color.GREEN.toDrawable()
return false
}
})
it may not be the best answer but it works.