Two dynamic lists, adjusting to each other - android

I have to make two RecyclerViews, that can dynamically adjust its heights to each other. Assume i have a screen = 1/3 of its height is first RV and next 2/3 of its height us second RV. This is normal state for two populated lists. But if first RV doesn't have enough elements to fill 1/3 of screen - second RV becomes higher and if seconds RV doesn't have enough elements to fill 2/3 of screen height - first list become taller.
Is this theoretically possible? If yes, can you give me some clues to start digging?

Try this.
private int itemHeight = "height of item layout which u are inflating in the recycler view"
ViewGroup.LayoutParams params = recyclerView.getLayoutParams();
params.height = itemHeight * numberOfItemInList;
recyclerView.requestLayout();

Related

Android Row: How to make first and third views' widths equal to the widest of them, with the middle view expanding to fill the row

I am designing a title row that has the item title in the middle, with a status description on the left and expiration date description on the right. The title should be centered at all times and expand width until it runs into either of the side views, at which time it should get ellipsized. I'm having trouble figuring out how to keep the title view centered while allowing for the two side views to have different sizes, while also trying to show as much of the text as possible in cases where the side views have only two or three characters, or as many as 20.
This is NOT a basic question of having the center view fill the space. The cases will outline the additional requirements I have that make it more tricky.
I tried two main approaches:
LinearLayout
I started with a horizontal LinearLayout. The first and third TextViews had widths of WRAP_CONTENT while the center title view had a width of 0dp and weight of 1, allowing it to fill the middle section of the row. The issue here was that the first and third views wouldn't have the same width with WRAP_CONTENT, so the title view would be shifted one way or the other. See here how the title is shifted to the left towards the smaller text:
After that, I assigned a weightSum of 20 to the LinearLayout, gave weights of 4 to the first and third views, a weight of 12 to the middle view, and width of 0dp for all 3. This worked better but wouldn't allow for longer text in the end views, even when there was plenty of room in the row for the view to expand:
Changing the views to use layout_weight and widths of WRAP_CONTENT didn't work either.
ConstraintLayout
I switch the row to a ConstraintLayout to try that as well.
I started this route with the end views using WRAP_CONTENT as their width and the title view using 0dp. The title view had its start linked to the end of the first view, and its end linked to the start of the last view. Of course, this would shift the title view towards whichever end text was shorter. Changing the end views to a width of 0dp yielded the same result.
From here I got closer. I linked the middle view's start and end to the parent LinearLayout so that it stretched the entire width. Then I linked the first view's end to the middle view's start, and the last view's start to the middle view's end, and set both of their widths to 0dp. By setting the middle view's width to 0dp and adding a marginStart and marginEnd of 64dp, I allowed some space for the first and last views to be shown. Still, this was like the LinearLayout solution with weights, as the gaps on each side of the middle view was of the fixed 64dp size, and didn't allow for expansion.
My last effort was so close! Changing the middle view's width to WRAP_CONTENT, and start and end both linked to the parent, allowed the end views to be of equal widths, and fully expanded. That is, UNTIL the title view became very long. With a width of WRAP_CONTENT, the middle view would push the end views off the screen with a very long text, even with a min_width set on the end views.
Changing the end views to have widths of WRAP_CONTENT didn't work either.
What I want to accomplish is most like this last try, except that I want the middle view to stop expanding and become ellipsized instead of pushing the end views out. Any other ideas to try?
The problem is if you want the first and third views equal width I don't think it is possible with just standard xml layouts as it's too late once a view has been sized, it won't be resized again without extra code.
Something like TableLayout has the extra code to resize all the rows to match, one it's sized them individually.
But you can to this yourself programmatically
e.g
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainLayout = findViewById(R.id.content);
// Wait until mainLayout has it's size calculated
ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Don't listen anymore
mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
TextView col1 = new TextView(getApplicationContext());
TextView col2 = new TextView(getApplicationContext());
TextView col3 = new TextView(getApplicationContext());
col1.setText("Much Long");
col1.setBackgroundColor(Color.CYAN);
col3.setText("Long");
col3.setBackgroundColor(Color.CYAN);
col3.setGravity(Gravity.RIGHT);
col2.setText("Really Really Really Really Really Really Really Really Really Long");
col2.setGravity(Gravity.CENTER_HORIZONTAL);
col2.setSingleLine(true);
// Set it to have Ellipsizes if too long
TextUtils.TruncateAt truncate = TextUtils.TruncateAt.END;
col2.setEllipsize(truncate);
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); // As big as the view wants to be
// Measure all the views
col1.measure(measureSpec, measureSpec);
col2.measure(measureSpec, measureSpec);
col3.measure(measureSpec, measureSpec);
// work which side is bigger
int maxWidth = Math.max(col1.getMeasuredWidth(), col3.getMeasuredWidth());
int parentWidth = mainLayout.getWidth();
// Probably should have some checks to make sure centre width does not go negative
int centreWidth = parentWidth - (2 * maxWidth);
col1.setWidth(maxWidth);
col2.setWidth(centreWidth);
col3.setWidth(maxWidth);
mainLayout.addView(col1);
mainLayout.addView(col2);
mainLayout.addView(col3);
}
};
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
}
Produces some examples
The centre of the middle column is always at the centre of the screen

First child of LinearLayout inside ScrollView with 1/3 space of device height

I have a ScrollView with a LinearLayout with 3 elements inside. I would like that the first element has a height of 1/3 the height of the device height and the other 2 with wrap_content, is this possible to do in xml or how would you do this? Using weight alone it does not seem possible because the LinearLayout inside the ScrollView could be longer than the device's height.
According to Android docs, android:layout_weight:
Indicates how much of the extra space in the LinearLayout is allocated to the view associated with these LayoutParams. Specify 0 if the view should not be stretched. Otherwise the extra pixels will be pro-rated among all views whose weight is greater than 0.
This implies that the weight is not dependent on the screen height, but rather only on its parent view, so you can't achieve that from xml. To achieve this I would compute the height of the screen and then resize the view:
Display screen = getWindowManager().getDefaultDisplay();
Point size = new Point();
screen.getSize(size);
int screenHeight = size.y;
myView.setLayoutParams(new LayoutParams(myView.getWidth(), screenHeight / 3));
You can give the first linear layout weight=0.333 with height=0 and the other two layout wrap_content

Smooth scrolling of expandablelistview containing listviews

My app has a screen with an expandablelistview. Within the expandablelistview are collapsible rows that contain listviews. These listviews contain a dynamic number of items and with dynamic heights on each item. As listviews are meant to be set to a hard-coded height and then to have scrolling within them, and I don't want to have scrolling within scrolling, I've had to get around that by calculating the actual height of the listview's items then set the height of the listview to that height so that all of its contents will fit within the exandablelistview row.
Here is my extension method that calculates the listview's height:
public static void SetHeightBasedOnChildren(this ListView listView) {
var listAdapter = listView.Adapter;
var totalHeight = listView.PaddingTop + listView.PaddingBottom;
if (listView.DividerHeight > 0)
totalHeight += (listView.DividerHeight * (listAdapter.Count - 1));
var desiredWidth = DeviceHelper.GetDimensions ().X;// Add parameter if listview doesn't always span entire width of screen
var listViewWidth = MeasureSpec.MakeMeasureSpec (desiredWidth, MeasureSpecMode.AtMost);
View listItem;
for (int i = 0; i < listAdapter.Count; i++) {
listItem = listAdapter.GetView (i, null, listView);
if (listItem is ViewGroup && listItem.LayoutParameters == null)
listItem.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
listItem.Measure (listViewWidth, (int)MeasureSpecMode.Unspecified);
totalHeight += listItem.MeasuredHeight;
}
var layoutParams = listView.LayoutParameters;
layoutParams.Height = totalHeight;
listView.LayoutParameters = layoutParams;
}
The issue with this is that when scrolling down the screen and reaching each listview, as it calculates the dynamic height of the listview's items and sets its height to that, it causes the scrolling to get really choppy.
As I mentioned that listviews are meant to be a hard-coded height, I tried swapping out our listviews for linearlayouts with vertical orientation, which are meant to have an adjustable height that fit their content so don't need any fancy height calculations on the fly. Unfortunately this approach didn't work, as linearlayouts don't cache their rows like listviews do, so the screen was completely unusable from trying to manage hundreds -> thousands of rows without any sort of caching. Note: the linearlayouts performed fine on the emulator but not on devices.
I'm wondering if anyone has ideas of how to get smooth scrolling in this situation. Some ideas I had...
Keep using listviews but set a hard-coded height on each item in the listview. Then I can calculate the height of the listview much faster, as I know the height based on the total number of rows. I would still have to set the height on each listview though, which makes it still not smooth but better than before. The downside is that I'd either need to make all rows tall enough to fit longer content (waste of vertical space), or truncate content that doesn't fit.
Look into if anyone has developed a hybrid widget, that is a linearlayout with vertical orientation that utilizes row caching.
Figure out how to force the expandablelistview to load the listview sections immediately on screen load rather than waiting until they're just about to come into view. The downside is we're holding a lot more in memory so this might render the screen unusable.
Thoughts??
EDIT: I should probably mention that the listviews are also within viewpagers, as the user can swipe between lists from different years (think tax records or mortgage records for a property). I bring this up so someone won't say, just don't have listviews at all, just only use the expandablelistview.

Measure a layout before adding views in it?

I have a RelativeLayout as root element. There are three LinearLayouts under root element. First and last has fixed height and the middle one takes rest of screen.
There are two ScrollViews inside the middle LinearLayout. I programatically add new views in them. I want to show three item in a scrollview no matter the screen size is.
The problem is, I can't calculate the height of are so I can't divide it by three and get item height required.
I tried to call measure() and getMeasuredHeight() but LinearLayout returned 21 (which I have no idea why) and ScrollViews returned 0. Both LinearLayout and ScrollViews has match_parent attribute.
So how can I get the actual height? I know it is calculated somewhere because I can see it covers the all empty area.
The Good
Don't unless you have a really good reason. This kind of layout won't work for tablets and larger devices (it will look very odd having giant list items) and it is not common behaviour.
The Bad
You said the top and bottom views have a fixed size, and you can get the height of the screen with:
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
// Use getSize on API13+
int displayHeight = display.getHeight();
Then the height of each row is just
int listHeight = displayHeight - (2 * fixedHeight)
int rowHeight = listHeight/3;
This is bad because it will not work in more complex layouts.
The Ugly
In onCreate the size of your views will not yet have been initialised. You need wait until the view has been given a size. There a few ways you could do this:
Use a View.OnLayoutChangeListener (API 11+)
Use a ViewTreeObserver.OnGlobalLayoutListener (API 1+)
Create a custom View class (subclass LinearLayout) and override onSizeChanged

Get Center point in my app android

In my app Horizontalscrollview is present. It consist of LinearLayout in it, to that layout i have added number of buttons to it.ViewFlipper is also present in it. As i flip the layout i want to move the horizontalscrollview the respective button should get to center position.
My layout(s) and number of button(s) are same. for 1st layout 1st button should be at center location?
thnx for any help....
Hmm, okay, just to point out, when you get to the first or last position (possibly first + 1, last - 1 as well, depending on the button size), you won't be able to have it in the center, as you can't overscroll with just a HorizontalScrollView. With that in mind, for your general case (you can handle the edge cases however you like -- I'd suggest just leaving it scrolled as far as you can, and giving the selected button some sort of highlight) you should be able to do something like this:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
//get an instance of your LinearLayout, HSV, and ViewFlipper
LinearLayout ll = (LinearLayout)findViewById(R.id.my_linear_layout);
ViewFlipper vf = (ViewFlipper)findViewById(R.id.my_view_flipper);
HorizontalScrollView hsv = (HorizontalScrollView)findViewById(R.id.my_hsv);
//as you've said, there should always be an equal number of Buttons as
//Views in the ViewFlipper. This code assumes that is always true.
View v = ll.getChildAt(vf.getDisplayedChild());
//assuming smoothScrollTo scrolls to the left side of the screen,
//which I think it does, we want to scroll to the Button's center
//point, minus (shifted to the right) by half the screen width
int scrollTo = v.getLeft() + (v.getWidth() - screenWidth) / 2);
//handle overflow for the edge cases
if (scrollTo < 0) scrollTo = 0;
else if (scrollTo > hsv.getWidth()) scrollTo = hsv.getWidth();
hsv.smoothScrollTo(scrollTo);
Untested, and I'm prone to small syntax errors, but the general idea may help. :)
Get the size of Display, and divide by 2, then subtract your button width divided by 2 ?
But it really sounds like you should have the buttons in its own view, then merge the views on top of each other.
<merge>
<HorizontalScrollView .../>
<Button ... />
</merge>

Categories

Resources