RecyclerView Item does not wrap height of ListView - java

So I have this activity, which looks like this:
<android.support.constraint.ConstraintLayout 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"
tools:context="com.example.jonas.trainingslog1.WorkoutDataActivity">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout
...
</android.support.constraint.ConstraintLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/layout">
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
As you can see, the activity has an RecyclerView which items look like this:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp">
<ListView
android:id="#+id/lst"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
<Button
android:layout_width="match_parent"
android:layout_height="30dp"
app:layout_constraintTop_toBottomOf="#+id/lst"/>
The Height of the ListView in the RecyclerView Item should only be as tall as its content, so I set it to wrap_content. The problem is, this only works as long as I have set the height of the RecyclerView Item Layout to match_parent, as soon as I change it to wrap_content the ListView shows only one item. How ever I cant leave the height of the RecyclerView Item as match_parent because then every item of the RecyclerView has a lot of unused space.
Anyone hows how to fix this problem? I dont understand this behavior of the xml files. I would really appreciate help.

Add this to you listview...set the height of the listview dynamically
public static void setListViewHeightBasedOnChildren(final ListView listView) {
listView.post(new Runnable() {
#Override
public void run() {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
int listWidth = listView.getMeasuredWidth();
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(
View.MeasureSpec.makeMeasureSpec(listWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
totalHeight += listItem.getMeasuredHeight();
Log.d("listItemHeight " + listItem.getMeasuredHeight(), "********");
}
Log.d("totalHeight " + totalHeight, "********");
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = (totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)));
listView.setLayoutParams(params);
listView.requestLayout();
}
});
}
add this to your recyclerview adapter

One option is to use a vertical linear layout where you put all your content of the page. Then set the height of the views (including the recyclerview) to 0dp and add a layout weight.

Related

Make a Recyclerview of 9 items with GridLayoutManager fit the screen

I have a Grid that was generated by a RecyclerView and GridLayoutManager , so in my case I will always have only 9 items on the Grid, so I want them to fill the entire screen. Below is how they look on my screen:
This is the activity_main.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="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
then, the single_item.xml is :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:padding="1dp"
>
<ImageView
android:id="#+id/image_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
/>
</RelativeLayout>
How I attached the Gridlayoutmanager :
rv = findViewById(R.id.rv);
ImageAdapter imageAdapter = new ImageAdapter(allBitmaps, MainActivity.this);
mLayoutManager = new GridLayoutManager(MainActivity.this, 3);
rv.setLayoutManager(mLayoutManager);
rv.setAdapter(imageAdapter);
So I want the grids to fit well the entire screen, how best can this be done ?
Here is the scenario:
Measure the width & height of the RecycerView before attaching the adapter to it:
Get the desired RecyclerView item width & height by dividing the width & height of the previous step by 3; as you have 3 items horizontally and vertically.
Pass the item width & height to the RecyclerView adapter as parameters and store them as fields.
In activity onCreate():
recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if (recyclerView.getViewTreeObserver().isAlive())
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
float width = (float) recyclerView.getWidth() / 3;
float height = (float) recyclerView.getHeight() / 3;
ImageAdapter adapter = new ImageAdapter(context, width, height, allBitmaps);
recyclerView.setAdapter(adapter);
return true;
}
});
Set the ImageView width & height in the ViewHolder:
class ViewHolder extends RecyclerView.ViewHolder {
ImageView image;
public ViewHolder(#NonNull View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
image.getLayoutParams().width = (int) itemWidth;
image.getLayoutParams().height = (int) itemHeight;
}
}
I tested this with just an ImageView as a list item layout:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitXY" />
Portrait preview:
Landscape preview:
I think if you want those images to fill up the screen, you need to modify the width and height of the image inside your single item.xml

Dynamically adding views in grid layout doesn't take the size specified

I am creating an app in which I want to dynamically add items to grid layout and want them to look like this:
What I did to achieve this is given as follows:
activity_main.xml
<ScrollView 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:fillViewport="true"
tools:context=".MainActivity">
<LinearLayout
android:weightSum="1"
android:id="#+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<GridLayout
android:id="#+id/myGrid"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#FFFFFF"
android:layout_weight="1">
</GridLayout>
</LinearLayout>
</ScrollView>
item_layout.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="#+id/item"
android:background="#color/itemBackground"
android:layout_weight="4"
android:layout_margin="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/cImage"
android:layout_marginTop="20dp"
android:layout_marginRight="40dp"
android:layout_marginLeft="40dp"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:src="#drawable/icon"/>
<TextView
android:id="#+id/cText"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.25"
android:textSize="20sp"
android:layout_marginTop="10dp"
android:textAlignment="center"
android:layout_marginBottom="10dp"
android:text="ORAL CARE"/>
</LinearLayout>
MainActivity.java: (Code adding items in grid layout)
gridLayout = (GridLayout) findViewById(R.id.myGrid);
myArray = new String[]{"pink","blue","red","orange","green","purple","white","black","brown","other"};
int total = myArray.length;
int column = 2;
int row = total / column;
gridLayout.setAlignmentMode(GridLayout.ALIGN_BOUNDS);
gridLayout.setColumnCount(column);
gridLayout.setRowCount(row + 1);
LinearLayout linearLayoutItem;
Display display = getWindowManager().getDefaultDisplay();
int displayWidth = display.getWidth();
int displayHeight = display.getHeight();
for(int i =0, c = 0, r = 0; i < total; i++, c++)
{
if(c == column)
{
c = 0;
r++;
}
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.item_layout, gridLayout, false);
TextView cText = (TextView) v.findViewById(R.id.cText);
ImageView cImage = (ImageView) v.findViewById(R.id.cImage);
cText.setText(myArray[i]);
cImage.setImageResource(R.drawable.icon);
v.setMinimumWidth(displayWidth/2); // setting width and height
v.setMinimumHeight(displayWidth/2);
gridLayout.addView(v);
GridLayout.LayoutParams param =new GridLayout.LayoutParams();
param.height = GridLayout.LayoutParams.WRAP_CONTENT;
param.width = GridLayout.LayoutParams.WRAP_CONTENT;
param.rightMargin = 20;
param.topMargin = 20;
param.setGravity(Gravity.CENTER);
param.columnSpec = GridLayout.spec(c);
param.rowSpec = GridLayout.spec(r);
This code is adding the views in grid layout perfectly. But the problem is that it is not adjusting the size of view according to the screen, as shown:
I have tried to make the view's width equal to half the space of screen but it doesn't seem to work. The image which I am using in imageView is large but I have tried experimenting with the size of the imageView as well but it doesn't work.
What I want is to:
Adjust the views (the gray colored views with image and text) according to the size of the screen on which it is run. As width of grid layout is match_parent.
Height of the view to be in ratio with width of the view.
Size of the image in the view to be in ratio with the size of the view.
Thanks in advance. Any help would be appreciated.

Strange behavior of the RecyclerView

I have the following code for example.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<String> names = new ArrayList<>();
names.add("one");
names.add("two");
names.add("three");
names.add("four");
names.add("five");
names.add("six");
names.add("seven");
names.add("eight");
names.add("nine");
names.add("ten");
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
LinearLayoutManager llm = new LinearLayoutManager(this);
rv.setLayoutManager(llm);
RvAdapter adapter = new RvAdapter(names);
rv.setAdapter(adapter);
}
}
RvAdapter.java
public class RvAdapter extends RecyclerView.Adapter<RvAdapter.PersonViewHolder> {
public static final String TAG = RvAdapter.class.getSimpleName();
List<String> persons;
RvAdapter(List<String> persons) {
this.persons = persons;
}
#Override
public RvAdapter.PersonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new PersonViewHolder(v);
}
#Override
public void onBindViewHolder(RvAdapter.PersonViewHolder holder, int position) {
holder.name.setText(persons.get(position));
Log.d(TAG, "onBindViewHolder: " + position);
}
#Override
public int getItemCount() {
return persons.size();
}
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView name;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
name = (TextView) itemView.findViewById(R.id.name);
}
}
}
activity_main.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mrmayhem.myapplication.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<android.support.v7.widget.CardView
android:id="#+id/cv"
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
</LinearLayout>
It works so good and correct. When I scroll through the list I get successive messages from Adapter one by one in Logs: "onBindViewHolder: " + position. But my problem in next. When i try add some View above the RecyclerView, as shown in the code below. Adapter displays all calls of the method onBindViewHolder simultaneously. Wrap the RecyclerView in activity_main.xml by the next code.
activity_main.xml (with header)
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mrmayhem.myapplication.MainActivity">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HEADER" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</RelativeLayout>
RecyclerView is a ListView. It will only display the items that you can currently see, reusing parts as you scroll.
Now you wrap the RecyclerView in a LinearLayout within a NestedScrollView, what happens?
A NestedScrollView needs to know the size of the LinearLayout to handle the scrolling.
How does the LinearLayout know how long it should be, without knowing the RecyclerViews total length? It doesn't.
So the recyclerView gets completely inflated, all views at once, all in a long line. No recycling, no reuse. This is the phenomenon you experience.
Now the LinearLayout can properly tell the NestedScrollView it's height (the total height of the TextView and all the items of the RecyclerView) and the NestedScrollView can handle the scrolling.
This is why nested scrolling is always a bad idea. If possible, try to move your TextView as an item into the RecyclerView and get rid of the wrapping NestedScrollview.
e.g. as seen here: Is there an addHeaderView equivalent for RecyclerView?
Another option would be to remove the recyclerview from the scroll view and put it below. The header would not scroll then, though.
Unless you have a good reason to have a NestedScrollView in your hierarchy, you could achieve a TextView on top with a RecyclerView below with just this layout code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.mrmayhem.myapplication.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HEADER" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>

Make RelativeLayout scrollable

I want to make a scrollable RelativeLayout where I can create Buttons with a x and y positions and custom width and height.
Here is the code i got so far XML:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/buttonDel"
android:fillViewport="true">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/RL">
</RelativeLayout>
</LinearLayout>
</ScrollView>
Java:
RelativeLayout linearLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
linearLayout = (RelativeLayout) findViewById(R.id.RL);
addButton();
}
private void addButton() {
for(int i = 0; i < 20; i++)
for(int j = 0; j < 4; j++)
{
Button but = new Button(this);
but.setX(j * 200);
but.setY(i * 200);
but.setText("B" + i);
linearLayout.addView(but, 200, 200);
}
}
It result in something that i want except the scrolling part. I don't know why the ScrollView isn't working.
Screen so far.
After I changed ScrollView layout_height="match_parent" to "warp_content"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ScrollView01"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</RelativeLayout>
</ScrollView>
However the right way to build the requisite structure is to design a gridview or listview .
Use single child for scroll view to behave correctly.
Change the ScrollView height from "match_parent" to "wrap_content" this should fix the problem:
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/buttonDel"
android:fillViewport="true"
>
...

ListView and FrameLayout in ScrollView [BUG]

I have a main Layout including 2 layout, one containing a FrameLayout with a Fragment inside. And the other one with a LinearLayout containing a ListView. This is how look my main layout :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="#layout/map_layout" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_weight="1"></include>
<include layout="#layout/list_layout" android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_weight="6"></include>
</LinearLayout>
</ScrollView>
</LinearLayout>
The first Fragment contain a map and the second contain a custom ListView with its own listAdapter.
So here my problem, I know it's kind of triky to put a ListView in a ScrollView. But in my onCreateView of my ListFragment.java I put this :
list.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
But the ListView keep catch the scroll. At a moment, the ScrollView bug and put the map and le list down of an infite ScrollView.
How can I do to make it work ?
I finally find the solution of my problem.
I needed to extend my listView so there its only scrolled by the ScrollView. here the code I find :
Helper.java:
public class Helper {
public static void getListViewSize(ListView myListView) {
ListAdapter myListAdapter = myListView.getAdapter();
if (myListAdapter == null) {
//do nothing return null
return;
}
//set listAdapter in loop for getting final size
int totalHeight = 0;
for (int size = 0; size < myListAdapter.getCount(); size++) {
View listItem = myListAdapter.getView(size, null, myListView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
//setting listview item in adapter
ViewGroup.LayoutParams params = myListView.getLayoutParams();
params.height = totalHeight + (myListView.getDividerHeight() * (myListAdapter.getCount() - 1));
myListView.setLayoutParams(params);
}
}
and to implement it, you just need this line:
Helper.getListViewSize(myList);
I have found this solution here : http://www.androidhub4you.com/2012/12/listview-into-scrollview-in-android.html

Categories