I'm trying to create an overlay for the corners in my app.
I want them to be rounded and black all the time like this:
I created it with this code:
<?xml version="1.0" encoding="utf-8"?>
<item>
<shape android:shape="rectangle">
<solid android:color="#000"/>
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#FFF"/>
<corners android:radius="24dp"/>
</shape>
</item>
Then I added it to my theme as an android:windowFrame:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowFrame">#drawable/corner_overlay</item>
</style>
But obviosly everything was white then. Now I'm trying to clip the second shape instead of coloring it white and if that doesnt work I'm looking for another way to get my corner overlay working!
Thanks for every help!
You can create a Custom Frame Layout and wrap all your content with it.
class RoundedCornerLayout : FrameLayout {
private var paint: Paint? = null
private var paint2: Paint? = null
private var cornerRadius: Float = 10f
private var mWidth: Int = 0
private var mHeight: Int = 0
constructor(context: Context) : super(context) {
init(context, null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
init(context, attrs, defStyle)
}
private fun init(context: Context, attrs: AttributeSet?, defStyle: Int) {
val metrics = context.getResources().getDisplayMetrics()
cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics)
paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint?.color = Color.BLACK
paint2 = Paint(Paint.ANTI_ALIAS_FLAG)
paint2?.color = Color.TRANSPARENT
paint2?.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
setWillNotDraw(false)
setLayerType(LAYER_TYPE_SOFTWARE, null)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mWidth = w
mHeight = h
}
override fun onDraw(canvas: Canvas?) {
canvas?.drawRect(0f, 0f, mWidth.toFloat(), mHeight.toFloat(), paint)
canvas?.drawRoundRect(RectF(0f, 0f, mWidth.toFloat(), mHeight.toFloat()), cornerRadius, cornerRadius, paint2)
}
companion object {
private val CORNER_RADIUS = 40.0f
}
}
And use it in your xml:
<com.dantes.backstack.RoundedCornerLayout
android:id="#+id/wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your content here-->
</com.dantes.backstack.RoundedCornerLayout>
Of course this is draft and you need to optimize it and make more clear to your purposes. But principal part is PorterDuffXfermode(PorterDuff.Mode.CLEAR). This mode make magic and cuts the tarnsparent rectangle from the dark.
Maybe it will help or at least push your minds in right direction.
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 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)
}
}