Created
October 22, 2018 10:35
-
-
Save salmaanahmed/602aa264f1b0e66758808eb8c0607596 to your computer and use it in GitHub Desktop.
FlagChatAdapter Gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
android:id="@+id/root_linear" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:animateLayoutChanges="true" > | |
<LinearLayout | |
android:id="@+id/linearLayout" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:layout_marginHorizontal="16dp" | |
android:orientation="vertical"> | |
<View | |
android:id="@+id/flagPadding" | |
android:layout_marginTop="8dp" | |
android:layout_width="match_parent" | |
android:layout_height="35dp" /> | |
<TextView | |
android:id="@+id/message" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:textSize="12sp" | |
android:paddingTop="5dp" | |
android:paddingHorizontal="10dp" | |
android:text="Lorem ipsum." | |
android:textColor="@android:color/black" /> | |
<TextView | |
android:id="@+id/time" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:text="10:00am" | |
android:paddingHorizontal="10dp" | |
android:textSize="8sp" /> | |
</LinearLayout> | |
<FrameLayout | |
android:id="@+id/line" | |
android:layout_height="0dp" | |
android:layout_width="3dp" | |
android:layout_marginHorizontal="8dp" | |
android:layout_alignTop="@+id/linearLayout" | |
android:layout_alignBottom="@+id/linearLayout" | |
android:background="@color/orange" /> | |
<TextView | |
android:id="@+id/name" | |
android:layout_width="wrap_content" | |
android:layout_height="35dp" | |
android:gravity="center" | |
android:layout_margin="8dp" | |
android:background="@color/orange" | |
android:paddingVertical="10dp" | |
android:paddingHorizontal="20dp" | |
android:textSize="14sp" | |
android:text="Name" | |
android:textColor="@android:color/white" /> | |
</RelativeLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
android:id="@+id/root_linear" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:animateLayoutChanges="true"> | |
<View | |
android:layout_width="match_parent" | |
android:layout_height="0.5dp" | |
android:layout_marginLeft="@dimen/textview_padding" | |
android:layout_toLeftOf="@+id/date" | |
android:layout_centerVertical="true" | |
android:background="@android:color/darker_gray" /> | |
<View | |
android:layout_width="match_parent" | |
android:layout_height="0.5dp" | |
android:layout_marginRight="@dimen/textview_padding" | |
android:layout_toRightOf="@+id/date" | |
android:layout_centerVertical="true" | |
android:background="@android:color/darker_gray" /> | |
<TextView | |
android:id="@+id/date" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="Yesterday" | |
android:layout_centerInParent="true" | |
android:padding="@dimen/textview_padding" | |
android:textSize="12sp" /> | |
</RelativeLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sasliderdemo.salmaan.ahmsal.com.flagchatadapter | |
import android.content.Context | |
import android.support.v4.content.ContextCompat | |
import android.view.View | |
import android.widget.Toast | |
import java.util.* | |
/** | |
* Created by salmaanahmed on 04/09/2018. | |
* Chat Adapter extended by FlagChatAdapter | |
*/ | |
class ChatAdapter(context: Context, private var list: ArrayList<Any>) : FlagChatAdapter(context) { | |
/** | |
* Name of the person user is chatting with | |
*/ | |
override val otherName: String get() = "John" | |
/** | |
* Size of list i.e. number of messages | |
*/ | |
override val listSize: Int get() = list.size | |
/** | |
* Message on this index | |
*/ | |
override fun chatMessage(position: Int): String { | |
return (list[position] as ChatModel).message | |
} | |
/** | |
* Message time on this index | |
*/ | |
override fun messageTime(position: Int): String { | |
return (list[position] as ChatModel).time | |
} | |
/** | |
* Message sender on this index | |
*/ | |
override fun isMe(position: Int): Boolean { | |
return (list[position] as ChatModel).isMe | |
} | |
/** | |
* Animation on this index | |
*/ | |
override fun animation(position: Int): Boolean { | |
return (list[position] as ChatModel).animate | |
} | |
/** | |
* Set animation status on this index | |
*/ | |
override fun setAnimationStatus(position: Int, animationStatus: Boolean) { | |
(list[position] as ChatModel).animate = animationStatus | |
} | |
/** | |
* Object type on this index | |
*/ | |
override fun isChatModel(position: Int): Boolean { | |
return list[position] is ChatModel | |
} | |
/** | |
* Date string on this index | |
*/ | |
override fun date(position: Int): String { | |
val item = list[position] as Calendar | |
return when { | |
item.isToday() -> "Today" | |
item.isYesterday() -> "Yesterday" | |
else -> item.toDateLong() | |
} | |
} | |
/** | |
* Bubble color of other person | |
*/ | |
override fun colorOther(context: Context): Int { | |
return ContextCompat.getColor(context, R.color.red) | |
} | |
/** | |
* Bubble color of user | |
*/ | |
override fun colorMe(context: Context): Int { | |
return ContextCompat.getColor(context, R.color.cyan) | |
} | |
/** | |
* Handle long click event | |
*/ | |
override fun onMessageLongClicked(position: Int) { | |
Toast.makeText(context, "Long clicked on position $position", Toast.LENGTH_LONG).show() | |
} | |
/** | |
* If you want to change show time logic, you can override this method. | |
* I prefer the logic currently implemented in adapter | |
*/ | |
override fun showTime(position: Int): Boolean { | |
return super.showTime(position) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sasliderdemo.salmaan.ahmsal.com.flagchatadapter | |
import android.content.Context | |
import android.support.v4.content.ContextCompat | |
import android.support.v7.widget.RecyclerView | |
import android.view.Gravity | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.view.animation.Animation | |
import android.view.animation.AnimationUtils | |
import android.widget.LinearLayout | |
import android.widget.RelativeLayout | |
import kotlinx.android.synthetic.main.cell_chat.view.* | |
import kotlinx.android.synthetic.main.cell_chat_date.view.* | |
/** | |
* Created by salmaanahmed on 04/09/2018. | |
* Extend your recycler view adapter with this adapter and voila! | |
* You have your animated chat adapter working. | |
*/ | |
abstract class FlagChatAdapter(val context: Context) : RecyclerView.Adapter<FlagChatAdapter.ViewHolder>() { | |
private val chatView = 0 //View types - Chat View | |
private val dateView = 1 //View types - Date View | |
/** | |
* return chat message on the position passed as parameter | |
*/ | |
abstract fun chatMessage(position: Int): String | |
/** | |
* return time of message as string format on the position passed as parameter | |
*/ | |
abstract fun messageTime(position: Int): String | |
/** | |
* return message sender on the position passed as parameter | |
* if its you, return true | |
*/ | |
abstract fun isMe(position: Int): Boolean | |
/** | |
* you must have a variable of animation in the object i.e. if you want to animate or not | |
*/ | |
abstract fun animation(position: Int): Boolean | |
/** | |
* the animation variable must be set to false when animation is performed once | |
* otherwise flags will animate on every scroll | |
*/ | |
abstract fun setAnimationStatus(position: Int, animationStatus: Boolean) | |
/** | |
* You can implement whatever you want onLongClick event | |
*/ | |
abstract fun onMessageLongClicked(position: Int) | |
/** | |
* Name of the sender | |
*/ | |
abstract val otherName: String | |
/** | |
* You shall simply return list.size | |
*/ | |
abstract val listSize: Int | |
/** | |
* OPTIONAL: | |
* you can change flag color of your chat message | |
*/ | |
open fun colorMe(context: Context): Int { | |
return ContextCompat.getColor(context, R.color.orange) | |
} | |
/** | |
* OPTIONAL: | |
* you can change flag color of other person's chat message | |
*/ | |
open fun colorOther(context: Context): Int { | |
return ContextCompat.getColor(context, R.color.green) | |
} | |
/** | |
* OPTIONAL: | |
* If your list contains some other data type from chat model i.e. date | |
* you must return false in that case so adapter can check for date and display the date | |
* chat message functions will not be called on the position if !isChatModel | |
*/ | |
open fun isChatModel(position: Int): Boolean { | |
return true | |
} | |
/** | |
* OPTIONAL: | |
* If !isChatModel adapter will display date place holder | |
* it will be populated with the string returned by this function | |
* you shall return the date or strings such as TODAY or YESTERDAY in this function | |
*/ | |
open fun date(position: Int): String { | |
return "" | |
} | |
/** | |
* OPTIONAL: | |
* This current code will not display time if same user sends message in same time | |
* You may override this method to change the logic or simply return true if you always want to show time | |
*/ | |
open fun showTime(position: Int): Boolean { | |
if (position > 0) { | |
if (isChatModel(position) && isChatModel(position - 1)) { | |
if (isMe(position) && isMe(position - 1) && messageTime(position) == messageTime(position - 1)) { | |
return false | |
} | |
} | |
} | |
return true | |
} | |
/** | |
* Overriden function to bind view holder | |
* Decide to show the chat view or date view | |
* Populate data in views and show animation | |
*/ | |
override fun onBindViewHolder(holder: ViewHolder, position: Int) { | |
if (isChatModel(position)) { // If it is chat model | |
val chatMessage = chatMessage(position) | |
val showTime = showTime(position) | |
val messageTime = messageTime(position) | |
val isMe = isMe(position) | |
val animate = animation(position) | |
// Bind data with viewholder | |
holder.bindData(chatMessage, showTime, messageTime, isMe, colorMe(context), colorOther(context), otherName) | |
// If previous message is from same person, hide the flag | |
if (position > 0 && isChatModel(position - 1) && isMe == isMe(position - 1)) { //Hide Flag | |
holder.itemView.name.visibility = View.GONE | |
holder.itemView.flagPadding.visibility = View.GONE | |
} else { // Otherwise show flag | |
if (animate) {// If flag is not animated before | |
holder.itemView.name.visibility = View.INVISIBLE | |
holder.itemView.flagPadding.visibility = View.VISIBLE | |
flagAnimation(holder.itemView, position) | |
} else { // Do not animate flag if its animated before i.e. if user is scrolling list | |
holder.itemView.name.visibility = View.VISIBLE | |
holder.itemView.flagPadding.visibility = View.VISIBLE | |
} | |
} | |
holder.itemView.rootView.setOnLongClickListener { | |
onMessageLongClicked(position) | |
return@setOnLongClickListener true | |
} | |
} else { | |
// If it is date, populate the textview | |
holder.itemView.date.text = date(position) | |
} | |
} | |
/** | |
* Item view type on basis of chat model | |
*/ | |
override fun getItemViewType(position: Int): Int { | |
return if (isChatModel(position)) chatView else dateView | |
} | |
/** | |
* Create view holder for both type of objects | |
*/ | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { | |
return if (viewType == chatView) { | |
val v = LayoutInflater.from(parent.context).inflate(R.layout.cell_chat, parent, false) | |
ViewHolder(v) | |
} else { | |
val v = LayoutInflater.from(parent.context).inflate(R.layout.cell_chat_date, parent, false) | |
ViewHolder(v) | |
} | |
} | |
/** | |
* Number of messages | |
*/ | |
override fun getItemCount(): Int { | |
return listSize | |
} | |
/** | |
* ViewHolder to cache the cells to optimize performance | |
* Populate the data, set colors, flags, animation etc. | |
*/ | |
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | |
fun bindData(message: String, showTime: Boolean = true, time: String, isMe: Boolean, colorMe: Int, colorOther: Int, otherName: String) { | |
setSides(isMe, colorMe, colorOther) | |
setMessage(message, showTime, time, isMe, otherName) | |
} | |
// Set sides, colors, and views according to sender | |
private fun setSides(isMe: Boolean, colorMe: Int, colorOther: Int) { | |
if (isMe) { | |
(itemView.line.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_RIGHT) | |
(itemView.name.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_RIGHT) | |
(itemView.message.layoutParams as LinearLayout.LayoutParams).gravity = Gravity.RIGHT | |
(itemView.message.layoutParams as LinearLayout.LayoutParams).leftMargin = itemView.context.resources.getDimensionPixelSize(R.dimen.message_padding) | |
(itemView.message.layoutParams as LinearLayout.LayoutParams).rightMargin = 0 | |
itemView.line.setBackgroundColor(colorMe) | |
itemView.name.setBackgroundColor(colorMe) | |
itemView.linearLayout.gravity = Gravity.RIGHT | |
itemView.name.gravity = Gravity.RIGHT | |
itemView.time.gravity = Gravity.RIGHT | |
} else { | |
(itemView.line.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0) | |
(itemView.name.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0) | |
(itemView.message.layoutParams as LinearLayout.LayoutParams).gravity = Gravity.LEFT | |
(itemView.message.layoutParams as LinearLayout.LayoutParams).rightMargin = itemView.context.resources.getDimensionPixelSize(R.dimen.message_padding) | |
(itemView.message.layoutParams as LinearLayout.LayoutParams).leftMargin = 0 | |
itemView.line.setBackgroundColor(colorOther) | |
itemView.name.setBackgroundColor(colorOther) | |
itemView.linearLayout.gravity = Gravity.LEFT | |
itemView.name.gravity = Gravity.LEFT | |
itemView.time.gravity = Gravity.LEFT | |
} | |
} | |
// Populate message, date, sender in the views | |
private fun setMessage(message: String, showTime: Boolean, time: String, isMe: Boolean, otherName: String) { | |
itemView.message.text = message | |
itemView.time.text = time | |
itemView.name.text = if (isMe) "Me" else otherName | |
if (showTime) itemView.time.visibility = View.VISIBLE | |
else itemView.time.visibility = View.GONE | |
} | |
} | |
/** | |
* Flag animation on the views | |
*/ | |
private fun flagAnimation(itemView: View, position: Int) { | |
val translateAnim = AnimationUtils.loadAnimation(itemView.context, R.anim.item_animation_from_bottom) | |
translateAnim.fillAfter = true | |
translateAnim.isFillEnabled = true | |
translateAnim.fillBefore = false | |
translateAnim.setAnimationListener(object : Animation.AnimationListener { | |
override fun onAnimationStart(animation: Animation) { | |
itemView.linearLayout.alpha = 0f | |
itemView.name.visibility = View.VISIBLE | |
} | |
override fun onAnimationEnd(animation: Animation) { | |
itemView.linearLayout.animate().alpha(1.0f).duration = 1000 | |
setAnimationStatus(position, false) | |
} | |
override fun onAnimationRepeat(animation: Animation) {} | |
}) | |
itemView.name.startAnimation(translateAnim) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<set xmlns:android="http://schemas.android.com/apk/res/android"> | |
<translate | |
android:duration="1000" | |
android:fillAfter="true" | |
android:fromYDelta="99%p" | |
android:toYDelta="0%p" /> | |
</set> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment