Tuesday, June 25, 2019

Grid UI Launcher by ConstraintLayout

When we are trying to build a custom launcher, we could think about some layout options like grid, scroll view... In this post, we are implementing the grid layout by android default constraint layout.

1. Draw GuideLines

constraintlayout with grid guideline

ConstraintLayout containing 5x4 grid with 6x5 guideline

guideline attribute percent

Set constraint like the above picture(percent 0.2, 0.4, etc...) to each of vertical, horizontal lines.

2. Grid Element View

Make Custom Element View composed of upper-side Icon image, lower-side app name.

class AppIconView(context:Context) : View(context) {
 private val paint = Paint()
 private val iconDrawRegion = Rect()
 private lateinit var label: String
 lateinit var icon: Bitmap
 lateinit var packageName: String

 init{
  paint.setTypeface(Typeface.create(Typeface.DEFAULT,Typeface.NORMAL))
  paint.setAntiAlias(true)
 }

 fun setAppInfo(packageName:String, label:String ,icon:BitmapDrawable){
  this.packageName = packageName
  this.label = label
  this.icon = icon.bitmap
 }

 override fun onDraw(canvas: Canvas) {
  paint.textSize = width/7f
  val paddingW = width/5
  iconDrawRegion.set(paddingW,paddingW,width-paddingW,width-paddingW)
  canvas.drawBitmap(icon,null,iconDrawRegion,paint)

  val textDrawSize = paint.measureText(label)
  canvas.drawText(label,(width-textDrawSize)/2f,height-(2*paint.textSize),paint)
 }

Above code would make a view seems like simple and conventional launcher. For launcher's other behaviors such as drag and drop, We need some additional code that would also be covered by later post.

3. Compose Simple Activity

 guideLineV = arrayOf(R.id.guide_v0,R.id.guide_v1,R.id.guide_v2,R.id.guide_v3,R.id.guide_v4)  guideLineH = arrayOf(R.id.guide_h0,R.id.guide_h1,R.id.guide_h2,R.id.guide_h3,R.id.guide_h4,R.id.guide_h5)  layout = findViewById(R.id.my_constraint_layout)
 var app_count =0
 val filter = Intent(Intent.ACTION_MAIN)
 filter.addCategory(Intent.CATEGORY_LAUNCHER)
 val allApps = packageManager.queryIntentActivities(filter, 0)
 val cs = ConstraintSet()
 for (info in allApps) {
  val da = AppIconView(this).apply {
   id = View.generateViewId()
   setAppInfo(info.activityInfo.packageName, info.loadLabel(packageManager).toString() ,info.activityInfo.loadIcon(packageManager) as BitmapDrawable)
   
  val col = app_count % 4
  val row = app_count / 4
  if(row>4){ // Because we didn't implement tab for multiple appPage, exceeded apps would be just skipped
   break
  }
  apps[row][col] = da
  da.col = col
  da.row = row
  layout.addView(da)
  cs.connect(da.id,ConstraintSet.START,guideLineV[col],ConstraintSet.START)
  cs.connect(da.id,ConstraintSet.END,guideLineV[col+1],ConstraintSet.START)
  cs.connect(da.id,ConstraintSet.TOP,guideLineH[row],ConstraintSet.TOP)
  cs.connect(da.id,ConstraintSet.BOTTOM,guideLineH[row+1],ConstraintSet.TOP)
  app_count++
 }
 cs.applyTo(layout)

I referenced this article for getting installed apps for custom launcher. We can insert above code to oncreate method, and it's done.

4. Result

Now, Our view would be seems like below.

android launcher grid layout

No comments:

Post a Comment

[osx command] file or folder monitoring

1. command fswatch [file or folder to monitor] | xargs -I {} echo $(date) {} >> [log file name] ...