I need to show the users profile picture who all are joining in a specified event and it should be in horizontal circular images one after other and after 5 images. it should show the remaining users total count. I need both java and xml file. These profile images will be from database. Please suggest me any library or a way to do it


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>

Try this here is the working code for java
RecyclerViewActivity
public class RecyclerViewActivity extends AppCompatActivity {
    private  final Integer[] IMAGES = {R.drawable.nilesh, R.drawable.nilesh, R.drawable.nilesh, R.drawable.nilesh,
            R.drawable.nilesh, R.drawable.nilesh, R.drawable.nilesh, R.drawable.nilesh};
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    RecyclerView myRecyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);
        Collections.addAll(arrayList, IMAGES);
        myRecyclerView=findViewById(R.id.myRecyclerView);
        myRecyclerView.setLayoutManager(new LinearLayoutManager(this,
                LinearLayoutManager.HORIZONTAL,false));
        myRecyclerView.addItemDecoration(new OverlapDecoration());
        myRecyclerView.setHasFixedSize(true);
        myRecyclerView.setAdapter(new DataAdapter(this,arrayList));
    }
    public class OverlapDecoration extends RecyclerView.ItemDecoration {
        private final static int vertOverlap = -40;
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            final int itemPosition = parent.getChildAdapterPosition(view);
            outRect.set(0, 0, vertOverlap, 0);
        }
    }
}
activity_recycler_view
<?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:background="?attr/colorBackgroundFloating"
    android:orientation="vertical">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/myRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="250dp"
         />
</LinearLayout>
DataAdapter
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
    private Context context;
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    public DataAdapter(Context context, ArrayList<Integer> imagesArray) {
        this.context = context;
        arrayList = imagesArray;
    }
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View imageLayout = LayoutInflater.from(context).inflate(R.layout.aa, parent, false);
        return new ViewHolder(imageLayout);
    }
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.imageView.setImageResource(arrayList.get(position));
        if (position == 3) {
            holder.relativeLayout.setVisibility(View.VISIBLE);
            holder.tvCount.setText(String.valueOf(arrayList.size()-4));
        }else {
            holder.relativeLayout.setVisibility(View.GONE);
        }
    }
    @Override
    public int getItemCount() {
        return 4;
    }
    public class ViewHolder extends RecyclerView.ViewHolder {
        RelativeLayout relativeLayout;
        TextView tvCount;
        CircleImageView imageView;
        public ViewHolder(View itemView) {
            super(itemView);
            relativeLayout = itemView.findViewById(R.id.relative);
            tvCount = itemView.findViewById(R.id.tvCount);
            imageView = itemView
                    .findViewById(R.id.profile_image);
        }
    }
}
layout.aa
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <de.hdodenhof.circleimageview.CircleImageView xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/profile_image"
        android:layout_width="96dp"
        android:layout_height="96dp"
        android:src="@drawable/kid"
        app:civ_border_color="#FF000000"
        app:civ_border_width="2dp" />
    <RelativeLayout
        android:id="@+id/relative"
        android:visibility="gone"
        android:layout_marginLeft="-40dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/imageCircleImageView"
            android:layout_width="96dp"
            android:layout_height="96dp"
            android:src="#919191"
            app:civ_border_color="#FF000000"
            app:civ_border_width="2dp"  />
        <TextView
            android:id="@+id/tvCount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/imageCircleImageView"
            android:layout_alignEnd="@id/imageCircleImageView"
            android:layout_alignStart="@id/imageCircleImageView"
            android:layout_alignTop="@id/imageCircleImageView"
            android:layout_gravity="center"
            android:gravity="center"
            android:padding="10dp"
            android:text=""
            android:textColor="#FFFFFF" />
    </RelativeLayout>
</LinearLayout>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With