I have several TextViews inside ConstraintLayout. The visibility of those TextViews are set at runtime based upon the data availability.
I need to bold the text of the first visible TextView.
I have tried many things but couldn't resolve the issue.
I have tried:
Looping through the child inside the parent layout and
finding the first child and making it bold. This approach always
finds the first child inside the parent layout regardless of it's
visiblility.
I put a check before retrieving the first element if view.visibility
== View.VISIBLE and also I checked view.isShown, both way again returns the first child in the view hierarchy irrespective of the visibility. So, view.visibility always returns View.VISIBLE.
What am I missing and how can I make it work?
Providing sample codes:
XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="MyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="First textview"
android:textColor="#color/black"
android:visibility="#{viewModel.textView1Visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Second textview"
android:textColor="#color/black"
android:visibility="#{viewModel.textView2Visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textView1" />
<TextView
android:id="#+id/textView3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Third textview"
android:textColor="#color/black"
android:visibility="#{viewModel.textView3Visibility}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textView2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
CODE
binding.apply {
/** We have three textviews.
* textView1 doesn't have data so we are hiding it.
* textView2 has data so, we need to show it.
* and textView2 will be the first item, we need to bold it as well. */
textView1.visibility = View.GONE
/** looping through the children of root layout and bolding the first visible */
rootLayout.forEach exit# { view ->
if(view.isVisible || view.isShown) {
(view as TextView).typeface = Typeface.DEFAULT_BOLD
return#exit
}
}
}
Your code is looping through all the children of the ConstraintLayout because return#exit acts like a continue and not a true break.
Try the following:
run exit#{
rootLayout.forEach { view ->
if (view.isVisible || view.isShown) {
(view as TextView).typeface = Typeface.DEFAULT_BOLD
return#forEach
}
}
}
There may be a more elegant way to go about this, but the above should work.
Update to code:
rootLayout.children.firstOrNull { view ->
view.isVisible || view.isShown
}?.also { view ->
(view as TextView).typeface = Typeface.DEFAULT_BOLD
}
I will also mention that your code will find the first visible child of the ConstraintLayout child order, but that view may not be the view that one would pick as the first looking at the screen. That is because the view order within the children of ConstraintLayout may differ from the on-screen order due to layout constraints. This may not be a problem for you, but it is something to think about.
Related
In my application I have recyclerView and for this I should use GridLayoutManager and set 2 items per column.
I want set this 2 items fit of screen, each items sticky to screen. Such as below image:
I write below codes, but after run application show me such as this :
And I want set 120dp for layout_width and layout_height!
My XML code :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/rowMasterQuestion_root"
android:layout_width="#dimen/_120mdp"
android:layout_height="#dimen/_120mdp"
android:background="#drawable/gray_border">
<TextView
android:id="#+id/rowMasterQuestion_title"
android:layout_width="#dimen/_120mdp"
android:layout_height="wrap_content"
android:layout_margin="#dimen/_15mdp"
android:gravity="center_horizontal"
android:textColor="#color/black"
android:textSize="#dimen/_9font_mdp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/rowMasterQuestion_answer"
android:layout_width="wrap_content"
android:layout_height="#dimen/_15mdp"
android:layout_marginLeft="#dimen/_10mdp"
android:layout_marginRight="#dimen/_10mdp"
android:layout_marginBottom="#dimen/_20mdp"
android:gravity="center"
android:paddingLeft="#dimen/_5mdp"
android:paddingRight="#dimen/_5mdp"
android:textColor="#color/white"
android:textSize="#dimen/_8font_mdp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
My Activity codes :
requestMain_childList.apply {
layoutManager = GridLayoutManager(this#RequestMainPage, 2, GridLayoutManager.VERTICAL, false)
hasFixedSize()
adapter = masterChildAdapter
}
How can I fix this issue?
This is tricky because you are wanting a different layout gravity for each item, depending on the column. I think that will have to be set in the RecyclerView.Adapter.onBindViewHolder function.
First, wrap the ConstraintLayout of your item view in a FrameLayout. Make the width of the outer FrameLayout match_parent so it will fill its column in the RecyclerView. You can also give the FrameLayout the amount of start/end padding you want to hold the items away from the sides of the screen.
<FrameLayout 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="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/wrappedView"
android:layout_width="#dimen/_120mdp"
android:layout_height="#dimen/_120mdp"
...>
...
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
Then in onBindViewHolder, you can set the gravity of the inner view to the left or right depending on which column it is in (even or odd data position).
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
// ...
val innerView = holder.wrappedView // or however you have it stored in view holder
(innerView.layoutParams as FrameLayout.LayoutParams).gravity = Gravity.CENTER_VERTICAL or
(if (position % 2 == 0) Gravity.START else Gravity.END)
}
Note, if you want the items to be centered in their columns instead, this is much easier. You can just set the gravity of the inner view to center in the XML and you don't have to do anything in the Adapter.
I have to hide Particular views in recyclerview, I am trying to hide some views but view has gone but space is there,I have tried putting heights=0 in cardview but still did not work , what I should I do?
This is my XML code list data
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-
auto">
<android.support.v7.widget.CardView
android:id="#+id/child_card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2sp"
app:cardElevation="12sp"
android:visibility="gone">
<LinearLayout
android:id="#+id/main_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="vertical"
android:padding="3dp"
android:visibility="visible">
<ImageView
android:id="#+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
app:srcCompat="#drawable/bsp_icon" />
<android.support.v7.widget.AppCompatTextView
android:id="#+id/label_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:lines="2"
android:maxLines="2"
android:padding="0dp"
android:textColor="#color/Black" />
</LinearLayout>
</android.support.v7.widget.CardView>
</layout>
and this is my recyclerview
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
tools:listitem="#layout/list_layout_dashboard_child_view">
</android.support.v7.widget.RecyclerView>
this is my Adapter code
if (txnTypeMap.get("CUST_CR") != null &&
obj.getChildName().equalsIgnoreCase(txnTypeMap.get("CUST_CR"))) {
binding.childCardView.setVisibility(View.VISIBLE);
}
important always set this if you are sure your data will change in recyclerview mRecyclerView.setHasFixedSize(false);
first remove the item from list e.g. list.remove(position which you want to hide)
next call notifyItemChanged(postion) and it should work ->this is better approach than calling notifyDataSetChanged()
You should hide all views or parent from UsersViewholder layout xml. You should hide entire viewholder or each view
Entire viewholder:
itemView.setVisibility(View.GONE);
or each element:
view.setVisibility(View.GONE);
But don't forget to set them VISIBLE otherwise, you will end up with some strange things from recycling
Without setting setVisibility to View.GONE, try to remove the the data from the data source and call notifyDataSetChanged()
This is the more efficient way to hide a view from RecyclerView
you have to set visibility gone to view in else case because views are reused in recycler view
if (txnTypeMap.get("CUST_CR") != null &&
obj.getChildName().equalsIgnoreCase(txnTypeMap.get("CUST_CR"))) {
binding.childCardView.setVisibility(View.VISIBLE);
} else binding.childCardView.setVisibility(View.GONE);
I hope it will work.
I am trying to make a chat application. Where I have different layouts for receive messages and send messaged. I am not able to get smooth scroll maybe because of variable image size? I think this issue related to layout I have made for the chat message box.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content" xmlns:tools="http://schemas.android.com/tools"
android:layout_marginTop="11dp"
android:layout_marginRight="16dp"
android:gravity="right"
>
<android.support.v7.widget.AppCompatTextView
android:id="#+id/tv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="11dp"
android:paddingVertical="9dp"
android:visibility="gone"
tools:text="fdsfdsfsdfs"
tools:visibility="gone"
android:textColor="#2A2617"
android:textSize="15dp"
android:fontFamily="#font/nunito_regular"
/>
<android.support.v7.widget.AppCompatImageView
android:id="#+id/iv_message"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#+id/tv_message"
android:layout_width="200dp"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:padding="11dp"
android:visibility="gone" />
</RelativeLayout>
It flickers when i scroll up. Code of View Holder is as follow
when(data.messageType){
Constant.MessageType.TEXT ->{
messageIv.visibility = View.GONE
messageTv.apply {
visibility = View.VISIBLE
text = data.content
}
}
Constant.MessageType.IMAGE -> {
Glide.with(messageIv.context)
.load(data.content)
.apply(
RequestOptions()
.apply {
diskCacheStrategy(DiskCacheStrategy.ALL)
skipMemoryCache(true)
})
.into(messageIv)
messageIv.visibility = View.VISIBLE
messageTv.apply {
visibility = View.GONE
}
}
}
I guess it call bind every time i scroll and it makes the visibility gone and visible. Which cause the view to have 0dp size for some amount of time and suddenly when image loads, it creates a sudden scroll in the view making it very unstable.
Any work-around for this?
I've spent the last 2 days attempting to triage why my RecyclerView is so is so unbearably slow while scrolling and I've narrowed it down to the ConstraintLayout I'm using for the rows. Using the GPU profiler on android shows green/blueish green bars all the way up to the top of the screen, indicating substantial jank. It's super obvious something is wrong.
Here's what my viewholder looks like:
class MyViewHolder(
override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bindTo(item: Item?, clickListener: (Item?) -> Unit) {
text_item_name.text = item?.name
Glide.with(containerView.context).load(item?.url).into(image_item)
containerView.setOnClickListener { clickListener(item) }
}
}
And here's what my layout looks like. Simple as can be:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:paddingBottom="#dimen/padding"
android:paddingEnd="#dimen/padding_2x"
android:paddingStart="#dimen/padding_2x"
android:paddingTop="#dimen/padding">
<ImageView
android:id="#+id/image_item"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="#dimen/margin_2x"
android:layout_marginStart="#dimen/margin_2x"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/text_coin_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/margin_2x"
android:layout_marginStart="#dimen/margin_2x"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#id/image_item"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
So my question is this: what is wrong with my layout that's causing the jank? Am I using an incorrect LayoutManager? Are the constraints causing overdraws?
If I completely rewrite the layout XML to be LinearLayout based, it's smooth as silk.
Well, I finally found the culprit:
android:layout_height="wrap_content"
If I specify a fixed size for this attribute all is well. The constant calculations to determine view height for each row were likely causing the issues.
I am creating app in which there is screen with searchBar by this lib.
My activity layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
android:id="#+id/wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<TextView
android:id="#+id/title"
android:textColor="#color/colorPrimary"
android:text="#string/mainScreenTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:paddingTop="100dp"/>
<com.arlib.floatingsearchview.FloatingSearchView
android:id="#+id/searchView"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="100dp"
android:animateLayoutChanges="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title"/>
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
There i have to move FloatingSearchingView to top of screen and change height to for example 500dp - like this:
app:layout_constraintTop_toTopOf="parent"
android:layout_height="500dp"
And later i will have to move it back and change height again to default value
app:layout_constraintTop_toBottomOf="#+id/title"
android:layout_height="50dp"
The problem is, i have to animate it. I have spent a lot of time searching for solution but unfortunately i didnt find anything.
I had to animate the TextView height inside a ConstraintLayout.
<androidx.constraintlayout.widget.ConstraintLayout
...
android:animateLayoutChanges="true"
>
<TextView
...
I first informed the ConstraintLayout that the layout was about to change:
constraintlayout.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
and then updated the textView height (or whatever other param)
textView.getLayoutParams().height = 200;
textView.requestLayout();
for something simple as that, no need to set animators or involve other libraries.
#beeb's version didn't work for me either, unfortunately. Although the question is old, here is a solution that worked for me, using the TransitionManager. It's a bit "redundant", as you have to duplicate some code, but therefor very easy and includes only two basic steps:
Create two layout.xml files. One for each state (before/after animation).
If your constraint layout is one of multiple children of a parent layout, you can create two files containing only this constraint layout and include the version you wan't at app-start via <include #layout=""/>, where previously you constraint layout has been. That way, you don't have to duplicate the entire layout file. This however caused the findViewById() method to return null, when my include element had an id. So be careful with this.
In your case this would be e.g.: floating_searching_view_at_bottom.xml:
<com.arlib.floatingsearchview.FloatingSearchView
android:id="#+id/searchView"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="100dp"
android:animateLayoutChanges="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title"
android:layout_height="50dp" />
and floating_searching_view_at_top.xml:
<com.arlib.floatingsearchview.FloatingSearchView
android:id="#+id/searchView"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginTop="100dp"
android:animateLayoutChanges="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title"
android:layout_height="50dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_height="500dp" />
Animate the transition between these layout files using TransitionManager:
boolean toggle = false // you should give this guy a more descriptive name!
Context context = this; // in scope of the activity.
public void animateTransition() {
// Load ConstraintSets from the two layouts
ConstraintSet atBottom = new ConstraintSet();
ConstraintSet atTop = new ConstraintSet();
atBottom.clone(context, R.layout.floating_searching_view_at_bottom);
atTop.clone(context, R.layout.floating_searching_view_at_top);
// apply ConstraintSet depending on "toggle"
ConstraintSet newLayout = (toggle) ? expanded : collapsed;
TransitionManager.beginDelayedTransition(constraintLayout);
newLayout.applyTo(constraintLayout);
// invert the toggle
toggle = !toggle;
}
Note:
1. Make sure context is of type Activity and you're using an
AppCompat theme. I accidentally used getApplicationContext(), what
obviously resulted in a crash.
2. If you are receiving an NPE on your constraintk
Try to use Animation class for this:
Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LayoutParams params = yourView.getLayoutParams();
// modify your layout params here
yourView.setLayoutParams(params);
}
};
animation.setDuration(500); // in ms
yourView.startAnimation(animation);