1st April, 2023
Howdy Android developer! In this tutorial we are going to create a view container (layout) with rounded corners. This is a subclass of the Android FrameLayout class. Let’s get started.
Step 1: Create New Project
Create a new project in Android Studio from File ⇒ New Project and select Empty Activity from the templates.
Step 2: Create custom View
Open the project tool window by clicking on the project option from the Tool window bar. After that select the Android option from the drop-down and open the app → java hierarchy.
Right click on the package name and select New → Kotlin or Java class. Modify the code as below.
package com.androidchunk.myapp import android.content.Context import android.graphics.* import android.util.AttributeSet import android.util.DisplayMetrics import android.util.TypedValue import android.widget.FrameLayout class RoundCornerLayout : FrameLayout { private var CORNER_RADIUS = 50f private var mCornerRadius = 0f private var mPaint: Paint? = null private var mMaskPaint: Paint? = null private var mMetrics: DisplayMetrics? = null constructor(context: Context) : super(context) { init(context) } constructor(context: Context, attrs: AttributeSet?) : super(context, attrs, 0) { init(context) } constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr) { init(context) } private fun init(context: Context) { mMetrics = context.resources.displayMetrics mCornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, mMetrics) mPaint = Paint(Paint.ANTI_ALIAS_FLAG) mMaskPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG) mMaskPaint!!.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) setWillNotDraw(false) } override fun draw(canvas: Canvas) { if (isInEditMode) { /* If you don't use this if clause, Android Studio's layout preview window will not showing anything!!! */ super.draw(canvas) } val offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) val offscreenCanvas = Canvas(offscreenBitmap) super.draw(offscreenCanvas) val maskBitmap = createMask(width, height) offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, mMaskPaint) canvas.drawBitmap(offscreenBitmap, 0f, 0f, mPaint) } private fun createMask(width: Int, height: Int): Bitmap { val mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8) val tempCanvas = Canvas(mask) val paint = Paint(Paint.ANTI_ALIAS_FLAG) paint.color = Color.WHITE tempCanvas.drawRect(0F, 0F, width.toFloat(), height.toFloat(), paint) paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) tempCanvas.drawRoundRect( RectF(0F, 0F, width.toFloat(), height.toFloat()), mCornerRadius, mCornerRadius, paint ) return mask } fun setCornerRadius(myCornerRadius: Float) { CORNER_RADIUS = myCornerRadius mCornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, mMetrics) this.invalidate() } }
package com.androidchunk.myapp; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; public class RoundCornerLayout extends FrameLayout { private float CORNER_RADIUS = 50.f; private float mCornerRadius; private Paint mPaint; private Paint mMaskPaint; private DisplayMetrics mMetrics; public RoundCornerLayout(@NonNull Context context) { super(context); init(context); } public RoundCornerLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs, 0); init(context); } public RoundCornerLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mMetrics = context.getResources().getDisplayMetrics(); mCornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, mMetrics); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); mMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); setWillNotDraw(false); } @Override public void draw(Canvas canvas) { if (isInEditMode()) { /* If you don't use this if clause, Android Studio's layout preview window will not showing anything!!! */ super.draw(canvas); } Bitmap offscreenBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas offscreenCanvas = new Canvas(offscreenBitmap); super.draw(offscreenCanvas); Bitmap maskBitmap = createMask(getWidth(), getHeight()); offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, mMaskPaint); canvas.drawBitmap(offscreenBitmap, 0f, 0f, mPaint); } private Bitmap createMask(int width, int height) { Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8); Canvas tempCanvas = new Canvas(mask); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.WHITE); tempCanvas.drawRect(0, 0, width, height, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); tempCanvas.drawRoundRect(new RectF(0, 0, width, height), mCornerRadius, mCornerRadius, paint); return mask; } public void setCornerRadius(float myCornerRadius) { CORNER_RADIUS = myCornerRadius; mCornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, mMetrics); this.invalidate(); } }
Step 3: Add View to Layout XML
Rebuild the project. Now open the activity’s xml layout file, you will see the custom layout on the palette. Add it to the activity’s XML layout.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp" tools:context=".MainActivity"> <com.androidchunk.myapp.RoundCornerLayout android:id="@+id/myRoundCornerLayout" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerHorizontal="true" android:background="#F50057" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:layout_marginEnd="10dp" android:layout_marginBottom="10dp" android:background="#6A7478" android:orientation="vertical" android:padding="20dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Corner" android:textColor="#FFFFFF" /> <SeekBar android:id="@+id/seekBar1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:max="100" android:progress="20" /> </LinearLayout> </RelativeLayout>
Step 4: Modify Your Activity
Finally, You will have to add custom layout control code in the activity. Change your activity as follows and run your app.
package com.androidchunk.myapp import android.os.Bundle import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { private lateinit var mRoundCornerLayout: RoundCornerLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mRoundCornerLayout = findViewById(R.id.myRoundCornerLayout) val seekBar = findViewById<SeekBar>(R.id.seekBar1) seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { runOnUiThread { mRoundCornerLayout.setCornerRadius(progress.toFloat()) } } override fun onStartTrackingTouch(seekBar: SeekBar) {} override fun onStopTrackingTouch(seekBar: SeekBar) {} }) } }
package com.androidchunk.myapp; import android.os.Bundle; import android.widget.SeekBar; import androidx.appcompat.app.AppCompatActivity; class MainActivity extends AppCompatActivity { private RoundCornerLayout mRoundCornerLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRoundCornerLayout = findViewById(R.id.myRoundCornerLayout); SeekBar seekBar = findViewById(R.id.seekBar1); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromUser) { runOnUiThread(new Runnable() { @Override public void run() { mRoundCornerLayout.setCornerRadius(progress); } }); } @Override public void onStartTrackingTouch(SeekBar seekBar){} @Override public void onStopTrackingTouch(SeekBar seekBar){} }); } }
Happy coding!