Java – Place a circular image horizontally to display the user profile picture

Place a circular image horizontally to display the user profile picture… here is a solution to the problem.

Place a circular image horizontally to display the user profile picture

I need to display all user profile pictures participating in the specified activity, it should be one after the other in the horizontal circular image, after 5 images. It should show the total number of users remaining. I need java and xml files. These profile images will come from the database. Please suggest me any library or method

I need like this

Solution

enter image description here

OverlapImageViewActivity.kt

class OverlapImageViewActivity : AppCompatActivity(), RecyclerViewClickListener {

private val mAdapter by lazy { OverlapRecyclerViewAdapter(this, this,overlapLimit) }

------limit number of items to be overlapped
    private val overlapLimit = 5

------set value of item overlapping
    private val overlapWidth = -50

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

------create dummy list to set on recycler view
        setDummyArrayList()

------set up recycler view
        val layoutManager = LinearLayoutManager(this,
                LinearLayoutManager.HORIZONTAL, false)
        recyclerView.layoutManager = layoutManager

------set item decoration for item overlapping
        recyclerView.addItemDecoration(OverlapRecyclerViewDecoration(overlapLimit, overlapWidth))
        recyclerView.adapter = mAdapter
        mAdapter.setImageList(setDummyArrayList())

}

/**
     * add dummy data to ArrayList
     */
    private fun setDummyArrayList(): ArrayList<OverlapImageModel> {
        val mArrayList = ArrayList<OverlapImageModel>()

-----fill data in to array list
        for (i in 0..30) {
            val imageModel = OverlapImageModel()
            imageModel.imageUrl = imageURLs[i % imageURLs.size]
            mArrayList.add(imageModel)
        }

return mArrayList
    }

override fun onNormalItemClicked(adapterPosition: Int) {
        toast(this,"Normal item clicked >> $adapterPosition")
    }

override fun onNumberedItemClick(adapterPosition: Int) {
        toast(this,"Numbered item clicked >> $adapterPosition")
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:orientation="vertical"
    tools:context=".activities. OverlapImageViewActivity">

<android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingEnd="5dp"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        tools:listitem="@layout/row_image" />

</LinearLayout>

OverLapRecyclerViewAdapter.kt

class OverlapRecyclerViewAdapter(private var mContext: Context, private var recyclerViewClickListener: RecyclerViewClickListener
                                 , private val overlapLimit: Int) : RecyclerView.Adapter<OverlapRecyclerViewAdapter.CustomViewHolder>() {

private val TAG = OverlapRecyclerViewAdapter::class.java.simpleName

----array list to be shown
    private var mImageList = ArrayList<OverlapImageModel>()

----array list to be shown after expansion
    private var mImageExpandedList = ArrayList<OverlapImageModel>()

----flag to indicate recyclerview is expanded or not
    private var isExpanded = false

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
        val view = LayoutInflater.from(mContext).inflate(R.layout.row_image, parent, false)
        return CustomViewHolder(view)
    }

override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
        val mCurrentImageModel = mImageList[position]

----bind data to view
        holder.bind(mCurrentImageModel)
    }

/**
     * set array list over adapter
     */
    fun setImageList(mImageList: ArrayList<OverlapImageModel>) {
        if (mImageList.size > overlapLimit) {
            for (mImageModel in mImageList) {
                if (this.mImageList.size <= overlapLimit) {
                    this.mImageList.add(mImageModel)
                } else {
                    this.mImageExpandedList.add(mImageModel)
                }
            }
        } else {
            this.mImageList = mImageList
        }
        notifyDataSetChanged()
    }

/**
     * add items to array list
     */
    fun addItems(mImageList: ArrayList<OverlapImageModel>) {
        this.mImageList.addAll(mImageList)
        notifyDataSetChanged()
    }

override fun getItemCount(): Int {
        return mImageList.size
    }

/**
     * get item by its position
     */
    fun getItem(pos: Int): OverlapImageModel {
        return mImageList[pos]
    }

inner class CustomViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

private var requestOptions: RequestOptions? = null

/**
         * init request option for glide
         */
        private fun getGlideRequestOptions(): RequestOptions {
            if (requestOptions == null) {
                requestOptions = RequestOptions()
                requestOptions?. error(R.mipmap.ic_launcher)
                requestOptions?. placeholder(R.mipmap.ic_launcher)
            }
            return requestOptions!!
        }

/**
         * bind model data to item
         */
        fun bind(mImageModel: OverlapImageModel) {

if (adapterPosition == overlapLimit && !isExpanded) {

----set text drawable to show count on last imageview
                val text = mImageExpandedList.size.toString()
                val drawable = TextDrawable.builder()
                        .beginConfig()
                        .textColor(Color.WHITE)
                        .width(90)
                        .height(90)
                        .endConfig()
                        .buildRound(text, Color.parseColor("#8FAE5D"))
                itemView.imageView.setImageDrawable(drawable)
            } else {

----load image
                Glide.with(mContext)
                        .load(mImageModel.imageUrl)
                        .apply(getGlideRequestOptions())
                        .into(itemView.imageView)
            }

----handle item click
            itemView.imageView.setOnClickListener {
                if (adapterPosition == overlapLimit && !isExpanded) {
                    recyclerViewClickListener.onNumberedItemClick(adapterPosition)
                } else {
                    recyclerViewClickListener.onNormalItemClicked(adapterPosition)
                }
            }
        }
    }
}

OverlapRecyclerViewDecoration.kt

class OverlapRecyclerViewDecoration(private val overlapLimit: Int, private val overlapWidth: Int) : RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State?) {

-----get current position of item
        val itemPosition = parent.getChildAdapterPosition(view)

-----avoid first item decoration else it will go of the screen
        if (itemPosition == 0) {
            return
        } else {

-----apply decoration
            when {
                itemPosition <= overlapLimit -> outRect.set(overlapWidth, 0, 0, 0)

else -> outRect.set(0, 0, 0, 0)
            }
        }
    }
}

TextDrawable.kt

class TextDrawable(builder: Builder) : ShapeDrawable(builder.shape) {

private val textPaint: Paint
    private val borderPaint: Paint
    private val text: String?
    private val color: Int
    private val shape: RectShape?
    private val height: Int
    private val width: Int
    private val fontSize: Int
    private val radius: Float
    private val borderThickness: Int

init {

 shape properties
        shape = builder.shape
        height = builder.height
        width = builder.width
        radius = builder.radius

 text and color
        text = if (builder.toUpperCase) builder.text!!. toUpperCase() else builder.text
        color = builder.color

 text paint settings
        fontSize = builder.fontSize
        textPaint = Paint()
        textPaint.color = builder.textColor
        textPaint.isAntiAlias = true
        textPaint.isFakeBoldText = builder.isBold
        textPaint.style = Paint.Style.FILL
        textPaint.typeface = builder.font
        textPaint.textAlign = Paint.Align.CENTER
        textPaint.strokeWidth = builder.borderThickness.toFloat()

 border paint settings
        borderThickness = builder.borderThickness
        borderPaint = Paint()
        borderPaint.color = getDarkerShade(builder.color)
        borderPaint.style = Paint.Style.STROKE
        borderPaint.strokeWidth = borderThickness.toFloat()

 drawable paint color
        val paint = paint
        paint.color = color

}

private fun getDarkerShade(color: Int): Int {
        return Color.rgb((SHADE_FACTOR * Color.red(color)).toInt(),
                (SHADE_FACTOR * Color.green(color)).toInt(),
                (SHADE_FACTOR * Color.blue(color)).toInt())
    }

override fun draw(canvas: Canvas) {
        super.draw(canvas)
        val r = bounds

 draw border
        if (borderThickness > 0) {
            drawBorder(canvas)
        }

val count = canvas.save()
        canvas.translate(r.left.toFloat(), r.top.toFloat())

 draw text
        val width = if (this.width < 0) r.width() else this.width
        val height = if (this.height < 0) r.height() else this.height
        val fontSize = if (this.fontSize < 0) Math.min(width, height) / 2 else this.fontSize
        textPaint.textSize = fontSize.toFloat()
        canvas.drawText(text!!, (width / 2).toFloat(), height / 2 - (textPaint.descent() + textPaint.ascent()) / 2, textPaint)

canvas.restoreToCount(count)

}

private fun drawBorder(canvas: Canvas) {
        val rect = RectF(bounds)
        rect.inset((borderThickness / 2).toFloat(), (borderThickness / 2).toFloat())

when (shape) {
            is OvalShape -> canvas.drawOval(rect, borderPaint)
            is RoundRectShape -> canvas.drawRoundRect(rect, radius, radius, borderPaint)
            else -> canvas.drawRect(rect, borderPaint)
        }
    }

override fun setAlpha(alpha: Int) {
        textPaint.alpha = alpha
    }

override fun setColorFilter(cf: ColorFilter?) {
        textPaint.colorFilter = cf
    }

override fun getOpacity(): Int {
        return PixelFormat.TRANSLUCENT
    }

override fun getIntrinsicWidth(): Int {
        return width
    }

override fun getIntrinsicHeight(): Int {
        return height
    }

class Builder : IConfigBuilder, IShapeBuilder, IBuilder {

var text: String? = null

var color: Int = 0

var borderThickness: Int = 0

var borderColor: Int = 0

var width: Int = 0

var height: Int = 0

var font: Typeface? = null

var shape: RectShape? = null

var textColor: Int = 0

var fontSize: Int = 0

var isBold: Boolean = false

var toUpperCase: Boolean = false

var radius: Float = 0.toFloat()

init {
            text = ""
            color = Color.GRAY
            textColor = Color.WHITE
            borderThickness = 0
            borderColor = 0
            width = -1
            height = -1
            shape = RectShape()
            font = Typeface.create("sans-serif-light", Typeface.NORMAL)
            fontSize = -1
            isBold = false
            toUpperCase = false
        }

override fun width(width: Int): IConfigBuilder {
            this.width = width
            return this
        }

override fun height(height: Int): IConfigBuilder {
            this.height = height
            return this
        }

override fun textColor(color: Int): IConfigBuilder {
            this.textColor = color
            return this
        }

override fun withBorder(thickness: Int): IConfigBuilder {
            this.borderThickness = thickness
            return this
        }

override fun borderColor(color: Int): IConfigBuilder {
            this.borderColor= borderColor
            return this
        }

override fun useFont(font: Typeface): IConfigBuilder {
            this.font = font
            return this
        }

override fun fontSize(size: Int): IConfigBuilder {
            this.fontSize = size
            return this
        }

override fun bold(): IConfigBuilder {
            this.isBold = true
            return this
        }

override fun toUpperCase(): IConfigBuilder {
            this.toUpperCase = true
            return this
        }

override fun beginConfig(): IConfigBuilder {
            return this
        }

override fun endConfig(): IShapeBuilder {
            return this
        }

override fun rect(): IBuilder {
            this.shape = RectShape()
            return this
        }

override fun round(): IBuilder {
            this.shape = OvalShape()
            return this
        }

override fun roundRect(radius: Int): IBuilder {
            this.radius = radius.toFloat()
            val radii = floatArrayOf(radius.toFloat(), radius.toFloat(), radius.toFloat(), radius.toFloat(), radius.toFloat(), radius.toFloat(), radius.toFloat(), radius.toFloat())
            this.shape = RoundRectShape(radii, null, null)
            return this
        }

override fun buildRect(text: String, color: Int): TextDrawable {
            rect()
            return build(text, color)
        }

override fun buildRoundRect(text: String, color: Int, radius: Int): TextDrawable {
            roundRect(radius)
            return build(text, color)
        }

override fun buildRound(text: String, color: Int): TextDrawable {
            round()
            return build(text, color)
        }

override fun build(text: String, color: Int): TextDrawable {
            this.color = color
            this.text = text
            return TextDrawable(this)
        }
    }

interface IConfigBuilder {
        fun width(width: Int): IConfigBuilder

fun height(height: Int): IConfigBuilder

fun textColor(color: Int): IConfigBuilder

fun withBorder(thickness: Int): IConfigBuilder

fun borderColor(color: Int): IConfigBuilder

fun useFont(font: Typeface): IConfigBuilder

fun fontSize(size: Int): IConfigBuilder

fun bold(): IConfigBuilder

fun toUpperCase(): IConfigBuilder

fun endConfig(): IShapeBuilder
    }

interface IBuilder {

fun build(text: String, color: Int): TextDrawable
    }

interface IShapeBuilder {

fun beginConfig(): IConfigBuilder

fun rect(): IBuilder

fun round(): IBuilder

fun roundRect(radius: Int): IBuilder

fun buildRect(text: String, color: Int): TextDrawable

fun buildRoundRect(text: String, color: Int, radius: Int): TextDrawable

fun buildRound(text: String, color: Int): TextDrawable
    }

companion object {
        private val SHADE_FACTOR = 0.9f

fun builder(): IShapeBuilder {
            return Builder()
        }
    }
}

RecyclerViewClickListener.kt

interface RecyclerViewClickListener {
    fun onNormalItemClicked(adapterPosition: Int)

fun onNumberedItemClick(adapterPosition: Int)
}

OverlapImageModel.kt

class OverlapImageModel {
    var imageUrl: String? = null
}

row_image.xml

<?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="@dimen/image_size"
    android:layout_height="@dimen/image_size"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_gravity="center"
    android:animateLayoutChanges="true">

<de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/imageView"
        app:civ_border_color="#ffffff"
        app:civ_border_width="2dp"
        android:layout_width="@dimen/image_size"
        android:layout_height="@dimen/image_size"
        android:layout_centerInParent="true"
        tools:src="@mipmap/ic_launcher" />
</RelativeLayout>

Related Problems and Solutions