My RecyclerView is having some elements in it. Every second element should be painted in a different color. This is my function.
val BackgroundElemnt: LinearLayout = itemView.findViewById(R.id.container1)
fun bind(currencyPost: CurrencyPost){
if (BackgroundElemnt.get(position)%2 == 0){
BackgroundElemnt.setBackgroundColor(0xB8B8B8)
}
else
{
BackgroundElemnt.setBackgroundColor(0xFFFFFF)
}
CName.text = currencyPost.Name
CValue.text = currencyPost.Value
}
Solution 1:
you should try this
val backgroundElement: LinearLayout = itemView.findViewById(R.id.container1)
fun bind(currencyPost: CurrencyPost){
if (position%2 == 0){
backgroundElement.setBackgroundColor(0xB8B8B8)
}
else
{
backgroundElement.setBackgroundColor(0xFFFFFF)
}
CName.text = currencyPost.Name
CValue.text = currencyPost.Value
}
I replaced if (BackgroundElemnt.get(position)%2 == 0) with if (position%2 == 0) because you're checking for the position of the item in the adapter not the position of the root view.
Solution 2:
You should create a flag in your model that decides what the colour should be, then you should modify your list item set the colour for every second item.
Example:
data class CurrencyPost(val id: Int, val name: String, val hasDifferentColour: Boolean = false)
assuming that is the model of the item, you should then convert your list item to indicate what colour it should be. So before you call adapter.submitList(listOfCurrencyPost)
you should map the items to show the colour
so
val modifiedListOfCountryPost =
listOfCurrencyPost.mapIndexed { countryPost, index ->
if (index % 2 == 0) countryPost.copy(hasDifferentColour = true)
}
then adapter.submit(modifiedListOfCountryPost)
in the onBind method of your adapter you should then do this
fun bind(currencyPost: CurrencyPost){
if (countryPost.hasDifferentColur){
BackgroundElemnt.setBackgroundColor(0xB8B8B8)
}else
{
BackgroundElemnt.setBackgroundColor(0xFFFFFF)
}
CName.text = currencyPost.Name
CValue.text = currencyPost.Value
}
Integer timeOut = 1000;
public void AlterColor(){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// put your condition statement here
finish();
}
},timeOut);
try this out...placae it in your class and call it within the main method.
When I use this, it removes one element with animation
{
notificationItems.remove(0);
adapterForNotification.notifyItemRemoved(0);
adapterForNotification.notifyItemRangeRemoved(0,count-1);
}
But, when I use this, it removes all element without animation
count = adapter.getItemCount();
for(int i = 0 ; i < count; ++i){
notificationItems.remove(0);
adapterForNotification.notifyItemRemoved(0);
adapterForNotification.notifyItemRangeRemoved(0,count-1)
}
As I can understand, you are able to remove items, but you need to add sort of animation while removing.
It can be done by deleting a single item at a time with a single animation for each item.
For instance by simulating a swipe animation on an item at a time, and post a delay before deleting the next item, and so on to the way down to the last item of the RecyclerView
Steps:
Step No.1:
In your activity that holds the clear all button and the RecyclerView instance: Create a method of single item delete
private void deleteItem(View rowView, final int position) {
Animation anim = AnimationUtils.loadAnimation(requireContext(),
android.R.anim.slide_out_right);
anim.setDuration(300);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
if (myDataSource.size() == 0) {
addEmptyView(); // adding empty view instead of the RecyclerView
return;
}
myDataSource.remove(position); //Remove the current content from the array
myRVAdapter.notifyDataSetChanged(); //Refresh list
}
}, anim.getDuration());
}
Step No.2:
Create the method that will delete all RecyclerView list items >> call it in your button click callback.
boolean mStopHandler = false;
private void deleteAllItems() {
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (myDataSource.size() == 0) {
mStopHandler = true;
}
if (!mStopHandler) {
View v = myRecyclerView.findViewHolderForAdapterPosition(0).itemView;
deleteItem(v, 0);
} else {
handler.removeCallbacksAndMessages(null);
}
handler.postDelayed(this, 250);
}
};
requireActivity().runOnUiThread(runnable);
}
Also it's important to handle configuration change in manifest, activity section, as if the configuration changes while clearing your recycler view list, an exception will be raised
<activity
android:name=".activities.MainActivity"
android:configChanges="orientation|screenSize|keyboard"
android:label="#string/app_name"
...
</activity>
You shouldn't be using both notifyItemRemoved() and notifyItemRangeRemoved(). Only use one at a time.
If you want to remove one item:
notificationItems.remove(index);
adapterForNotification.notifyItemRemoved(index);
If you want to remove all items:
int origCount = notificationItems.size();
notificationItems.clear();
adapterForNotification.notifyItemRangeRemoved(0, origCount - 1);
If you want to remove a range of items:
notificationItems.subList(startIndex, endIndex).clear();
adapterForNotification.notifyItemRangeRemoved(startIndex, endIndex);
EDIT:
If you want to remove each item one by one and show the removal animation for each, try this:
for (int i = 0; i < notificationItems.size(); i++) {
notificationItems.remove(i);
adapterForNotification.notifyItemRemoved(i);
}
count = adapter.getItemCount();
for (int i = 0 ; i < count; ++i){
notificationItems.remove(0);
}
adapterForNotification.notifyItemRangeRemoved(0, count-1)
I did this by accessing the views of the list items directly from the adapater of the recyclerview, animating them, and notifying the adapter after the last animation has played.
fun deleteMultipleAnimated(){
val oldSize = myList.size
myList.removeAt(3)
myList.removeAt(4)
val newSize = myList.size
for(i in newSize until oldSize){
val listItemView = myRecycler.findViewHolderForAdapterPosition(i) as MyAdapater.MyViewHolder
if(i == oldSize-1){
//only the last animation notifies the adapter
listItemView.myView.animate().scaleX(0f).setDuration(250).withEndAction{
myAdapter.notifyItemRangeRemoved(newSize, oldSize)}
}else{
//every other view is animated without an end action
listItemView.myView.animate().scaleX(0f).duration = 250
}
}
}
I am using https://github.com/florent37/TutoShowcase this showcaseview library in my code.
It works fine in activity and fragment.But when I call in recyclerview item it shows multiple popups and gets blackout.
mRecyclerViewList.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Logger.log("Call");
TextView textView = (TextView) mRecyclerViewList.getChildAt(0).findViewById(R.id.txt_add_tocart_btn);
Logger.log("Textview" + textView);
textView.setFocusableInTouchMode(true);
TutoShowcase.from((Activity) context).setContentView(R.layout.tuto_showcase_tuto_sample)
.setFitsSystemWindows(true).on(textView).addRoundRect(35).showOnce("1").show();
// unregister listener (this is important)
if (Build.VERSION.SDK_INT < 16) {
mRecyclerViewList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mRecyclerViewList.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
How can I avoid multiple popup's?
Your question how to avoid Multiple popup's:
Just set a boolean value to avoid showing multiple times.
boolean isShown = false;
mRecyclerViewList.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Logger.log("Call");
if(!isShown){
TextView textView = (TextView) mRecyclerViewList.getChildAt(0).findViewById(R.id.txt_add_tocart_btn);
Logger.log("Textview" + textView);
textView.setFocusableInTouchMode(true);
TutoShowcase.from((Activity) context).setContentView(R.layout.tuto_showcase_tuto_sample)
.setFitsSystemWindows(true).on(textView).addRoundRect(35).showOnce("1").show();
isShown = true;
}
// unregister listener (this is important)
if (Build.VERSION.SDK_INT < 16) {
mRecyclerViewList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mRecyclerViewList.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
As described in documentation of OnGlobalLayoutListener:
to be invoked when the global layout state or the visibility of views within the view tree changes.
That's why you got many and many showcase views. Every time ViewTree changed, you generate showcase.
You don't need for ViewTreeObserver and GlobalLayoutListener.
Move
TutoShowcase.from
to onViewCreated for example.
I'd like to be able to show or hide a tableview with the click of a button. I know how to set the visibility of the view, just not how to toggle back and forth.
Edit: Another option that seems to work for anyone else needing to do this:
showHide.Click += delegate
{
if (otherEquip.Visibility == ViewStates.Visible)
{
otherEquip.Visibility = ViewStates.Invisible;
}
else
{
otherEquip.Visibility = ViewStates.Visible;
}
};
There is no built in toggle method that I know of. There are also three visibility states a view can have - visible, invisible, gone - so "toggling" doesn't really work there. If you wanted to swap between invisible and visible, for example, you could do something like:
view.Visibility = view.Visibility == ViewStates.Invisible
? ViewStates.Invisible
: ViewStates.Visible;
Or if you wanted to make it more reusable you could put it in an extension method:
public static class ViewExtensions
{
public static void ToggleVisibility(this View view)
{
view.Visibility = view.Visibility == ViewStates.Invisible
? ViewStates.Invisible
: ViewStates.Visible;
}
}
and then call on the view:
view.ToggleVisibility();
I have a multi-line TextView that has android:ellipsize="end" set. I would like to know, however, if the string I place in there is actually too long (so that I may make sure the full string is shown elsewhere on the page).
I could use TextView.length() and find about what the approximate length of string will fit, but since it's multiple lines, the TextView handles when to wrap, so this won't always work.
Any ideas?
You can get the layout of the TextView and check the ellipsis count per line. For an end ellipsis, it is sufficient to check the last line, like this:
Layout l = textview.getLayout();
if (l != null) {
int lines = l.getLineCount();
if (lines > 0)
if (l.getEllipsisCount(lines-1) > 0)
Log.d(TAG, "Text is ellipsized");
}
This only works after the layout phase, otherwise the returned layout will be null, so call this at an appropriate place in your code.
textView.getLayout is the way to go but the problem with that is that it returns null if layout is not prepared. Use the below solution.
ViewTreeObserver vto = textview.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Layout l = textview.getLayout();
if ( l != null){
int lines = l.getLineCount();
if ( lines > 0)
if ( l.getEllipsisCount(lines-1) > 0)
Log.d(TAG, "Text is ellipsized");
}
}
});
Code snippet for removing the listener (source):
mLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
scrollToGridPos(getCenterPoint(), false);
mLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
I think the easiest solution to this question is the following code:
String text = "some looooong text";
textView.setText(text);
boolean isEllipsize = !((textView.getLayout().getText().toString()).equalsIgnoreCase(text));
This code assumes that in your XML the TextView set a maxLineCount :)
This worked to me:
textView.post(new Runnable() {
#Override
public void run() {
if (textView.getLineCount() > 1) {
//do something
}
}
});
The most eloquent solution I have found (in Kotlin) is to create an extension function on TextView
fun TextView.isEllipsized() = layout.text.toString() != text.toString()
This is great because it doesn't require knowing what the full string is or worrying about how many lines the TextView is using.
TextView.text is the full text that it's trying to show, whereas TextView.layout.text is what's actually shown on the screen so if they are different it must be getting ellipsized
To use it:
if (my_text_view.isEllipsized()) {
...
}
public int getEllipsisCount (int line):
Returns the number of characters to be ellipsized away, or 0 if no ellipsis is to take place.
So, simply call :
int lineCount = textview1.getLineCount();
if(textview1.getLayout().getEllipsisCount(lineCount) > 0) {
// Do anything here..
}
Since the getLayout() cant be called before the layout is set, use this:
ViewTreeObserver vto = textview.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Layout l = textview.getLayout();
if ( l != null){
int lines = l.getLineCount();
if ( lines > 0)
if ( l.getEllipsisCount(lines-1) > 0)
Log.d(TAG, "Text is ellipsized");
}
}
});
And finally do not forget to remove removeOnGlobalLayoutListener when you need it nomore.
lateinit var toggleMoreButton: Runnable
toggleMoreButton = Runnable {
if(reviewTextView.layout == null) { // wait while layout become available
reviewTextView.post(toggleMoreButton)
return#Runnable
}
readMoreButton.visibility = if(reviewTextView.layout.text.toString() != comment) View.VISIBLE else View.GONE
}
reviewTextView.post(toggleMoreButton)
It is some typical case:
comment in 'reviewTextView'
comment can collapsed by some criteria
if comment collapsed you show button 'readMoreButton'
The Kotlin way:
textView.post {
if (textView.lineCount > MAX_LINES_COLLAPSED) {
// text is not fully displayed
}
}
Actually View.post() is executed after the view has been rendered and will run the function provided
Simple Kotlin method. Allows android:ellipsize and android:maxLines to be used
fun isEllipsized(textView: TextView, text: String?) = textView.layout.text.toString() != text
Solution with kotlin extensions:
infoText.afterLayoutConfiguration {
val hasEllipsize = infoText.hasEllipsize()
...
}
Extensions:
/**
* Function for detect when layout completely configure.
*/
fun View.afterLayoutConfiguration(func: () -> Unit) {
viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
viewTreeObserver?.removeOnGlobalLayoutListener(this)
func()
}
})
}
fun TextView.hasEllipsize(): Boolean = layout.getEllipsisCount(lineCount - 1) > 0
it is working for me
if (l != null) {
int lines = l.getLineCount();
if (lines > 0) {
for (int i = 0; i < lines; i++) {
if (l.getEllipsisCount(i) > 0) {
ellipsize = true;
break;
}
}
}
}
If your textview contains multiple paragraphs, using getEllipsisCount will not work for empty lines within it. getEllipsisCount for the last line of any paragraph will return 0.
Really work so, for example, to pass full data to dialog from item of RecyclerView:
holder.subInfo.post(new Runnable() {
#Override
public void run() {
Layout l = holder.subInfo.getLayout();
if (l != null) {
final int count = l.getLineCount();
if (count >= 3) {
holder.subInfo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final int c = holder.subInfo.getLineCount();
if (c >= 3) {
onClickToShowInfoDialog.showDialog(holder.title.getText().toString(), holder.subInfo.getText().toString());
}
}
});
}
}
}
});
Combining #Thorstenvv awnser with #Tiano fix, here is the Kotlin version :
val layout = textView.layout ?: return#doOnLayout
val lines = layout.lineCount
val hasLine = lines > 0
val hasEllipsis = ((lines - 1) downTo 0).any { layout.getEllipsisCount(it) > 0 }
if (hasLine && hasEllipsis) {
// Text is ellipsized
}
In Kotlin, you can use the below code.
var str= "Kotlin is one of the best languages."
textView.text=str
textView.post {
val isEllipsize: Boolean = !textView.layout.text.toString().equals(str)
if (isEllipsize) {
holder.itemView.tv_viewMore.visibility = View.VISIBLE
} else {
holder.itemView.tv_viewMore.visibility = View.GONE
}
}
This is simple library for creating textview expandable. Like Continue or Less. This library extended version TextView. Easy to use.
implementation 'com.github.mahimrocky:ShowMoreText:1.0.2'
Like this,
1 https://raw.githubusercontent.com/mahimrocky/ShowMoreText/master/screenshot1.png
2 https://raw.githubusercontent.com/mahimrocky/ShowMoreText/master/screenshot2.png
<com.skyhope.showmoretextview.ShowMoreTextView
android:id="#+id/text_view_show_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/text"
/>
In Activity you can use like:
ShowMoreTextView textView = findViewById(R.id.text_view_show_more);
//You have to use following one of method
// For using character length
textView.setShowingChar(numberOfCharacter);
//number of line you want to short
textView.setShowingLine(numberOfLine);
After researching I found the best way for me in Kotlin
To get the ellipsize status the textView must be rendered first, so we have to set the text first, then check the ellipsize logic inside textView.post scope
textView.text = "your text"
textView.post {
var ellipsized: Boolean = textView.layout.text.toString()).equalsIgnoreCase("your text"))
if(ellipsized){
//your logic goes below
}
}
Using getEllipsisCount won't work with text that has empty lines within it. I used the following code to make it work :
message.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if(m.isEllipsized == -1) {
Layout l = message.getLayout();
if (message.getLineCount() > 5) {
m.isEllipsized = 1;
message.setMaxLines(5);
return false;
} else {
m.isEllipsized = 0;
}
}
return true;
}
});
Make sure not to set a maxLineCount in your XML. Then you can check for the lineCount in your code and if it is greater than a certain number, you can return false to cancel the drawing of the TextView and set the line count as well as a flag to save whether the text view is too long or not. The text view will draw again with the correct line count and you will know whether its ellipsized or not with the flag.
You can then use the isEllipsized flag to do whatever you require.
create a method inside your TextViewUtils class
public static boolean isEllipsized(String newValue, String oldValue) {
return !((newValue).equals(oldValue));
}
call this method when it's required eg:
if (TextViewUtils.isEllipsized(textviewDescription.getLayout().getText().toString(), yourModelObject.getDescription()))
holder.viewMore.setVisibility(View.VISIBLE);//show view more option
else
holder.viewMore.setVisibility(View.GONE);//hide
but textView.getLayout() can't call before the view(layout) set.